@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.js CHANGED
@@ -1,13 +1,14 @@
1
- import { StyleSheet, Platform, Keyboard, View, Pressable, TextInput, Text, useWindowDimensions, Animated, Easing, PanResponder, Image, ScrollView, ActivityIndicator, FlatList, AppState, KeyboardAvoidingView, Modal, Dimensions, Linking } from 'react-native';
1
+ import { useUser, useMe, useConversations, useAttachmentUpload, useConversation, useMembers, usePresence, useMessages, useTyping, useRealtimeStatus, computeGroupPosition, formatDayLabel, useReactions, sameDay, useThread, useAttachmentUrl, usePoolse, useVoiceRoom, useCalls, useCallRoom } from '@poolse/react';
2
2
  import { createContext, forwardRef, useRef, useState, useEffect, useImperativeHandle, useMemo, useCallback, useContext, createElement } from 'react';
3
+ import { StyleSheet, Platform, Keyboard, View, Pressable, TextInput, Text, useWindowDimensions, Animated, Easing, PanResponder, Image, ScrollView, ActivityIndicator, FlatList, AppState, KeyboardAvoidingView, Modal, Dimensions, Linking, StatusBar } from 'react-native';
3
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
- import { useUser, useMe, useConversations, useAttachmentUpload, useConversation, useMembers, usePresence, useMessages, useTyping, useRealtimeStatus, computeGroupPosition, formatDayLabel, useReactions, sameDay, useThread, useAttachmentUrl, usePoolse, useVoiceRoom, useCalls, useCallRoom } from '@poolse/react';
5
5
  import { SvgXml, Svg, Path, Circle, G } from 'react-native-svg';
6
6
  import * as ImagePicker from 'expo-image-picker';
7
7
  import * as DocumentPicker from 'expo-document-picker';
8
8
  import { manipulateAsync, SaveFormat } from 'expo-image-manipulator';
9
9
  import * as Clipboard from 'expo-clipboard';
10
10
  import Markdown, { MarkdownIt } from 'react-native-markdown-display';
11
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
11
12
 
12
13
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
13
14
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
@@ -179,79 +180,6 @@ function PoolseTheme({ mode, theme, children }) {
179
180
  function usePoolseTheme() {
180
181
  return useContext(ThemeContext) ?? defaultLightTheme;
181
182
  }
182
- function CallVideoGrid({ participants, livekitReactNative, labelFor }) {
183
- const theme = usePoolseTheme();
184
- const label = labelFor ?? ((id) => `User ${id.slice(0, 6)}`);
185
- const VideoTrack = livekitReactNative?.VideoTrack;
186
- if (participants.length === 0) return null;
187
- const columns = participants.length <= 1 ? 1 : 2;
188
- const basis = columns === 1 ? "100%" : "48%";
189
- return /* @__PURE__ */ jsx(View, { style: styles.grid, children: participants.map((participant) => /* @__PURE__ */ jsxs(
190
- View,
191
- {
192
- style: [
193
- styles.tile,
194
- {
195
- flexBasis: basis,
196
- backgroundColor: theme.colors.surface,
197
- // A ring rather than a badge: it reads at a glance in a
198
- // grid, and doesn't cover a face.
199
- borderColor: participant.speaking ? theme.colors.brand : "transparent"
200
- }
201
- ],
202
- children: [
203
- participant.videoTrackRef && VideoTrack ? /* @__PURE__ */ jsx(
204
- VideoTrack,
205
- {
206
- trackRef: participant.videoTrackRef,
207
- style: styles.video,
208
- objectFit: "cover",
209
- mirror: participant.isSelf,
210
- zOrder: participant.isSelf ? 1 : 0
211
- }
212
- ) : /* @__PURE__ */ jsx(View, { style: styles.placeholder, children: /* @__PURE__ */ jsx(Text, { style: [styles.initial, { color: theme.colors.ink3 }], children: label(participant.userId).slice(0, 1).toUpperCase() }) }),
213
- /* @__PURE__ */ jsxs(View, { style: styles.caption, children: [
214
- /* @__PURE__ */ jsx(Text, { numberOfLines: 1, style: [styles.name, { color: theme.colors.onBrand }], children: participant.isSelf ? "You" : participant.name ?? label(participant.userId) }),
215
- !participant.micEnabled && // Muted is worth showing even for yourself — it is the
216
- // answer to "why can nobody hear me".
217
- /* @__PURE__ */ jsx(Text, { style: [styles.muted, { color: theme.colors.onBrand }], children: "muted" })
218
- ] })
219
- ]
220
- },
221
- participant.userId
222
- )) });
223
- }
224
- var styles = StyleSheet.create({
225
- grid: {
226
- flex: 1,
227
- flexDirection: "row",
228
- flexWrap: "wrap",
229
- justifyContent: "center",
230
- alignContent: "center",
231
- gap: 8
232
- },
233
- tile: {
234
- aspectRatio: 3 / 4,
235
- borderRadius: 16,
236
- borderWidth: 2,
237
- overflow: "hidden",
238
- justifyContent: "flex-end"
239
- },
240
- video: { ...StyleSheet.absoluteFillObject },
241
- placeholder: { ...StyleSheet.absoluteFillObject, alignItems: "center", justifyContent: "center" },
242
- initial: { fontSize: 48, fontWeight: "600" },
243
- caption: {
244
- flexDirection: "row",
245
- alignItems: "center",
246
- gap: 6,
247
- paddingHorizontal: 10,
248
- paddingVertical: 6,
249
- // Legible over both a bright video frame and a flat placeholder.
250
- backgroundColor: "rgba(0,0,0,0.45)"
251
- },
252
- name: { flexShrink: 1, fontSize: 13, fontWeight: "600" },
253
- muted: { fontSize: 11, opacity: 0.85 }
254
- });
255
183
 
256
184
  // src/primitives/userColor.ts
257
185
  var PALETTE = [
@@ -290,7 +218,7 @@ function Avatar({ src, name, seed, online, size = "md" }) {
290
218
  View,
291
219
  {
292
220
  style: [
293
- styles2.root,
221
+ styles.root,
294
222
  {
295
223
  width: dim,
296
224
  height: dim,
@@ -324,7 +252,7 @@ function Avatar({ src, name, seed, online, size = "md" }) {
324
252
  View,
325
253
  {
326
254
  style: [
327
- styles2.presence,
255
+ styles.presence,
328
256
  {
329
257
  backgroundColor: theme.colors.presence,
330
258
  borderColor: theme.colors.surface,
@@ -344,7 +272,7 @@ function computeInitials(name) {
344
272
  const parts = name.trim().split(/\s+/).slice(0, 2);
345
273
  return parts.map((p) => p.charAt(0).toUpperCase()).join("").slice(0, 2) || "?";
346
274
  }
347
- var styles2 = StyleSheet.create({
275
+ var styles = StyleSheet.create({
348
276
  root: {
349
277
  alignItems: "center",
350
278
  justifyContent: "center",
@@ -368,10 +296,18 @@ var ICONS = {
368
296
  vb: "0 0 24 24",
369
297
  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"/>'
370
298
  },
299
+ bluetooth: {
300
+ vb: "0 0 24 24",
301
+ d: '<path d="M7.5 7.5 16.5 16.5 12 21V3l4.5 4.5L7.5 16.5"/>'
302
+ },
371
303
  camera: {
372
304
  vb: "0 0 24 24",
373
305
  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"/>'
374
306
  },
307
+ "camera-flip": {
308
+ vb: "0 0 24 24",
309
+ 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"/>'
310
+ },
375
311
  "check-double": {
376
312
  vb: "0 0 24 24",
377
313
  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"/>'
@@ -388,6 +324,10 @@ var ICONS = {
388
324
  vb: "0 0 24 24",
389
325
  d: '<path d="M9 5 16 12 9 19"/>'
390
326
  },
327
+ "chevron-up": {
328
+ vb: "0 0 24 24",
329
+ d: '<path d="M6 14.5 12 8.5 18 14.5"/>'
330
+ },
391
331
  close: {
392
332
  vb: "0 0 24 24",
393
333
  d: '<path d="M6 6 18 18M18 6 6 18"/>'
@@ -416,6 +356,10 @@ var ICONS = {
416
356
  vb: "0 0 24 24",
417
357
  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"/>'
418
358
  },
359
+ headphones: {
360
+ vb: "0 0 24 24",
361
+ 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"/>'
362
+ },
419
363
  heart: {
420
364
  vb: "0 0 24 24",
421
365
  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"/>'
@@ -452,6 +396,10 @@ var ICONS = {
452
396
  vb: "0 0 24 24",
453
397
  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"/>'
454
398
  },
399
+ "mic-off": {
400
+ vb: "0 0 24 24",
401
+ 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"/>'
402
+ },
455
403
  "more-h": {
456
404
  vb: "0 0 24 24",
457
405
  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"/>'
@@ -460,6 +408,14 @@ var ICONS = {
460
408
  vb: "0 0 24 24",
461
409
  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"/>'
462
410
  },
411
+ phone: {
412
+ vb: "0 0 24 24",
413
+ 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"/>'
414
+ },
415
+ "phone-off": {
416
+ vb: "0 0 24 24",
417
+ 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>'
418
+ },
463
419
  pin: {
464
420
  vb: "0 0 24 24",
465
421
  d: '<path d="M9 3.4h6v1.8l-1 5.6 3 3.2H7l3-3.2-1-5.6V3.4Z"/><path d="M12 17v3.6"/>'
@@ -500,6 +456,14 @@ var ICONS = {
500
456
  vb: "0 0 24 24",
501
457
  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"/>'
502
458
  },
459
+ speaker: {
460
+ vb: "0 0 24 24",
461
+ 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"/>'
462
+ },
463
+ "speaker-off": {
464
+ vb: "0 0 24 24",
465
+ 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"/>'
466
+ },
503
467
  "thumbs-up": {
504
468
  vb: "0 0 24 24",
505
469
  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"/>'
@@ -524,6 +488,14 @@ var ICONS = {
524
488
  vb: "0 0 24 24",
525
489
  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"/>'
526
490
  },
491
+ video: {
492
+ vb: "0 0 24 24",
493
+ 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"/>'
494
+ },
495
+ "video-off": {
496
+ vb: "0 0 24 24",
497
+ 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"/>'
498
+ },
527
499
  webhook: {
528
500
  vb: "0 0 24 24",
529
501
  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"/>'
@@ -554,16 +526,16 @@ function MemberList({
554
526
  );
555
527
  const effectiveOnline = onlineExternalIds ?? presenceOnline;
556
528
  if (loading && members.length === 0) {
557
- return /* @__PURE__ */ jsx(View, { style: [styles3.placeholder, { backgroundColor: theme.colors.paper }], children: /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.brand }) });
529
+ return /* @__PURE__ */ jsx(View, { style: [styles2.placeholder, { backgroundColor: theme.colors.paper }], children: /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.brand }) });
558
530
  }
559
531
  if (error) {
560
- return /* @__PURE__ */ jsxs(View, { style: [styles3.placeholder, { backgroundColor: theme.colors.paper }], children: [
532
+ return /* @__PURE__ */ jsxs(View, { style: [styles2.placeholder, { backgroundColor: theme.colors.paper }], children: [
561
533
  /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.error, fontFamily: theme.type.fontBody }, children: "Failed to load members." }),
562
- error.message ? /* @__PURE__ */ jsx(Text, { style: [styles3.errorDetail, { color: theme.colors.ink3 }], children: error.message }) : null
534
+ error.message ? /* @__PURE__ */ jsx(Text, { style: [styles2.errorDetail, { color: theme.colors.ink3 }], children: error.message }) : null
563
535
  ] });
564
536
  }
565
537
  if (members.length === 0) {
566
- return /* @__PURE__ */ jsx(View, { style: [styles3.placeholder, { backgroundColor: theme.colors.paper }], children: typeof emptyState === "string" || emptyState == null ? /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.ink3, fontFamily: theme.type.fontBody }, children: emptyState ?? "No members." }) : emptyState });
538
+ return /* @__PURE__ */ jsx(View, { style: [styles2.placeholder, { backgroundColor: theme.colors.paper }], children: typeof emptyState === "string" || emptyState == null ? /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.ink3, fontFamily: theme.type.fontBody }, children: emptyState ?? "No members." }) : emptyState });
567
539
  }
568
540
  return /* @__PURE__ */ jsx(
569
541
  FlatList,
@@ -602,23 +574,23 @@ function MemberRow({
602
574
  const resolved = useUser(externalId);
603
575
  const name = labelFor?.(externalId) ?? resolved.profile?.displayName ?? externalId;
604
576
  const avatarUrl = avatarFor?.(externalId) ?? resolved.profile?.avatarUrl ?? null;
605
- return /* @__PURE__ */ jsxs(View, { style: styles3.row, children: [
577
+ return /* @__PURE__ */ jsxs(View, { style: styles2.row, children: [
606
578
  /* @__PURE__ */ jsx(Avatar, { src: avatarUrl, name, seed: externalId, online, size: "md" }),
607
- /* @__PURE__ */ jsxs(View, { style: styles3.body, children: [
579
+ /* @__PURE__ */ jsxs(View, { style: styles2.body, children: [
608
580
  /* @__PURE__ */ jsx(
609
581
  Text,
610
582
  {
611
- style: [styles3.name, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
583
+ style: [styles2.name, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
612
584
  numberOfLines: 1,
613
585
  children: name
614
586
  }
615
587
  ),
616
- /* @__PURE__ */ jsx(Text, { style: [styles3.role, { color: theme.colors.ink3 }], children: role })
588
+ /* @__PURE__ */ jsx(Text, { style: [styles2.role, { color: theme.colors.ink3 }], children: role })
617
589
  ] }),
618
- onRemove ? /* @__PURE__ */ jsx(Pressable, { onPress: onRemove, hitSlop: 8, style: styles3.removeBtn, children: /* @__PURE__ */ jsx(PoolseIcon, { name: "close", size: 16, color: theme.colors.ink2 }) }) : null
590
+ onRemove ? /* @__PURE__ */ jsx(Pressable, { onPress: onRemove, hitSlop: 8, style: styles2.removeBtn, children: /* @__PURE__ */ jsx(PoolseIcon, { name: "close", size: 16, color: theme.colors.ink2 }) }) : null
619
591
  ] });
620
592
  }
621
- var styles3 = StyleSheet.create({
593
+ var styles2 = StyleSheet.create({
622
594
  placeholder: {
623
595
  flex: 1,
624
596
  alignItems: "center",
@@ -721,11 +693,11 @@ function GroupDetailsSheet({
721
693
  animationType: "slide",
722
694
  onRequestClose: onClose,
723
695
  statusBarTranslucent: true,
724
- children: /* @__PURE__ */ jsxs(View, { style: styles4.modalRoot, children: [
696
+ children: /* @__PURE__ */ jsxs(View, { style: styles3.modalRoot, children: [
725
697
  /* @__PURE__ */ jsx(
726
698
  Pressable,
727
699
  {
728
- style: styles4.backdrop,
700
+ style: styles3.backdrop,
729
701
  onPress: onClose,
730
702
  accessibilityLabel: "Close group details"
731
703
  }
@@ -734,7 +706,7 @@ function GroupDetailsSheet({
734
706
  Animated.View,
735
707
  {
736
708
  style: [
737
- styles4.sheet,
709
+ styles3.sheet,
738
710
  {
739
711
  backgroundColor: theme.colors.paper,
740
712
  borderTopLeftRadius: theme.radii.xl,
@@ -744,14 +716,14 @@ function GroupDetailsSheet({
744
716
  theme.shadows.lg
745
717
  ],
746
718
  children: [
747
- /* @__PURE__ */ jsx(View, { ...panHandlers, style: styles4.dragHandle, children: /* @__PURE__ */ jsx(View, { style: [styles4.dragPill, { backgroundColor: theme.colors.ink3 }] }) }),
748
- /* @__PURE__ */ jsxs(View, { style: styles4.headerRow, children: [
719
+ /* @__PURE__ */ jsx(View, { ...panHandlers, style: styles3.dragHandle, children: /* @__PURE__ */ jsx(View, { style: [styles3.dragPill, { backgroundColor: theme.colors.ink3 }] }) }),
720
+ /* @__PURE__ */ jsxs(View, { style: styles3.headerRow, children: [
749
721
  /* @__PURE__ */ jsx(
750
722
  Pressable,
751
723
  {
752
724
  onPress: onClose,
753
725
  hitSlop: 16,
754
- style: [styles4.closeBtn, { backgroundColor: theme.colors.surface2 }],
726
+ style: [styles3.closeBtn, { backgroundColor: theme.colors.surface2 }],
755
727
  accessibilityLabel: "Close",
756
728
  children: /* @__PURE__ */ jsx(PoolseIcon, { name: "close", size: 18, color: theme.colors.ink })
757
729
  }
@@ -760,34 +732,34 @@ function GroupDetailsSheet({
760
732
  Text,
761
733
  {
762
734
  style: [
763
- styles4.headerTitle,
735
+ styles3.headerTitle,
764
736
  { color: theme.colors.ink, fontFamily: theme.type.fontDisplay }
765
737
  ],
766
738
  children: "Group details"
767
739
  }
768
740
  )
769
741
  ] }),
770
- /* @__PURE__ */ jsxs(View, { style: styles4.summary, children: [
742
+ /* @__PURE__ */ jsxs(View, { style: styles3.summary, children: [
771
743
  /* @__PURE__ */ jsx(Avatar, { src: null, name: title, seed: conversationId, size: "lg" }),
772
744
  /* @__PURE__ */ jsx(
773
745
  Text,
774
746
  {
775
747
  style: [
776
- styles4.title,
748
+ styles3.title,
777
749
  { color: theme.colors.ink, fontFamily: theme.type.fontDisplay }
778
750
  ],
779
751
  numberOfLines: 1,
780
752
  children: title
781
753
  }
782
754
  ),
783
- /* @__PURE__ */ jsx(Text, { style: [styles4.subtitle, { color: theme.colors.ink3 }], children: subtitle })
755
+ /* @__PURE__ */ jsx(Text, { style: [styles3.subtitle, { color: theme.colors.ink3 }], children: subtitle })
784
756
  ] }),
785
- /* @__PURE__ */ jsx(View, { style: [styles4.divider, { backgroundColor: theme.colors.border }] }),
786
- /* @__PURE__ */ jsx(View, { style: styles4.sectionLabelRow, children: /* @__PURE__ */ jsx(
757
+ /* @__PURE__ */ jsx(View, { style: [styles3.divider, { backgroundColor: theme.colors.border }] }),
758
+ /* @__PURE__ */ jsx(View, { style: styles3.sectionLabelRow, children: /* @__PURE__ */ jsx(
787
759
  Text,
788
760
  {
789
761
  style: [
790
- styles4.sectionLabel,
762
+ styles3.sectionLabel,
791
763
  { color: theme.colors.ink2, fontFamily: theme.type.fontBody }
792
764
  ],
793
765
  children: "Members"
@@ -810,7 +782,7 @@ function GroupDetailsSheet({
810
782
  }
811
783
  );
812
784
  }
813
- var styles4 = StyleSheet.create({
785
+ var styles3 = StyleSheet.create({
814
786
  modalRoot: {
815
787
  flex: 1,
816
788
  justifyContent: "flex-end"
@@ -904,7 +876,7 @@ function ChatHeader({
904
876
  View,
905
877
  {
906
878
  style: [
907
- styles5.root,
879
+ styles4.root,
908
880
  {
909
881
  backgroundColor: theme.colors.surface,
910
882
  borderBottomColor: theme.colors.border
@@ -929,32 +901,32 @@ function ChatHeader({
929
901
  {
930
902
  ...headerOnPress ? { onPress: headerOnPress } : {},
931
903
  style: [
932
- styles5.root,
904
+ styles4.root,
933
905
  {
934
906
  backgroundColor: theme.colors.surface,
935
907
  borderBottomColor: theme.colors.border
936
908
  }
937
909
  ],
938
910
  children: [
939
- onBack ? /* @__PURE__ */ jsx(Pressable, { onPress: onBack, hitSlop: 12, style: styles5.backBtn, accessibilityLabel: "Back", children: /* @__PURE__ */ jsx(PoolseIcon, { name: "reply", size: 20, color: theme.colors.ink }) }) : null,
911
+ onBack ? /* @__PURE__ */ jsx(Pressable, { onPress: onBack, hitSlop: 12, style: styles4.backBtn, accessibilityLabel: "Back", children: /* @__PURE__ */ jsx(PoolseIcon, { name: "reply", size: 20, color: theme.colors.ink }) }) : null,
940
912
  /* @__PURE__ */ jsx(Avatar, { src: avatarUrl, name: title, seed: avatarSeed, online: isOnline, size: "md" }),
941
- /* @__PURE__ */ jsxs(View, { style: styles5.titleCol, children: [
913
+ /* @__PURE__ */ jsxs(View, { style: styles4.titleCol, children: [
942
914
  /* @__PURE__ */ jsx(
943
915
  Text,
944
916
  {
945
- style: [styles5.title, { color: theme.colors.ink, fontFamily: theme.type.fontDisplay }],
917
+ style: [styles4.title, { color: theme.colors.ink, fontFamily: theme.type.fontDisplay }],
946
918
  numberOfLines: 1,
947
919
  children: title
948
920
  }
949
921
  ),
950
- subtitle ? /* @__PURE__ */ jsx(Text, { style: [styles5.subtitle, { color: theme.colors.ink3 }], numberOfLines: 1, children: subtitle }) : null
922
+ subtitle ? /* @__PURE__ */ jsx(Text, { style: [styles4.subtitle, { color: theme.colors.ink3 }], numberOfLines: 1, children: subtitle }) : null
951
923
  ] }),
952
924
  onMembersPress ? /* @__PURE__ */ jsx(
953
925
  Pressable,
954
926
  {
955
927
  onPress: onMembersPress,
956
928
  hitSlop: 12,
957
- style: styles5.actionBtn,
929
+ style: styles4.actionBtn,
958
930
  accessibilityLabel: "Show members",
959
931
  children: /* @__PURE__ */ jsx(PoolseIcon, { name: "users", size: 20, color: theme.colors.ink2 })
960
932
  }
@@ -974,7 +946,7 @@ function ChatHeader({
974
946
  ) : null
975
947
  ] });
976
948
  }
977
- var styles5 = StyleSheet.create({
949
+ var styles4 = StyleSheet.create({
978
950
  root: {
979
951
  flexDirection: "row",
980
952
  alignItems: "center",
@@ -1032,13 +1004,13 @@ function ConversationList({
1032
1004
  const error = isControlled ? controlledError ?? null : auto.error;
1033
1005
  const counts = unreadCounts ?? (isControlled ? void 0 : auto.unreadCounts);
1034
1006
  if (loading && conversations.length === 0) {
1035
- return /* @__PURE__ */ jsx(View, { style: [styles6.placeholder, { backgroundColor: theme.colors.paper }], children: /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.brand }) });
1007
+ return /* @__PURE__ */ jsx(View, { style: [styles5.placeholder, { backgroundColor: theme.colors.paper }], children: /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.brand }) });
1036
1008
  }
1037
1009
  if (error) {
1038
- return /* @__PURE__ */ jsx(View, { style: [styles6.placeholder, { backgroundColor: theme.colors.paper }], children: /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.error, fontFamily: theme.type.fontBody }, children: "Failed to load conversations." }) });
1010
+ return /* @__PURE__ */ jsx(View, { style: [styles5.placeholder, { backgroundColor: theme.colors.paper }], children: /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.error, fontFamily: theme.type.fontBody }, children: "Failed to load conversations." }) });
1039
1011
  }
1040
1012
  if (conversations.length === 0) {
1041
- return /* @__PURE__ */ jsx(View, { style: [styles6.placeholder, { backgroundColor: theme.colors.paper }], children: typeof emptyState === "string" || emptyState == null ? /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.ink3, fontFamily: theme.type.fontBody }, children: emptyState ?? "No conversations yet." }) : emptyState });
1013
+ return /* @__PURE__ */ jsx(View, { style: [styles5.placeholder, { backgroundColor: theme.colors.paper }], children: typeof emptyState === "string" || emptyState == null ? /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.ink3, fontFamily: theme.type.fontBody }, children: emptyState ?? "No conversations yet." }) : emptyState });
1042
1014
  }
1043
1015
  return /* @__PURE__ */ jsx(
1044
1016
  FlatList,
@@ -1088,25 +1060,25 @@ function ConversationRow({
1088
1060
  {
1089
1061
  onPress: () => onSelect?.(conv),
1090
1062
  style: [
1091
- styles6.row,
1063
+ styles5.row,
1092
1064
  {
1093
1065
  backgroundColor: selected ? theme.colors.surface2 : "transparent"
1094
1066
  }
1095
1067
  ],
1096
1068
  children: [
1097
- avatarUrl ? /* @__PURE__ */ jsx(Image, { source: { uri: avatarUrl }, style: styles6.rowAvatar }) : /* @__PURE__ */ jsx(Avatar, { src: null, name: title, seed: avatarSeed, size: "md" }),
1098
- /* @__PURE__ */ jsxs(View, { style: styles6.body, children: [
1069
+ avatarUrl ? /* @__PURE__ */ jsx(Image, { source: { uri: avatarUrl }, style: styles5.rowAvatar }) : /* @__PURE__ */ jsx(Avatar, { src: null, name: title, seed: avatarSeed, size: "md" }),
1070
+ /* @__PURE__ */ jsxs(View, { style: styles5.body, children: [
1099
1071
  /* @__PURE__ */ jsx(
1100
1072
  Text,
1101
1073
  {
1102
- style: [styles6.name, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
1074
+ style: [styles5.name, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
1103
1075
  numberOfLines: 1,
1104
1076
  children: title
1105
1077
  }
1106
1078
  ),
1107
- conv.last_message_preview ? /* @__PURE__ */ jsx(Text, { style: [styles6.preview, { color: theme.colors.ink3 }], numberOfLines: 1, children: conv.last_message_preview }) : conv.last_message_at ? /* @__PURE__ */ jsx(Text, { style: [styles6.preview, { color: theme.colors.ink3 }], numberOfLines: 1, children: formatRelative(conv.last_message_at) }) : null
1079
+ conv.last_message_preview ? /* @__PURE__ */ jsx(Text, { style: [styles5.preview, { color: theme.colors.ink3 }], numberOfLines: 1, children: conv.last_message_preview }) : conv.last_message_at ? /* @__PURE__ */ jsx(Text, { style: [styles5.preview, { color: theme.colors.ink3 }], numberOfLines: 1, children: formatRelative(conv.last_message_at) }) : null
1108
1080
  ] }),
1109
- unread > 0 ? /* @__PURE__ */ jsx(View, { style: [styles6.unreadPill, { backgroundColor: theme.colors.unreadPill }], children: /* @__PURE__ */ jsx(Text, { style: [styles6.unreadText, { color: theme.colors.unreadPillText }], children: unread > 99 ? "99+" : String(unread) }) }) : null
1081
+ unread > 0 ? /* @__PURE__ */ jsx(View, { style: [styles5.unreadPill, { backgroundColor: theme.colors.unreadPill }], children: /* @__PURE__ */ jsx(Text, { style: [styles5.unreadText, { color: theme.colors.unreadPillText }], children: unread > 99 ? "99+" : String(unread) }) }) : null
1110
1082
  ]
1111
1083
  }
1112
1084
  );
@@ -1124,7 +1096,7 @@ function formatRelative(iso) {
1124
1096
  return "";
1125
1097
  }
1126
1098
  }
1127
- var styles6 = StyleSheet.create({
1099
+ var styles5 = StyleSheet.create({
1128
1100
  placeholder: {
1129
1101
  flex: 1,
1130
1102
  alignItems: "center",
@@ -1217,11 +1189,11 @@ function AttachmentPicker({
1217
1189
  onClose();
1218
1190
  }
1219
1191
  };
1220
- return /* @__PURE__ */ jsx(Modal, { visible, transparent: true, animationType: "slide", onRequestClose: onClose, children: /* @__PURE__ */ jsx(Pressable, { style: styles7.backdrop, onPress: onClose, children: /* @__PURE__ */ jsxs(
1192
+ return /* @__PURE__ */ jsx(Modal, { visible, transparent: true, animationType: "slide", onRequestClose: onClose, children: /* @__PURE__ */ jsx(Pressable, { style: styles6.backdrop, onPress: onClose, children: /* @__PURE__ */ jsxs(
1221
1193
  View,
1222
1194
  {
1223
1195
  style: [
1224
- styles7.sheet,
1196
+ styles6.sheet,
1225
1197
  {
1226
1198
  backgroundColor: theme.colors.surface,
1227
1199
  borderTopLeftRadius: theme.radii.lg,
@@ -1230,19 +1202,19 @@ function AttachmentPicker({
1230
1202
  theme.shadows.lg
1231
1203
  ],
1232
1204
  children: [
1233
- camera ? /* @__PURE__ */ jsxs(Pressable, { style: styles7.item, onPress: () => pickFromKind("camera"), children: [
1205
+ camera ? /* @__PURE__ */ jsxs(Pressable, { style: styles6.item, onPress: () => pickFromKind("camera"), children: [
1234
1206
  /* @__PURE__ */ jsx(PoolseIcon, { name: "camera", size: 20, color: theme.colors.ink2 }),
1235
- /* @__PURE__ */ jsx(Text, { style: [styles7.itemText, { color: theme.colors.ink }], children: "Camera" })
1207
+ /* @__PURE__ */ jsx(Text, { style: [styles6.itemText, { color: theme.colors.ink }], children: "Camera" })
1236
1208
  ] }) : null,
1237
- /* @__PURE__ */ jsxs(Pressable, { style: styles7.item, onPress: () => pickFromKind("image"), children: [
1209
+ /* @__PURE__ */ jsxs(Pressable, { style: styles6.item, onPress: () => pickFromKind("image"), children: [
1238
1210
  /* @__PURE__ */ jsx(PoolseIcon, { name: "image", size: 20, color: theme.colors.ink2 }),
1239
- /* @__PURE__ */ jsx(Text, { style: [styles7.itemText, { color: theme.colors.ink }], children: "Photo or video" })
1211
+ /* @__PURE__ */ jsx(Text, { style: [styles6.itemText, { color: theme.colors.ink }], children: "Photo or video" })
1240
1212
  ] }),
1241
- /* @__PURE__ */ jsxs(Pressable, { style: styles7.item, onPress: () => pickFromKind("document"), children: [
1213
+ /* @__PURE__ */ jsxs(Pressable, { style: styles6.item, onPress: () => pickFromKind("document"), children: [
1242
1214
  /* @__PURE__ */ jsx(PoolseIcon, { name: "attachment", size: 20, color: theme.colors.ink2 }),
1243
- /* @__PURE__ */ jsx(Text, { style: [styles7.itemText, { color: theme.colors.ink }], children: "File" })
1215
+ /* @__PURE__ */ jsx(Text, { style: [styles6.itemText, { color: theme.colors.ink }], children: "File" })
1244
1216
  ] }),
1245
- /* @__PURE__ */ jsx(Pressable, { style: styles7.cancel, onPress: onClose, children: /* @__PURE__ */ jsx(Text, { style: [styles7.itemText, { color: theme.colors.brand }], children: "Cancel" }) })
1217
+ /* @__PURE__ */ jsx(Pressable, { style: styles6.cancel, onPress: onClose, children: /* @__PURE__ */ jsx(Text, { style: [styles6.itemText, { color: theme.colors.brand }], children: "Cancel" }) })
1246
1218
  ]
1247
1219
  }
1248
1220
  ) }) });
@@ -1388,7 +1360,7 @@ async function defaultAdapter(kind) {
1388
1360
  })
1389
1361
  );
1390
1362
  }
1391
- var styles7 = StyleSheet.create({
1363
+ var styles6 = StyleSheet.create({
1392
1364
  backdrop: {
1393
1365
  flex: 1,
1394
1366
  backgroundColor: "rgba(0,0,0,0.35)",
@@ -1418,11 +1390,11 @@ var styles7 = StyleSheet.create({
1418
1390
  });
1419
1391
  function DaySeparator({ label }) {
1420
1392
  const theme = usePoolseTheme();
1421
- return /* @__PURE__ */ jsx(View, { style: styles8.wrap, children: /* @__PURE__ */ jsx(
1393
+ return /* @__PURE__ */ jsx(View, { style: styles7.wrap, children: /* @__PURE__ */ jsx(
1422
1394
  View,
1423
1395
  {
1424
1396
  style: [
1425
- styles8.chip,
1397
+ styles7.chip,
1426
1398
  {
1427
1399
  backgroundColor: theme.colors.surface2,
1428
1400
  borderColor: theme.colors.border
@@ -1432,7 +1404,7 @@ function DaySeparator({ label }) {
1432
1404
  Text,
1433
1405
  {
1434
1406
  style: [
1435
- styles8.label,
1407
+ styles7.label,
1436
1408
  {
1437
1409
  color: theme.colors.ink2,
1438
1410
  fontFamily: theme.type.fontBody
@@ -1444,7 +1416,7 @@ function DaySeparator({ label }) {
1444
1416
  }
1445
1417
  ) });
1446
1418
  }
1447
- var styles8 = StyleSheet.create({
1419
+ var styles7 = StyleSheet.create({
1448
1420
  wrap: {
1449
1421
  alignItems: "center",
1450
1422
  marginVertical: 8
@@ -1471,8 +1443,8 @@ function UploadQueueStrip() {
1471
1443
  {
1472
1444
  horizontal: true,
1473
1445
  showsHorizontalScrollIndicator: false,
1474
- contentContainerStyle: styles9.scroll,
1475
- style: [styles9.scrollStyle, { marginBottom: 8 }],
1446
+ contentContainerStyle: styles8.scroll,
1447
+ style: [styles8.scrollStyle, { marginBottom: 8 }],
1476
1448
  children: upload.queue.map((item) => {
1477
1449
  const isError = item.status === "error";
1478
1450
  const isUploading = item.status === "uploading" || item.status === "pending";
@@ -1483,7 +1455,7 @@ function UploadQueueStrip() {
1483
1455
  if (item.status === "ready" || isError) upload.remove(item.localId);
1484
1456
  else upload.cancel(item.localId);
1485
1457
  };
1486
- return /* @__PURE__ */ jsxs(View, { style: styles9.tileWrap, children: [
1458
+ return /* @__PURE__ */ jsxs(View, { style: styles8.tileWrap, children: [
1487
1459
  isImage && previewUri ? /* @__PURE__ */ jsx(ImageTile, { uri: previewUri, uploading: isUploading, error: isError }) : /* @__PURE__ */ jsx(
1488
1460
  FileTile,
1489
1461
  {
@@ -1498,13 +1470,13 @@ function UploadQueueStrip() {
1498
1470
  {
1499
1471
  onPress: handleClose,
1500
1472
  hitSlop: 8,
1501
- style: styles9.closeBtn,
1473
+ style: styles8.closeBtn,
1502
1474
  accessibilityLabel: isError ? "Remove failed upload" : "Cancel upload",
1503
1475
  children: /* @__PURE__ */ jsx(PoolseIcon, { name: "close", size: 11, color: "#fff" })
1504
1476
  }
1505
1477
  ) : null,
1506
- isUploading ? /* @__PURE__ */ jsx(View, { style: styles9.progressOverlay, children: /* @__PURE__ */ jsx(View, { style: [styles9.spinner, { borderColor: theme.colors.onBrand }] }) }) : null,
1507
- isError ? /* @__PURE__ */ jsx(View, { style: [styles9.errorOverlay, { backgroundColor: "rgba(229,72,77,0.85)" }], children: /* @__PURE__ */ jsx(Text, { style: [styles9.errorText, { color: "#fff" }], children: "Failed" }) }) : null
1478
+ isUploading ? /* @__PURE__ */ jsx(View, { style: styles8.progressOverlay, children: /* @__PURE__ */ jsx(View, { style: [styles8.spinner, { borderColor: theme.colors.onBrand }] }) }) : null,
1479
+ isError ? /* @__PURE__ */ jsx(View, { style: [styles8.errorOverlay, { backgroundColor: "rgba(229,72,77,0.85)" }], children: /* @__PURE__ */ jsx(Text, { style: [styles8.errorText, { color: "#fff" }], children: "Failed" }) }) : null
1508
1480
  ] }, item.localId);
1509
1481
  })
1510
1482
  }
@@ -1517,7 +1489,7 @@ function ImageTile({ uri, uploading, error }) {
1517
1489
  {
1518
1490
  source: { uri },
1519
1491
  style: [
1520
- styles9.imageTile,
1492
+ styles8.imageTile,
1521
1493
  {
1522
1494
  backgroundColor: theme.colors.surface2,
1523
1495
  borderColor: error ? theme.colors.error : "transparent",
@@ -1539,24 +1511,24 @@ function FileTile({
1539
1511
  View,
1540
1512
  {
1541
1513
  style: [
1542
- styles9.fileTile,
1514
+ styles8.fileTile,
1543
1515
  {
1544
1516
  backgroundColor: theme.colors.surface2,
1545
1517
  borderColor: error ? theme.colors.error : uploading ? theme.colors.brand : theme.colors.border
1546
1518
  }
1547
1519
  ],
1548
1520
  children: [
1549
- /* @__PURE__ */ jsx(View, { style: [styles9.fileIconBox, { backgroundColor: theme.colors.brandSoft }], children: /* @__PURE__ */ jsx(PoolseIcon, { name: "attachment", size: 18, color: theme.colors.brand }) }),
1550
- /* @__PURE__ */ jsxs(View, { style: styles9.fileInfo, children: [
1521
+ /* @__PURE__ */ jsx(View, { style: [styles8.fileIconBox, { backgroundColor: theme.colors.brandSoft }], children: /* @__PURE__ */ jsx(PoolseIcon, { name: "attachment", size: 18, color: theme.colors.brand }) }),
1522
+ /* @__PURE__ */ jsxs(View, { style: styles8.fileInfo, children: [
1551
1523
  /* @__PURE__ */ jsx(
1552
1524
  Text,
1553
1525
  {
1554
- style: [styles9.fileName, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
1526
+ style: [styles8.fileName, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
1555
1527
  numberOfLines: 1,
1556
1528
  children: filename
1557
1529
  }
1558
1530
  ),
1559
- /* @__PURE__ */ jsx(Text, { style: [styles9.fileSize, { color: theme.colors.ink3 }], children: formatBytes(size) })
1531
+ /* @__PURE__ */ jsx(Text, { style: [styles8.fileSize, { color: theme.colors.ink3 }], children: formatBytes(size) })
1560
1532
  ] })
1561
1533
  ]
1562
1534
  }
@@ -1570,7 +1542,7 @@ function formatBytes(n) {
1570
1542
  return `${(n / 1024 / 1024 / 1024).toFixed(1)} GB`;
1571
1543
  }
1572
1544
  var TILE = 64;
1573
- var styles9 = StyleSheet.create({
1545
+ var styles8 = StyleSheet.create({
1574
1546
  scrollStyle: {
1575
1547
  overflow: "visible"
1576
1548
  },
@@ -1786,7 +1758,7 @@ var MessageComposer = forwardRef(
1786
1758
  View,
1787
1759
  {
1788
1760
  style: [
1789
- styles10.wrap,
1761
+ styles9.wrap,
1790
1762
  {
1791
1763
  backgroundColor: theme.colors.surface,
1792
1764
  borderTopColor: theme.colors.border,
@@ -1809,13 +1781,13 @@ var MessageComposer = forwardRef(
1809
1781
  }
1810
1782
  ) : null,
1811
1783
  attachments && !isEditing ? /* @__PURE__ */ jsx(UploadQueueStrip, {}) : null,
1812
- /* @__PURE__ */ jsxs(View, { style: styles10.row, children: [
1784
+ /* @__PURE__ */ jsxs(View, { style: styles9.row, children: [
1813
1785
  attachments && onAttachPress ? /* @__PURE__ */ jsx(
1814
1786
  Pressable,
1815
1787
  {
1816
1788
  onPress: onAttachPress,
1817
1789
  hitSlop: 8,
1818
- style: styles10.iconBtn,
1790
+ style: styles9.iconBtn,
1819
1791
  accessibilityLabel: "Attach file",
1820
1792
  children: /* @__PURE__ */ jsx(PoolseIcon, { name: "attachment", size: 22, color: theme.colors.ink2 })
1821
1793
  }
@@ -1834,7 +1806,7 @@ var MessageComposer = forwardRef(
1834
1806
  multiline: true,
1835
1807
  editable: !disabled,
1836
1808
  style: [
1837
- styles10.input,
1809
+ styles9.input,
1838
1810
  {
1839
1811
  color: theme.colors.ink,
1840
1812
  backgroundColor: theme.colors.surface2,
@@ -1851,7 +1823,7 @@ var MessageComposer = forwardRef(
1851
1823
  onPress: handleSend,
1852
1824
  disabled: submitDisabled,
1853
1825
  style: [
1854
- styles10.sendBtn,
1826
+ styles9.sendBtn,
1855
1827
  {
1856
1828
  backgroundColor: submitDisabled ? theme.colors.ink3 : theme.colors.brand,
1857
1829
  opacity: submitDisabled ? 0.6 : 1
@@ -1873,7 +1845,7 @@ function EditChip({ message, onCancel }) {
1873
1845
  View,
1874
1846
  {
1875
1847
  style: [
1876
- styles10.replyChip,
1848
+ styles9.replyChip,
1877
1849
  {
1878
1850
  backgroundColor: theme.colors.surface2,
1879
1851
  borderLeftColor: theme.colors.brand
@@ -1881,10 +1853,10 @@ function EditChip({ message, onCancel }) {
1881
1853
  ],
1882
1854
  children: [
1883
1855
  /* @__PURE__ */ jsxs(View, { style: { flex: 1 }, children: [
1884
- /* @__PURE__ */ jsx(Text, { style: [styles10.replyName, { color: theme.colors.brand }], children: "Editing message" }),
1885
- /* @__PURE__ */ jsx(Text, { style: [styles10.replyBody, { color: theme.colors.ink2 }], numberOfLines: 1, children: message.body ?? "\u2026" })
1856
+ /* @__PURE__ */ jsx(Text, { style: [styles9.replyName, { color: theme.colors.brand }], children: "Editing message" }),
1857
+ /* @__PURE__ */ jsx(Text, { style: [styles9.replyBody, { color: theme.colors.ink2 }], numberOfLines: 1, children: message.body ?? "\u2026" })
1886
1858
  ] }),
1887
- onCancel ? /* @__PURE__ */ jsx(Pressable, { onPress: onCancel, hitSlop: 8, style: styles10.replyCancel, children: /* @__PURE__ */ jsx(PoolseIcon, { name: "close", size: 16, color: theme.colors.ink2 }) }) : null
1859
+ onCancel ? /* @__PURE__ */ jsx(Pressable, { onPress: onCancel, hitSlop: 8, style: styles9.replyCancel, children: /* @__PURE__ */ jsx(PoolseIcon, { name: "close", size: 16, color: theme.colors.ink2 }) }) : null
1888
1860
  ]
1889
1861
  }
1890
1862
  );
@@ -1901,7 +1873,7 @@ function ReplyChip({
1901
1873
  View,
1902
1874
  {
1903
1875
  style: [
1904
- styles10.replyChip,
1876
+ styles9.replyChip,
1905
1877
  {
1906
1878
  backgroundColor: theme.colors.surface2,
1907
1879
  borderLeftColor: theme.colors.brand
@@ -1909,18 +1881,18 @@ function ReplyChip({
1909
1881
  ],
1910
1882
  children: [
1911
1883
  /* @__PURE__ */ jsxs(View, { style: { flex: 1 }, children: [
1912
- /* @__PURE__ */ jsxs(Text, { style: [styles10.replyName, { color: theme.colors.brand }], children: [
1884
+ /* @__PURE__ */ jsxs(Text, { style: [styles9.replyName, { color: theme.colors.brand }], children: [
1913
1885
  "Replying to ",
1914
1886
  senderName
1915
1887
  ] }),
1916
- /* @__PURE__ */ jsx(Text, { style: [styles10.replyBody, { color: theme.colors.ink2 }], numberOfLines: 1, children: message.body ?? "\u2026" })
1888
+ /* @__PURE__ */ jsx(Text, { style: [styles9.replyBody, { color: theme.colors.ink2 }], numberOfLines: 1, children: message.body ?? "\u2026" })
1917
1889
  ] }),
1918
- onCancel ? /* @__PURE__ */ jsx(Pressable, { onPress: onCancel, hitSlop: 8, style: styles10.replyCancel, children: /* @__PURE__ */ jsx(PoolseIcon, { name: "close", size: 16, color: theme.colors.ink2 }) }) : null
1890
+ onCancel ? /* @__PURE__ */ jsx(Pressable, { onPress: onCancel, hitSlop: 8, style: styles9.replyCancel, children: /* @__PURE__ */ jsx(PoolseIcon, { name: "close", size: 16, color: theme.colors.ink2 }) }) : null
1919
1891
  ]
1920
1892
  }
1921
1893
  );
1922
1894
  }
1923
- var styles10 = StyleSheet.create({
1895
+ var styles9 = StyleSheet.create({
1924
1896
  wrap: {
1925
1897
  borderTopWidth: StyleSheet.hairlineWidth,
1926
1898
  paddingTop: 8,
@@ -2017,11 +1989,11 @@ function MentionInput({ conversationId, onSend, ...composerProps }) {
2017
1989
  transparent: true,
2018
1990
  animationType: "slide",
2019
1991
  onRequestClose: () => setPickerOpen(false),
2020
- children: /* @__PURE__ */ jsx(Pressable, { style: styles11.backdrop, onPress: () => setPickerOpen(false), children: /* @__PURE__ */ jsxs(
1992
+ children: /* @__PURE__ */ jsx(Pressable, { style: styles10.backdrop, onPress: () => setPickerOpen(false), children: /* @__PURE__ */ jsxs(
2021
1993
  View,
2022
1994
  {
2023
1995
  style: [
2024
- styles11.sheet,
1996
+ styles10.sheet,
2025
1997
  {
2026
1998
  backgroundColor: theme.colors.surface,
2027
1999
  borderTopLeftRadius: theme.radii.lg,
@@ -2032,7 +2004,7 @@ function MentionInput({ conversationId, onSend, ...composerProps }) {
2032
2004
  /* @__PURE__ */ jsx(
2033
2005
  Text,
2034
2006
  {
2035
- style: [styles11.title, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
2007
+ style: [styles10.title, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
2036
2008
  children: "Mention a member"
2037
2009
  }
2038
2010
  ),
@@ -2063,12 +2035,12 @@ function MentionInput({ conversationId, onSend, ...composerProps }) {
2063
2035
  }
2064
2036
  function MentionRow({ member, onPress }) {
2065
2037
  const theme = usePoolseTheme();
2066
- return /* @__PURE__ */ jsxs(Pressable, { onPress, style: styles11.row, children: [
2038
+ return /* @__PURE__ */ jsxs(Pressable, { onPress, style: styles10.row, children: [
2067
2039
  /* @__PURE__ */ jsx(Avatar, { src: null, name: member.external_id, seed: member.external_id, size: "sm" }),
2068
- /* @__PURE__ */ jsx(Text, { style: [styles11.rowText, { color: theme.colors.ink, fontFamily: theme.type.fontBody }], children: member.external_id })
2040
+ /* @__PURE__ */ jsx(Text, { style: [styles10.rowText, { color: theme.colors.ink, fontFamily: theme.type.fontBody }], children: member.external_id })
2069
2041
  ] });
2070
2042
  }
2071
- var styles11 = StyleSheet.create({
2043
+ var styles10 = StyleSheet.create({
2072
2044
  backdrop: {
2073
2045
  flex: 1,
2074
2046
  backgroundColor: "rgba(0,0,0,0.35)",
@@ -2148,12 +2120,12 @@ function MessageList({
2148
2120
  [renderItem, data.length]
2149
2121
  );
2150
2122
  if (loading && messages.length === 0) {
2151
- return /* @__PURE__ */ jsx(View, { style: [styles12.placeholder, { backgroundColor: theme.colors.paper }], children: /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.brand }) });
2123
+ return /* @__PURE__ */ jsx(View, { style: [styles11.placeholder, { backgroundColor: theme.colors.paper }], children: /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.brand }) });
2152
2124
  }
2153
2125
  if (messages.length === 0) {
2154
- return /* @__PURE__ */ jsx(View, { style: [styles12.placeholder, { backgroundColor: theme.colors.paper }], children: typeof emptyState === "string" ? /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.ink3, fontFamily: theme.type.fontBody }, children: emptyState }) : emptyState ?? null });
2126
+ return /* @__PURE__ */ jsx(View, { style: [styles11.placeholder, { backgroundColor: theme.colors.paper }], children: typeof emptyState === "string" ? /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.ink3, fontFamily: theme.type.fontBody }, children: emptyState }) : emptyState ?? null });
2155
2127
  }
2156
- return /* @__PURE__ */ jsxs(View, { style: styles12.root, children: [
2128
+ return /* @__PURE__ */ jsxs(View, { style: styles11.root, children: [
2157
2129
  /* @__PURE__ */ jsx(
2158
2130
  FlatList,
2159
2131
  {
@@ -2180,7 +2152,7 @@ function MessageList({
2180
2152
  {
2181
2153
  onPress: scrollToBottom,
2182
2154
  style: [
2183
- styles12.jumpButton,
2155
+ styles11.jumpButton,
2184
2156
  {
2185
2157
  backgroundColor: theme.colors.brand
2186
2158
  },
@@ -2189,7 +2161,7 @@ function MessageList({
2189
2161
  accessibilityLabel: `${newCount} new ${newCount === 1 ? "message" : "messages"}, jump to latest`,
2190
2162
  children: [
2191
2163
  /* @__PURE__ */ jsx(PoolseIcon, { name: "chevron-down", size: 14, color: theme.colors.onBrand }),
2192
- /* @__PURE__ */ jsxs(Text, { style: [styles12.jumpText, { color: theme.colors.onBrand }], children: [
2164
+ /* @__PURE__ */ jsxs(Text, { style: [styles11.jumpText, { color: theme.colors.onBrand }], children: [
2193
2165
  newCount,
2194
2166
  " new ",
2195
2167
  newCount === 1 ? "message" : "messages"
@@ -2199,7 +2171,7 @@ function MessageList({
2199
2171
  ) : null
2200
2172
  ] });
2201
2173
  }
2202
- var styles12 = StyleSheet.create({
2174
+ var styles11 = StyleSheet.create({
2203
2175
  root: {
2204
2176
  flex: 1
2205
2177
  },
@@ -2250,11 +2222,11 @@ function MessageActions({
2250
2222
  if (isSelf && onDelete)
2251
2223
  items.push({ label: "Delete", icon: "trash", onPress: onDelete, danger: true });
2252
2224
  const showEmojiRow = !!onPickReaction && emojis.length > 0;
2253
- return /* @__PURE__ */ jsx(Modal, { visible, transparent: true, animationType: "fade", onRequestClose: onClose, children: /* @__PURE__ */ jsx(Pressable, { style: styles13.backdrop, onPress: onClose, children: /* @__PURE__ */ jsxs(
2225
+ return /* @__PURE__ */ jsx(Modal, { visible, transparent: true, animationType: "fade", onRequestClose: onClose, children: /* @__PURE__ */ jsx(Pressable, { style: styles12.backdrop, onPress: onClose, children: /* @__PURE__ */ jsxs(
2254
2226
  View,
2255
2227
  {
2256
2228
  style: [
2257
- styles13.sheet,
2229
+ styles12.sheet,
2258
2230
  {
2259
2231
  backgroundColor: theme.colors.surface,
2260
2232
  borderTopLeftRadius: theme.radii.lg,
@@ -2263,21 +2235,21 @@ function MessageActions({
2263
2235
  theme.shadows.lg
2264
2236
  ],
2265
2237
  children: [
2266
- showEmojiRow ? /* @__PURE__ */ jsx(View, { style: [styles13.emojiRow, { borderBottomColor: theme.colors.border }], children: emojis.map((emoji) => /* @__PURE__ */ jsx(
2238
+ showEmojiRow ? /* @__PURE__ */ jsx(View, { style: [styles12.emojiRow, { borderBottomColor: theme.colors.border }], children: emojis.map((emoji) => /* @__PURE__ */ jsx(
2267
2239
  Pressable,
2268
2240
  {
2269
2241
  onPress: () => {
2270
2242
  onPickReaction(emoji);
2271
2243
  onClose();
2272
2244
  },
2273
- style: styles13.emojiBtn,
2245
+ style: styles12.emojiBtn,
2274
2246
  hitSlop: 6,
2275
2247
  accessibilityLabel: `React with ${emoji}`,
2276
- children: /* @__PURE__ */ jsx(Text, { style: styles13.emoji, children: emoji })
2248
+ children: /* @__PURE__ */ jsx(Text, { style: styles12.emoji, children: emoji })
2277
2249
  },
2278
2250
  emoji
2279
2251
  )) }) : null,
2280
- items.map((item) => /* @__PURE__ */ jsxs(Pressable, { onPress: item.onPress, style: styles13.item, children: [
2252
+ items.map((item) => /* @__PURE__ */ jsxs(Pressable, { onPress: item.onPress, style: styles12.item, children: [
2281
2253
  /* @__PURE__ */ jsx(
2282
2254
  PoolseIcon,
2283
2255
  {
@@ -2290,7 +2262,7 @@ function MessageActions({
2290
2262
  Text,
2291
2263
  {
2292
2264
  style: [
2293
- styles13.itemText,
2265
+ styles12.itemText,
2294
2266
  {
2295
2267
  color: item.danger ? theme.colors.error : theme.colors.ink,
2296
2268
  fontFamily: theme.type.fontBody
@@ -2304,7 +2276,7 @@ function MessageActions({
2304
2276
  }
2305
2277
  ) }) });
2306
2278
  }
2307
- var styles13 = StyleSheet.create({
2279
+ var styles12 = StyleSheet.create({
2308
2280
  backdrop: {
2309
2281
  flex: 1,
2310
2282
  backgroundColor: "rgba(0,0,0,0.35)",
@@ -2369,7 +2341,7 @@ function AttachmentPreview({ attachment, onPress, maxWidth = 280 }) {
2369
2341
  View,
2370
2342
  {
2371
2343
  style: [
2372
- styles14.imagePlaceholder,
2344
+ styles13.imagePlaceholder,
2373
2345
  {
2374
2346
  width: maxWidth,
2375
2347
  height: 200,
@@ -2386,7 +2358,7 @@ function AttachmentPreview({ attachment, onPress, maxWidth = 280 }) {
2386
2358
  onPress,
2387
2359
  disabled: !onPress,
2388
2360
  style: [
2389
- styles14.fileCard,
2361
+ styles13.fileCard,
2390
2362
  {
2391
2363
  backgroundColor: theme.colors.surface2,
2392
2364
  borderColor: theme.colors.border,
@@ -2399,12 +2371,12 @@ function AttachmentPreview({ attachment, onPress, maxWidth = 280 }) {
2399
2371
  /* @__PURE__ */ jsx(
2400
2372
  Text,
2401
2373
  {
2402
- style: [styles14.fileName, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
2374
+ style: [styles13.fileName, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
2403
2375
  numberOfLines: 1,
2404
2376
  children: attachment.original_filename ?? "file"
2405
2377
  }
2406
2378
  ),
2407
- /* @__PURE__ */ jsx(Text, { style: [styles14.fileSize, { color: theme.colors.ink3 }], children: formatBytes2(attachment.byte_size) })
2379
+ /* @__PURE__ */ jsx(Text, { style: [styles13.fileSize, { color: theme.colors.ink3 }], children: formatBytes2(attachment.byte_size) })
2408
2380
  ] })
2409
2381
  ]
2410
2382
  }
@@ -2416,7 +2388,7 @@ function formatBytes2(n) {
2416
2388
  if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB`;
2417
2389
  return `${(n / 1024 / 1024 / 1024).toFixed(1)} GB`;
2418
2390
  }
2419
- var styles14 = StyleSheet.create({
2391
+ var styles13 = StyleSheet.create({
2420
2392
  imagePlaceholder: {},
2421
2393
  fileCard: {
2422
2394
  flexDirection: "row",
@@ -2445,7 +2417,7 @@ function ImageMosaic({ images, maxWidth, radius }) {
2445
2417
  View,
2446
2418
  {
2447
2419
  style: [
2448
- styles15.root,
2420
+ styles14.root,
2449
2421
  {
2450
2422
  borderRadius: r,
2451
2423
  ...maxWidth ? { maxWidth } : {}
@@ -2456,17 +2428,17 @@ function ImageMosaic({ images, maxWidth, radius }) {
2456
2428
  Tile,
2457
2429
  {
2458
2430
  attachment: visible[0],
2459
- style: styles15.tileFull,
2431
+ style: styles14.tileFull,
2460
2432
  onPress: () => setViewerIndex(0),
2461
2433
  overlay: null
2462
2434
  }
2463
2435
  ) : null,
2464
- n === 2 ? /* @__PURE__ */ jsxs(View, { style: styles15.rowEven, children: [
2436
+ n === 2 ? /* @__PURE__ */ jsxs(View, { style: styles14.rowEven, children: [
2465
2437
  /* @__PURE__ */ jsx(
2466
2438
  Tile,
2467
2439
  {
2468
2440
  attachment: visible[0],
2469
- style: styles15.tileHalfSquare,
2441
+ style: styles14.tileHalfSquare,
2470
2442
  onPress: () => setViewerIndex(0),
2471
2443
  overlay: null
2472
2444
  }
@@ -2475,7 +2447,7 @@ function ImageMosaic({ images, maxWidth, radius }) {
2475
2447
  Tile,
2476
2448
  {
2477
2449
  attachment: visible[1],
2478
- style: styles15.tileHalfSquare,
2450
+ style: styles14.tileHalfSquare,
2479
2451
  onPress: () => setViewerIndex(1),
2480
2452
  overlay: null
2481
2453
  }
@@ -2486,17 +2458,17 @@ function ImageMosaic({ images, maxWidth, radius }) {
2486
2458
  Tile,
2487
2459
  {
2488
2460
  attachment: visible[0],
2489
- style: styles15.tileHero,
2461
+ style: styles14.tileHero,
2490
2462
  onPress: () => setViewerIndex(0),
2491
2463
  overlay: null
2492
2464
  }
2493
2465
  ),
2494
- /* @__PURE__ */ jsxs(View, { style: [styles15.rowEven, { marginTop: 2 }], children: [
2466
+ /* @__PURE__ */ jsxs(View, { style: [styles14.rowEven, { marginTop: 2 }], children: [
2495
2467
  /* @__PURE__ */ jsx(
2496
2468
  Tile,
2497
2469
  {
2498
2470
  attachment: visible[1],
2499
- style: styles15.tileHalfSquare,
2471
+ style: styles14.tileHalfSquare,
2500
2472
  onPress: () => setViewerIndex(1),
2501
2473
  overlay: null
2502
2474
  }
@@ -2505,7 +2477,7 @@ function ImageMosaic({ images, maxWidth, radius }) {
2505
2477
  Tile,
2506
2478
  {
2507
2479
  attachment: visible[2],
2508
- style: styles15.tileHalfSquare,
2480
+ style: styles14.tileHalfSquare,
2509
2481
  onPress: () => setViewerIndex(2),
2510
2482
  overlay: null
2511
2483
  }
@@ -2513,12 +2485,12 @@ function ImageMosaic({ images, maxWidth, radius }) {
2513
2485
  ] })
2514
2486
  ] }) : null,
2515
2487
  n === 4 ? /* @__PURE__ */ jsxs(Fragment, { children: [
2516
- /* @__PURE__ */ jsxs(View, { style: styles15.rowEven, children: [
2488
+ /* @__PURE__ */ jsxs(View, { style: styles14.rowEven, children: [
2517
2489
  /* @__PURE__ */ jsx(
2518
2490
  Tile,
2519
2491
  {
2520
2492
  attachment: visible[0],
2521
- style: styles15.tileHalfSquare,
2493
+ style: styles14.tileHalfSquare,
2522
2494
  onPress: () => setViewerIndex(0),
2523
2495
  overlay: null
2524
2496
  }
@@ -2527,18 +2499,18 @@ function ImageMosaic({ images, maxWidth, radius }) {
2527
2499
  Tile,
2528
2500
  {
2529
2501
  attachment: visible[1],
2530
- style: styles15.tileHalfSquare,
2502
+ style: styles14.tileHalfSquare,
2531
2503
  onPress: () => setViewerIndex(1),
2532
2504
  overlay: null
2533
2505
  }
2534
2506
  )
2535
2507
  ] }),
2536
- /* @__PURE__ */ jsxs(View, { style: [styles15.rowEven, { marginTop: 2 }], children: [
2508
+ /* @__PURE__ */ jsxs(View, { style: [styles14.rowEven, { marginTop: 2 }], children: [
2537
2509
  /* @__PURE__ */ jsx(
2538
2510
  Tile,
2539
2511
  {
2540
2512
  attachment: visible[2],
2541
- style: styles15.tileHalfSquare,
2513
+ style: styles14.tileHalfSquare,
2542
2514
  onPress: () => setViewerIndex(2),
2543
2515
  overlay: null
2544
2516
  }
@@ -2547,7 +2519,7 @@ function ImageMosaic({ images, maxWidth, radius }) {
2547
2519
  Tile,
2548
2520
  {
2549
2521
  attachment: visible[3],
2550
- style: styles15.tileHalfSquare,
2522
+ style: styles14.tileHalfSquare,
2551
2523
  onPress: () => setViewerIndex(3),
2552
2524
  overlay: overflow > 0 ? `+${overflow}` : null
2553
2525
  }
@@ -2583,18 +2555,18 @@ function Tile({
2583
2555
  }) {
2584
2556
  const theme = usePoolseTheme();
2585
2557
  const { url } = useAttachmentUrl(attachment.id);
2586
- return /* @__PURE__ */ jsxs(Pressable, { onPress, style: [styles15.tileBase, style], children: [
2558
+ return /* @__PURE__ */ jsxs(Pressable, { onPress, style: [styles14.tileBase, style], children: [
2587
2559
  url ? /* @__PURE__ */ jsx(
2588
2560
  Image,
2589
2561
  {
2590
2562
  source: { uri: url },
2591
- style: [styles15.tileImage, { backgroundColor: theme.colors.surface2 }],
2563
+ style: [styles14.tileImage, { backgroundColor: theme.colors.surface2 }],
2592
2564
  resizeMode: "cover",
2593
2565
  accessible: true,
2594
2566
  accessibilityLabel: attachment.original_filename ?? "image"
2595
2567
  }
2596
- ) : /* @__PURE__ */ jsx(View, { style: [styles15.tileImage, { backgroundColor: theme.colors.surface2 }] }),
2597
- overlay ? /* @__PURE__ */ jsx(View, { style: styles15.overflow, children: /* @__PURE__ */ jsx(Text, { style: styles15.overflowText, children: overlay }) }) : null
2568
+ ) : /* @__PURE__ */ jsx(View, { style: [styles14.tileImage, { backgroundColor: theme.colors.surface2 }] }),
2569
+ overlay ? /* @__PURE__ */ jsx(View, { style: styles14.overflow, children: /* @__PURE__ */ jsx(Text, { style: styles14.overflowText, children: overlay }) }) : null
2598
2570
  ] });
2599
2571
  }
2600
2572
  function FullScreenViewer({
@@ -2603,7 +2575,7 @@ function FullScreenViewer({
2603
2575
  }) {
2604
2576
  const { url } = useAttachmentUrl(attachment.id);
2605
2577
  const { width, height } = Dimensions.get("window");
2606
- return /* @__PURE__ */ jsxs(Pressable, { style: styles15.viewer, onPress: onClose, children: [
2578
+ return /* @__PURE__ */ jsxs(Pressable, { style: styles14.viewer, onPress: onClose, children: [
2607
2579
  url ? /* @__PURE__ */ jsx(
2608
2580
  Image,
2609
2581
  {
@@ -2619,14 +2591,14 @@ function FullScreenViewer({
2619
2591
  {
2620
2592
  onPress: onClose,
2621
2593
  hitSlop: 12,
2622
- style: styles15.closeBtn,
2594
+ style: styles14.closeBtn,
2623
2595
  accessibilityLabel: "Close viewer",
2624
2596
  children: /* @__PURE__ */ jsx(PoolseIcon, { name: "close", size: 20, color: "#ffffff" })
2625
2597
  }
2626
2598
  )
2627
2599
  ] });
2628
2600
  }
2629
- var styles15 = StyleSheet.create({
2601
+ var styles14 = StyleSheet.create({
2630
2602
  root: {
2631
2603
  width: "100%",
2632
2604
  overflow: "hidden",
@@ -2781,7 +2753,7 @@ function MessageBubble({
2781
2753
  View,
2782
2754
  {
2783
2755
  style: [
2784
- styles16.wrap,
2756
+ styles15.wrap,
2785
2757
  {
2786
2758
  alignSelf: isSelf ? "flex-end" : "flex-start",
2787
2759
  // Pixel maxWidth (not percentage) so single-line and
@@ -2795,7 +2767,7 @@ function MessageBubble({
2795
2767
  View,
2796
2768
  {
2797
2769
  style: [
2798
- styles16.bubble,
2770
+ styles15.bubble,
2799
2771
  {
2800
2772
  backgroundColor: bubbleColor,
2801
2773
  borderColor: isSelf ? "transparent" : theme.colors.border,
@@ -2817,7 +2789,7 @@ function MessageBubble({
2817
2789
  theme.shadows.sm
2818
2790
  ],
2819
2791
  children: [
2820
- showSenderName && !isSelf && (groupPosition === "first" || groupPosition === "standalone") ? /* @__PURE__ */ jsx(Text, { style: [styles16.senderName, { color: senderTint, fontFamily: theme.type.fontBody }], children: senderLabel }) : null,
2792
+ showSenderName && !isSelf && (groupPosition === "first" || groupPosition === "standalone") ? /* @__PURE__ */ jsx(Text, { style: [styles15.senderName, { color: senderTint, fontFamily: theme.type.fontBody }], children: senderLabel }) : null,
2821
2793
  message.quoted_message ? /* @__PURE__ */ jsx(
2822
2794
  QuotedCard,
2823
2795
  {
@@ -2833,7 +2805,7 @@ function MessageBubble({
2833
2805
  Text,
2834
2806
  {
2835
2807
  style: [
2836
- styles16.body,
2808
+ styles15.body,
2837
2809
  {
2838
2810
  color: theme.colors.ink3,
2839
2811
  fontFamily: theme.type.fontBody,
@@ -2858,7 +2830,7 @@ function MessageBubble({
2858
2830
  Text,
2859
2831
  {
2860
2832
  style: [
2861
- styles16.body,
2833
+ styles15.body,
2862
2834
  {
2863
2835
  color: textColor,
2864
2836
  fontFamily: theme.type.fontBody,
@@ -2873,18 +2845,18 @@ function MessageBubble({
2873
2845
  Text,
2874
2846
  {
2875
2847
  style: [
2876
- styles16.readMore,
2848
+ styles15.readMore,
2877
2849
  { color: isSelf ? theme.colors.onBrand : theme.colors.brand }
2878
2850
  ],
2879
2851
  children: expanded ? "Show less" : "Read more"
2880
2852
  }
2881
2853
  ) }) : null,
2882
- /* @__PURE__ */ jsxs(View, { style: styles16.metaRow, children: [
2854
+ /* @__PURE__ */ jsxs(View, { style: styles15.metaRow, children: [
2883
2855
  isEdited ? /* @__PURE__ */ jsx(
2884
2856
  Text,
2885
2857
  {
2886
2858
  style: [
2887
- styles16.editedTag,
2859
+ styles15.editedTag,
2888
2860
  {
2889
2861
  color: isSelf ? theme.colors.onBrand : theme.colors.ink3,
2890
2862
  opacity: 0.7
@@ -2897,7 +2869,7 @@ function MessageBubble({
2897
2869
  Text,
2898
2870
  {
2899
2871
  style: [
2900
- styles16.meta,
2872
+ styles15.meta,
2901
2873
  {
2902
2874
  color: isSelf ? theme.colors.onBrand : theme.colors.ink3,
2903
2875
  opacity: isSelf ? 0.85 : 1
@@ -2906,7 +2878,7 @@ function MessageBubble({
2906
2878
  children: time
2907
2879
  }
2908
2880
  ),
2909
- isSelf && readState ? /* @__PURE__ */ jsx(View, { style: styles16.readTick, children: /* @__PURE__ */ jsx(
2881
+ isSelf && readState ? /* @__PURE__ */ jsx(View, { style: styles15.readTick, children: /* @__PURE__ */ jsx(
2910
2882
  PoolseIcon,
2911
2883
  {
2912
2884
  name: readState === "read" ? "check-double" : "check",
@@ -2915,7 +2887,7 @@ function MessageBubble({
2915
2887
  }
2916
2888
  ) }) : null
2917
2889
  ] }),
2918
- actionsTrigger ? /* @__PURE__ */ jsx(View, { style: styles16.actionsSlot, children: actionsTrigger }) : null
2890
+ actionsTrigger ? /* @__PURE__ */ jsx(View, { style: styles15.actionsSlot, children: actionsTrigger }) : null
2919
2891
  ]
2920
2892
  }
2921
2893
  )
@@ -2940,15 +2912,15 @@ function QuotedCard({
2940
2912
  onPress,
2941
2913
  disabled: !onPress,
2942
2914
  style: [
2943
- styles16.quoted,
2915
+ styles15.quoted,
2944
2916
  {
2945
2917
  backgroundColor: bg,
2946
2918
  borderLeftColor: accent
2947
2919
  }
2948
2920
  ],
2949
2921
  children: [
2950
- /* @__PURE__ */ jsx(Text, { style: [styles16.quotedSender, { color: accent }], children: senderName }),
2951
- /* @__PURE__ */ jsx(Text, { style: [styles16.quotedBody, { color: txt }], numberOfLines: 2, children: quoted.body ?? "\u2026" })
2922
+ /* @__PURE__ */ jsx(Text, { style: [styles15.quotedSender, { color: accent }], children: senderName }),
2923
+ /* @__PURE__ */ jsx(Text, { style: [styles15.quotedBody, { color: txt }], numberOfLines: 2, children: quoted.body ?? "\u2026" })
2952
2924
  ]
2953
2925
  }
2954
2926
  );
@@ -2980,7 +2952,7 @@ function formatTime(iso) {
2980
2952
  function isSameDay(a, b) {
2981
2953
  return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
2982
2954
  }
2983
- var styles16 = StyleSheet.create({
2955
+ var styles15 = StyleSheet.create({
2984
2956
  wrap: {
2985
2957
  marginBottom: 2
2986
2958
  },
@@ -3060,7 +3032,7 @@ function ReactionStrip({
3060
3032
  });
3061
3033
  const entries = Object.entries(reactions);
3062
3034
  if (entries.length === 0) return null;
3063
- return /* @__PURE__ */ jsx(View, { style: styles17.strip, children: entries.map(([emoji, userIds]) => {
3035
+ return /* @__PURE__ */ jsx(View, { style: styles16.strip, children: entries.map(([emoji, userIds]) => {
3064
3036
  const userArr = Array.isArray(userIds) ? userIds : [];
3065
3037
  const meReacted = meId !== null && userArr.includes(meId);
3066
3038
  return /* @__PURE__ */ jsxs(
@@ -3068,7 +3040,7 @@ function ReactionStrip({
3068
3040
  {
3069
3041
  onPress: () => meReacted ? removeReaction(emoji) : addReaction(emoji),
3070
3042
  style: [
3071
- styles17.chip,
3043
+ styles16.chip,
3072
3044
  {
3073
3045
  backgroundColor: meReacted ? theme.colors.brandSoft : theme.colors.surface2,
3074
3046
  borderColor: meReacted ? theme.colors.brand : theme.colors.border,
@@ -3077,12 +3049,12 @@ function ReactionStrip({
3077
3049
  }
3078
3050
  ],
3079
3051
  children: [
3080
- /* @__PURE__ */ jsx(Text, { style: styles17.emoji, children: emoji }),
3052
+ /* @__PURE__ */ jsx(Text, { style: styles16.emoji, children: emoji }),
3081
3053
  /* @__PURE__ */ jsx(
3082
3054
  Text,
3083
3055
  {
3084
3056
  style: [
3085
- styles17.count,
3057
+ styles16.count,
3086
3058
  {
3087
3059
  color: meReacted ? theme.colors.brand : theme.colors.ink2,
3088
3060
  fontWeight: meReacted ? "700" : "600"
@@ -3104,11 +3076,11 @@ function ReactionPicker({
3104
3076
  emojis = DEFAULT_EMOJIS
3105
3077
  }) {
3106
3078
  const theme = usePoolseTheme();
3107
- return /* @__PURE__ */ jsx(Modal, { visible, transparent: true, animationType: "fade", onRequestClose: onClose, children: /* @__PURE__ */ jsx(Pressable, { style: styles17.backdrop, onPress: onClose, children: /* @__PURE__ */ jsx(
3079
+ return /* @__PURE__ */ jsx(Modal, { visible, transparent: true, animationType: "fade", onRequestClose: onClose, children: /* @__PURE__ */ jsx(Pressable, { style: styles16.backdrop, onPress: onClose, children: /* @__PURE__ */ jsx(
3108
3080
  View,
3109
3081
  {
3110
3082
  style: [
3111
- styles17.sheet,
3083
+ styles16.sheet,
3112
3084
  {
3113
3085
  backgroundColor: theme.colors.surface,
3114
3086
  borderRadius: theme.radii.lg
@@ -3118,19 +3090,19 @@ function ReactionPicker({
3118
3090
  children: emojis.map((emoji) => /* @__PURE__ */ jsx(
3119
3091
  Pressable,
3120
3092
  {
3121
- style: styles17.emojiBtn,
3093
+ style: styles16.emojiBtn,
3122
3094
  onPress: () => {
3123
3095
  onPick(emoji);
3124
3096
  onClose();
3125
3097
  },
3126
- children: /* @__PURE__ */ jsx(Text, { style: styles17.bigEmoji, children: emoji })
3098
+ children: /* @__PURE__ */ jsx(Text, { style: styles16.bigEmoji, children: emoji })
3127
3099
  },
3128
3100
  emoji
3129
3101
  ))
3130
3102
  }
3131
3103
  ) }) });
3132
3104
  }
3133
- var styles17 = StyleSheet.create({
3105
+ var styles16 = StyleSheet.create({
3134
3106
  strip: {
3135
3107
  flexDirection: "row",
3136
3108
  flexWrap: "wrap",
@@ -3298,15 +3270,15 @@ function MessageRow({
3298
3270
  View,
3299
3271
  {
3300
3272
  style: [
3301
- styles18.row,
3273
+ styles17.row,
3302
3274
  { alignSelf: isSelf ? "flex-end" : "flex-start" },
3303
3275
  // Lift this row above the dim backdrop the parent renders
3304
3276
  // during edit mode. zIndex covers iOS / web; elevation does
3305
3277
  // the equivalent on Android.
3306
- isEditing ? styles18.rowEditing : null
3278
+ isEditing ? styles17.rowEditing : null
3307
3279
  ],
3308
3280
  children: [
3309
- showAvatarSlot ? /* @__PURE__ */ jsx(View, { style: styles18.avatarSlot, children: showAvatarRender ? /* @__PURE__ */ jsx(
3281
+ showAvatarSlot ? /* @__PURE__ */ jsx(View, { style: styles17.avatarSlot, children: showAvatarRender ? /* @__PURE__ */ jsx(
3310
3282
  Avatar,
3311
3283
  {
3312
3284
  src: avatarUrl ?? null,
@@ -3315,14 +3287,14 @@ function MessageRow({
3315
3287
  size: "sm"
3316
3288
  }
3317
3289
  ) : null }) : null,
3318
- /* @__PURE__ */ jsxs(View, { style: styles18.bubbleCol, children: [
3290
+ /* @__PURE__ */ jsxs(View, { style: styles17.bubbleCol, children: [
3319
3291
  swipeEnabled ? /* @__PURE__ */ jsx(
3320
3292
  Animated.View,
3321
3293
  {
3322
3294
  pointerEvents: "none",
3323
3295
  style: [
3324
- styles18.replyHint,
3325
- isSelf ? styles18.replyHintRight : styles18.replyHintLeft,
3296
+ styles17.replyHint,
3297
+ isSelf ? styles17.replyHintRight : styles17.replyHintLeft,
3326
3298
  {
3327
3299
  opacity: replyIconOpacity,
3328
3300
  transform: [{ scale: replyIconScale }],
@@ -3373,13 +3345,13 @@ function MessageRow({
3373
3345
  )
3374
3346
  }
3375
3347
  ),
3376
- threads && (msg.reply_count ?? 0) > 0 && onOpenThread ? /* @__PURE__ */ jsx(Pressable, { onPress: onOpenThread, style: styles18.threadPill, children: /* @__PURE__ */ jsxs(Text, { style: [styles18.threadPillText, { color: theme.colors.brand }], children: [
3348
+ threads && (msg.reply_count ?? 0) > 0 && onOpenThread ? /* @__PURE__ */ jsx(Pressable, { onPress: onOpenThread, style: styles17.threadPill, children: /* @__PURE__ */ jsxs(Text, { style: [styles17.threadPillText, { color: theme.colors.brand }], children: [
3377
3349
  "\u{1F4AC} ",
3378
3350
  msg.reply_count,
3379
3351
  " ",
3380
3352
  msg.reply_count === 1 ? "reply" : "replies"
3381
3353
  ] }) }) : null,
3382
- reactions ? /* @__PURE__ */ jsx(View, { style: [styles18.reactionsRow, { alignSelf: isSelf ? "flex-end" : "flex-start" }], children: /* @__PURE__ */ jsx(
3354
+ reactions ? /* @__PURE__ */ jsx(View, { style: [styles17.reactionsRow, { alignSelf: isSelf ? "flex-end" : "flex-start" }], children: /* @__PURE__ */ jsx(
3383
3355
  ReactionStrip,
3384
3356
  {
3385
3357
  messageId: msg.id,
@@ -3438,7 +3410,7 @@ function MessageRow({
3438
3410
  }
3439
3411
  );
3440
3412
  }
3441
- var styles18 = StyleSheet.create({
3413
+ var styles17 = StyleSheet.create({
3442
3414
  row: {
3443
3415
  flexDirection: "row",
3444
3416
  alignItems: "flex-end",
@@ -3598,13 +3570,13 @@ function ThreadSheet({
3598
3570
  ),
3599
3571
  [rootMessage, meId, labelFor, avatarFor]
3600
3572
  );
3601
- return /* @__PURE__ */ jsxs(View, { style: styles19.modalRoot, children: [
3602
- /* @__PURE__ */ jsx(Pressable, { style: styles19.backdrop, onPress: onClose, accessibilityLabel: "Close thread" }),
3573
+ return /* @__PURE__ */ jsxs(View, { style: styles18.modalRoot, children: [
3574
+ /* @__PURE__ */ jsx(Pressable, { style: styles18.backdrop, onPress: onClose, accessibilityLabel: "Close thread" }),
3603
3575
  /* @__PURE__ */ jsxs(
3604
3576
  Animated.View,
3605
3577
  {
3606
3578
  style: [
3607
- styles19.sheet,
3579
+ styles18.sheet,
3608
3580
  {
3609
3581
  height: sheetHeight,
3610
3582
  backgroundColor: theme.colors.paper,
@@ -3616,14 +3588,14 @@ function ThreadSheet({
3616
3588
  ],
3617
3589
  children: [
3618
3590
  /* @__PURE__ */ jsxs(View, { ...panHandlers, children: [
3619
- /* @__PURE__ */ jsx(View, { style: styles19.dragHandle, children: /* @__PURE__ */ jsx(View, { style: [styles19.dragPill, { backgroundColor: theme.colors.ink3 }] }) }),
3620
- /* @__PURE__ */ jsxs(View, { style: [styles19.header, { borderBottomColor: theme.colors.border }], children: [
3591
+ /* @__PURE__ */ jsx(View, { style: styles18.dragHandle, children: /* @__PURE__ */ jsx(View, { style: [styles18.dragPill, { backgroundColor: theme.colors.ink3 }] }) }),
3592
+ /* @__PURE__ */ jsxs(View, { style: [styles18.header, { borderBottomColor: theme.colors.border }], children: [
3621
3593
  /* @__PURE__ */ jsx(
3622
3594
  Pressable,
3623
3595
  {
3624
3596
  onPress: onClose,
3625
3597
  hitSlop: 16,
3626
- style: [styles19.closeBtn, { backgroundColor: theme.colors.surface2 }],
3598
+ style: [styles18.closeBtn, { backgroundColor: theme.colors.surface2 }],
3627
3599
  accessibilityLabel: "Close thread",
3628
3600
  children: /* @__PURE__ */ jsx(PoolseIcon, { name: "close", size: 18, color: theme.colors.ink })
3629
3601
  }
@@ -3632,7 +3604,7 @@ function ThreadSheet({
3632
3604
  Text,
3633
3605
  {
3634
3606
  style: [
3635
- styles19.title,
3607
+ styles18.title,
3636
3608
  { color: theme.colors.ink, fontFamily: theme.type.fontDisplay }
3637
3609
  ],
3638
3610
  children: "Thread"
@@ -3643,12 +3615,12 @@ function ThreadSheet({
3643
3615
  /* @__PURE__ */ jsxs(
3644
3616
  KeyboardAvoidingView,
3645
3617
  {
3646
- style: styles19.kavBody,
3618
+ style: styles18.kavBody,
3647
3619
  behavior: Platform.OS === "ios" ? "padding" : "height",
3648
3620
  keyboardVerticalOffset: Platform.OS === "ios" ? gutter : 0,
3649
3621
  children: [
3650
- /* @__PURE__ */ jsx(View, { style: [styles19.rootPreview, { backgroundColor: theme.colors.surface2 }], children: rootRow }),
3651
- /* @__PURE__ */ jsx(View, { style: styles19.listWrap, children: /* @__PURE__ */ jsx(
3622
+ /* @__PURE__ */ jsx(View, { style: [styles18.rootPreview, { backgroundColor: theme.colors.surface2 }], children: rootRow }),
3623
+ /* @__PURE__ */ jsx(View, { style: styles18.listWrap, children: /* @__PURE__ */ jsx(
3652
3624
  MessageList,
3653
3625
  {
3654
3626
  messages: replies,
@@ -3684,7 +3656,7 @@ function ThreadSheet({
3684
3656
  )
3685
3657
  ] });
3686
3658
  }
3687
- var styles19 = StyleSheet.create({
3659
+ var styles18 = StyleSheet.create({
3688
3660
  modalRoot: {
3689
3661
  flex: 1,
3690
3662
  justifyContent: "flex-end"
@@ -3756,7 +3728,7 @@ function TypingIndicator({ typing, labelFor }) {
3756
3728
  const ids = Array.from(typing);
3757
3729
  const isTyping = ids.length > 0;
3758
3730
  if (!isTyping) return /* @__PURE__ */ jsx(View, { style: { height: 0 } });
3759
- return /* @__PURE__ */ jsxs(View, { style: styles20.row, children: [
3731
+ return /* @__PURE__ */ jsxs(View, { style: styles19.row, children: [
3760
3732
  /* @__PURE__ */ jsx(TypingDots, {}),
3761
3733
  /* @__PURE__ */ jsx(TypingLabel, { ids, ...labelFor ? { labelFor } : {} })
3762
3734
  ] });
@@ -3801,7 +3773,7 @@ function TypingDots() {
3801
3773
  ],
3802
3774
  opacity: val.interpolate({ inputRange: [0, 1], outputRange: [0.5, 1] })
3803
3775
  });
3804
- return /* @__PURE__ */ jsxs(View, { style: styles20.dotsWrap, children: [
3776
+ return /* @__PURE__ */ jsxs(View, { style: styles19.dotsWrap, children: [
3805
3777
  /* @__PURE__ */ jsx(Animated.View, { style: dot(a) }),
3806
3778
  /* @__PURE__ */ jsx(Animated.View, { style: dot(b) }),
3807
3779
  /* @__PURE__ */ jsx(Animated.View, { style: dot(c) })
@@ -3817,9 +3789,9 @@ function TypingLabel({
3817
3789
  const name1 = useDisplayName(id1, labelFor);
3818
3790
  const name2 = useDisplayName(id2, labelFor);
3819
3791
  const text = ids.length === 1 ? `${name1} is typing` : ids.length === 2 ? `${name1} and ${name2} are typing` : `${ids.length} people are typing`;
3820
- return /* @__PURE__ */ jsx(Text, { style: [styles20.label, { color: theme.colors.ink2, fontFamily: theme.type.fontBody }], children: text });
3792
+ return /* @__PURE__ */ jsx(Text, { style: [styles19.label, { color: theme.colors.ink2, fontFamily: theme.type.fontBody }], children: text });
3821
3793
  }
3822
- var styles20 = StyleSheet.create({
3794
+ var styles19 = StyleSheet.create({
3823
3795
  row: {
3824
3796
  flexDirection: "row",
3825
3797
  alignItems: "center",
@@ -4057,12 +4029,12 @@ function ConversationView({
4057
4029
  return /* @__PURE__ */ jsx(UploadProvider, { children: /* @__PURE__ */ jsxs(
4058
4030
  KeyboardAvoidingView,
4059
4031
  {
4060
- style: [styles21.root, { backgroundColor: theme.colors.paper }],
4032
+ style: [styles20.root, { backgroundColor: theme.colors.paper }],
4061
4033
  behavior: Platform.OS === "ios" ? "padding" : "height",
4062
4034
  keyboardVerticalOffset: keyboardOffset,
4063
4035
  children: [
4064
4036
  headerNode,
4065
- /* @__PURE__ */ jsxs(View, { style: styles21.listWrap, children: [
4037
+ /* @__PURE__ */ jsxs(View, { style: styles20.listWrap, children: [
4066
4038
  /* @__PURE__ */ jsx(
4067
4039
  MessageList,
4068
4040
  {
@@ -4080,7 +4052,7 @@ function ConversationView({
4080
4052
  editingId ? /* @__PURE__ */ jsx(
4081
4053
  Pressable,
4082
4054
  {
4083
- style: styles21.editBackdrop,
4055
+ style: styles20.editBackdrop,
4084
4056
  onPress: () => setEditingId(null),
4085
4057
  accessibilityLabel: "Cancel editing"
4086
4058
  }
@@ -4115,7 +4087,7 @@ function readStateForMessage(msg, members, meId, sequenceByMessageId) {
4115
4087
  });
4116
4088
  return anyRead ? "read" : "sent";
4117
4089
  }
4118
- var styles21 = StyleSheet.create({
4090
+ var styles20 = StyleSheet.create({
4119
4091
  root: {
4120
4092
  flex: 1
4121
4093
  },
@@ -4197,13 +4169,13 @@ function UserPickerSheet({
4197
4169
  animationType: "slide",
4198
4170
  onRequestClose: handleClose,
4199
4171
  statusBarTranslucent: true,
4200
- children: /* @__PURE__ */ jsxs(View, { style: styles22.modalRoot, children: [
4201
- /* @__PURE__ */ jsx(Pressable, { style: styles22.backdrop, onPress: handleClose }),
4172
+ children: /* @__PURE__ */ jsxs(View, { style: styles21.modalRoot, children: [
4173
+ /* @__PURE__ */ jsx(Pressable, { style: styles21.backdrop, onPress: handleClose }),
4202
4174
  /* @__PURE__ */ jsxs(
4203
4175
  Animated.View,
4204
4176
  {
4205
4177
  style: [
4206
- styles22.sheet,
4178
+ styles21.sheet,
4207
4179
  {
4208
4180
  backgroundColor: theme.colors.paper,
4209
4181
  borderTopLeftRadius: theme.radii.xl,
@@ -4213,14 +4185,14 @@ function UserPickerSheet({
4213
4185
  theme.shadows.lg
4214
4186
  ],
4215
4187
  children: [
4216
- /* @__PURE__ */ jsx(View, { ...panHandlers, style: styles22.dragHandle, children: /* @__PURE__ */ jsx(View, { style: [styles22.dragPill, { backgroundColor: theme.colors.ink3 }] }) }),
4217
- /* @__PURE__ */ jsxs(View, { style: styles22.header, children: [
4188
+ /* @__PURE__ */ jsx(View, { ...panHandlers, style: styles21.dragHandle, children: /* @__PURE__ */ jsx(View, { style: [styles21.dragPill, { backgroundColor: theme.colors.ink3 }] }) }),
4189
+ /* @__PURE__ */ jsxs(View, { style: styles21.header, children: [
4218
4190
  /* @__PURE__ */ jsx(
4219
4191
  Pressable,
4220
4192
  {
4221
4193
  onPress: handleClose,
4222
4194
  hitSlop: 16,
4223
- style: [styles22.closeBtn, { backgroundColor: theme.colors.surface2 }],
4195
+ style: [styles21.closeBtn, { backgroundColor: theme.colors.surface2 }],
4224
4196
  accessibilityLabel: "Close picker",
4225
4197
  children: /* @__PURE__ */ jsx(PoolseIcon, { name: "close", size: 18, color: theme.colors.ink })
4226
4198
  }
@@ -4229,7 +4201,7 @@ function UserPickerSheet({
4229
4201
  Text,
4230
4202
  {
4231
4203
  style: [
4232
- styles22.title,
4204
+ styles21.title,
4233
4205
  { color: theme.colors.ink, fontFamily: theme.type.fontDisplay }
4234
4206
  ],
4235
4207
  numberOfLines: 1,
@@ -4243,7 +4215,7 @@ function UserPickerSheet({
4243
4215
  disabled: !canCreate,
4244
4216
  hitSlop: 6,
4245
4217
  style: [
4246
- styles22.createBtn,
4218
+ styles21.createBtn,
4247
4219
  {
4248
4220
  backgroundColor: canCreate ? theme.colors.brand : theme.colors.surface2,
4249
4221
  opacity: canCreate ? 1 : 0.6
@@ -4274,7 +4246,7 @@ function UserPickerSheet({
4274
4246
  value: groupName,
4275
4247
  onChangeText: setGroupName,
4276
4248
  style: [
4277
- styles22.groupNameInput,
4249
+ styles21.groupNameInput,
4278
4250
  {
4279
4251
  color: theme.colors.ink,
4280
4252
  backgroundColor: theme.colors.surface2,
@@ -4293,7 +4265,7 @@ function UserPickerSheet({
4293
4265
  value: query,
4294
4266
  onChangeText: setQuery,
4295
4267
  style: [
4296
- styles22.searchInput,
4268
+ styles21.searchInput,
4297
4269
  {
4298
4270
  color: theme.colors.ink,
4299
4271
  backgroundColor: theme.colors.surface2,
@@ -4309,7 +4281,7 @@ function UserPickerSheet({
4309
4281
  {
4310
4282
  data: filtered,
4311
4283
  keyExtractor: (u) => u.externalId,
4312
- ListEmptyComponent: /* @__PURE__ */ jsx(View, { style: styles22.empty, children: /* @__PURE__ */ jsx(Text, { style: [styles22.emptyText, { color: theme.colors.ink3 }], children: query ? "No matches." : "No users to show." }) }),
4284
+ ListEmptyComponent: /* @__PURE__ */ jsx(View, { style: styles21.empty, children: /* @__PURE__ */ jsx(Text, { style: [styles21.emptyText, { color: theme.colors.ink3 }], children: query ? "No matches." : "No users to show." }) }),
4313
4285
  renderItem: ({ item }) => /* @__PURE__ */ jsx(
4314
4286
  UserRow,
4315
4287
  {
@@ -4347,29 +4319,29 @@ function UserRow({
4347
4319
  {
4348
4320
  onPress,
4349
4321
  style: [
4350
- styles22.row,
4322
+ styles21.row,
4351
4323
  {
4352
4324
  backgroundColor: selected ? theme.colors.brandSoft : "transparent"
4353
4325
  }
4354
4326
  ],
4355
4327
  children: [
4356
- user.avatarUrl ? /* @__PURE__ */ jsx(Image, { source: { uri: user.avatarUrl }, style: styles22.avatarImg }) : /* @__PURE__ */ jsx(Avatar, { src: null, name: user.name, seed: user.externalId, size: "md" }),
4357
- /* @__PURE__ */ jsxs(View, { style: styles22.body, children: [
4328
+ user.avatarUrl ? /* @__PURE__ */ jsx(Image, { source: { uri: user.avatarUrl }, style: styles21.avatarImg }) : /* @__PURE__ */ jsx(Avatar, { src: null, name: user.name, seed: user.externalId, size: "md" }),
4329
+ /* @__PURE__ */ jsxs(View, { style: styles21.body, children: [
4358
4330
  /* @__PURE__ */ jsx(
4359
4331
  Text,
4360
4332
  {
4361
- style: [styles22.name, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
4333
+ style: [styles21.name, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
4362
4334
  numberOfLines: 1,
4363
4335
  children: user.name
4364
4336
  }
4365
4337
  ),
4366
- user.subtitle ? /* @__PURE__ */ jsx(Text, { style: [styles22.subtitle, { color: theme.colors.ink3 }], numberOfLines: 1, children: user.subtitle }) : null
4338
+ user.subtitle ? /* @__PURE__ */ jsx(Text, { style: [styles21.subtitle, { color: theme.colors.ink3 }], numberOfLines: 1, children: user.subtitle }) : null
4367
4339
  ] }),
4368
4340
  isGroup ? /* @__PURE__ */ jsx(
4369
4341
  View,
4370
4342
  {
4371
4343
  style: [
4372
- styles22.checkbox,
4344
+ styles21.checkbox,
4373
4345
  {
4374
4346
  borderColor: selected ? theme.colors.brand : theme.colors.border,
4375
4347
  backgroundColor: selected ? theme.colors.brand : "transparent"
@@ -4382,7 +4354,7 @@ function UserRow({
4382
4354
  }
4383
4355
  );
4384
4356
  }
4385
- var styles22 = StyleSheet.create({
4357
+ var styles21 = StyleSheet.create({
4386
4358
  modalRoot: {
4387
4359
  flex: 1,
4388
4360
  justifyContent: "flex-end"
@@ -4642,7 +4614,7 @@ var PoolseInbox = forwardRef(
4642
4614
  },
4643
4615
  [renderConversationRow, meExternalId, labelFor, avatarFor, unreadCounts]
4644
4616
  );
4645
- return /* @__PURE__ */ jsxs(View, { style: [styles23.root, { backgroundColor: theme.colors.paper }], children: [
4617
+ return /* @__PURE__ */ jsxs(View, { style: [styles22.root, { backgroundColor: theme.colors.paper }], children: [
4646
4618
  /* @__PURE__ */ jsxs(
4647
4619
  Animated.View,
4648
4620
  {
@@ -4653,7 +4625,7 @@ var PoolseInbox = forwardRef(
4653
4625
  View,
4654
4626
  {
4655
4627
  style: [
4656
- styles23.listHeader,
4628
+ styles22.listHeader,
4657
4629
  {
4658
4630
  backgroundColor: theme.colors.surface,
4659
4631
  borderBottomColor: theme.colors.border
@@ -4664,13 +4636,13 @@ var PoolseInbox = forwardRef(
4664
4636
  Text,
4665
4637
  {
4666
4638
  style: [
4667
- styles23.listTitle,
4639
+ styles22.listTitle,
4668
4640
  { color: theme.colors.ink, fontFamily: theme.type.fontDisplay }
4669
4641
  ],
4670
4642
  children: title
4671
4643
  }
4672
4644
  ),
4673
- /* @__PURE__ */ jsxs(View, { style: styles23.headerActions, children: [
4645
+ /* @__PURE__ */ jsxs(View, { style: styles22.headerActions, children: [
4674
4646
  users ? /* @__PURE__ */ jsxs(Fragment, { children: [
4675
4647
  /* @__PURE__ */ jsx(
4676
4648
  HeaderIconButton,
@@ -4783,7 +4755,7 @@ function HeaderIconButton({
4783
4755
  hitSlop: 10,
4784
4756
  accessibilityLabel: label,
4785
4757
  style: ({ pressed }) => [
4786
- styles23.iconBtn,
4758
+ styles22.iconBtn,
4787
4759
  {
4788
4760
  backgroundColor: pressed ? theme.colors.surface2 : "transparent"
4789
4761
  }
@@ -4813,31 +4785,31 @@ function DefaultConversationRow({
4813
4785
  {
4814
4786
  onPress,
4815
4787
  style: [
4816
- styles23.row,
4788
+ styles22.row,
4817
4789
  {
4818
4790
  backgroundColor: selected ? theme.colors.surface2 : "transparent",
4819
4791
  borderBottomColor: theme.colors.border
4820
4792
  }
4821
4793
  ],
4822
4794
  children: [
4823
- avatarUrl ? /* @__PURE__ */ jsx(Image, { source: { uri: avatarUrl }, style: styles23.rowAvatar }) : /* @__PURE__ */ jsx(Avatar, { src: null, name: title, seed: avatarSeed, size: "md" }),
4824
- /* @__PURE__ */ jsxs(View, { style: styles23.rowBody, children: [
4795
+ avatarUrl ? /* @__PURE__ */ jsx(Image, { source: { uri: avatarUrl }, style: styles22.rowAvatar }) : /* @__PURE__ */ jsx(Avatar, { src: null, name: title, seed: avatarSeed, size: "md" }),
4796
+ /* @__PURE__ */ jsxs(View, { style: styles22.rowBody, children: [
4825
4797
  /* @__PURE__ */ jsx(
4826
4798
  Text,
4827
4799
  {
4828
- style: [styles23.rowTitle, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
4800
+ style: [styles22.rowTitle, { color: theme.colors.ink, fontFamily: theme.type.fontBody }],
4829
4801
  numberOfLines: 1,
4830
4802
  children: title
4831
4803
  }
4832
4804
  ),
4833
- /* @__PURE__ */ jsx(Text, { style: [styles23.rowSubtitle, { color: theme.colors.ink3 }], numberOfLines: 1, children: conv.last_message_preview ?? (isDirect ? otherExtId ?? "" : `${(conv.member_external_ids ?? []).length} members`) })
4805
+ /* @__PURE__ */ jsx(Text, { style: [styles22.rowSubtitle, { color: theme.colors.ink3 }], numberOfLines: 1, children: conv.last_message_preview ?? (isDirect ? otherExtId ?? "" : `${(conv.member_external_ids ?? []).length} members`) })
4834
4806
  ] }),
4835
- unread > 0 ? /* @__PURE__ */ jsx(View, { style: [styles23.unreadPill, { backgroundColor: theme.colors.unreadPill }], children: /* @__PURE__ */ jsx(Text, { style: [styles23.unreadText, { color: theme.colors.unreadPillText }], children: unread > 99 ? "99+" : String(unread) }) }) : null
4807
+ unread > 0 ? /* @__PURE__ */ jsx(View, { style: [styles22.unreadPill, { backgroundColor: theme.colors.unreadPill }], children: /* @__PURE__ */ jsx(Text, { style: [styles22.unreadText, { color: theme.colors.unreadPillText }], children: unread > 99 ? "99+" : String(unread) }) }) : null
4836
4808
  ]
4837
4809
  }
4838
4810
  );
4839
4811
  }
4840
- var styles23 = StyleSheet.create({
4812
+ var styles22 = StyleSheet.create({
4841
4813
  root: {
4842
4814
  flex: 1,
4843
4815
  overflow: "hidden"
@@ -5041,10 +5013,10 @@ function VoiceRoomBar({
5041
5013
  View,
5042
5014
  {
5043
5015
  style: [
5044
- styles24.bar,
5016
+ styles23.bar,
5045
5017
  { backgroundColor: theme.colors.surface, borderBottomColor: theme.colors.border }
5046
5018
  ],
5047
- children: unavailableSlot ?? /* @__PURE__ */ jsx(Text, { style: [styles24.hint, { color: theme.colors.ink3 }], children: "Voice needs react-native-webrtc and a development build." })
5019
+ children: unavailableSlot ?? /* @__PURE__ */ jsx(Text, { style: [styles23.hint, { color: theme.colors.ink3 }], children: "Voice needs react-native-webrtc and a development build." })
5048
5020
  }
5049
5021
  );
5050
5022
  }
@@ -5052,22 +5024,22 @@ function VoiceRoomBar({
5052
5024
  View,
5053
5025
  {
5054
5026
  style: [
5055
- styles24.bar,
5027
+ styles23.bar,
5056
5028
  { backgroundColor: theme.colors.surface, borderBottomColor: theme.colors.border }
5057
5029
  ],
5058
5030
  children: [
5059
- /* @__PURE__ */ jsxs(View, { style: styles24.row, children: [
5031
+ /* @__PURE__ */ jsxs(View, { style: styles23.row, children: [
5060
5032
  /* @__PURE__ */ jsx(
5061
5033
  View,
5062
5034
  {
5063
5035
  style: [
5064
- styles24.dot,
5036
+ styles23.dot,
5065
5037
  { backgroundColor: connected ? theme.colors.brand : theme.colors.ink3 }
5066
5038
  ]
5067
5039
  }
5068
5040
  ),
5069
5041
  connected ? /* @__PURE__ */ jsxs(Fragment, { children: [
5070
- /* @__PURE__ */ jsx(View, { style: styles24.people, children: participants.length === 0 ? /* @__PURE__ */ jsx(Text, { style: [styles24.hint, { color: theme.colors.ink3 }], children: "Waiting for others\u2026" }) : participants.map((p) => /* @__PURE__ */ jsx(ParticipantChip, { participant: p, label: label(p.userId) }, p.userId)) }),
5042
+ /* @__PURE__ */ jsx(View, { style: styles23.people, children: participants.length === 0 ? /* @__PURE__ */ jsx(Text, { style: [styles23.hint, { color: theme.colors.ink3 }], children: "Waiting for others\u2026" }) : participants.map((p) => /* @__PURE__ */ jsx(ParticipantChip, { participant: p, label: label(p.userId) }, p.userId)) }),
5071
5043
  /* @__PURE__ */ jsx(
5072
5044
  Pressable,
5073
5045
  {
@@ -5075,7 +5047,7 @@ function VoiceRoomBar({
5075
5047
  accessibilityRole: "button",
5076
5048
  accessibilityState: { selected: muted },
5077
5049
  style: [
5078
- styles24.btn,
5050
+ styles23.btn,
5079
5051
  {
5080
5052
  borderColor: muted ? theme.colors.brand : theme.colors.border,
5081
5053
  backgroundColor: muted ? theme.colors.brandSoft : theme.colors.paper
@@ -5089,25 +5061,25 @@ function VoiceRoomBar({
5089
5061
  {
5090
5062
  onPress: leave,
5091
5063
  accessibilityRole: "button",
5092
- style: [styles24.btn, { borderColor: theme.colors.error }],
5064
+ style: [styles23.btn, { borderColor: theme.colors.error }],
5093
5065
  children: /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.error }, children: "Leave" })
5094
5066
  }
5095
5067
  )
5096
5068
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5097
- /* @__PURE__ */ jsx(Text, { style: [styles24.hint, { color: theme.colors.ink3 }], children: status === "connecting" ? "Connecting\u2026" : "Talk to everyone here" }),
5069
+ /* @__PURE__ */ jsx(Text, { style: [styles23.hint, { color: theme.colors.ink3 }], children: status === "connecting" ? "Connecting\u2026" : "Talk to everyone here" }),
5098
5070
  /* @__PURE__ */ jsx(
5099
5071
  Pressable,
5100
5072
  {
5101
5073
  onPress: () => void join(),
5102
5074
  disabled: status === "connecting",
5103
5075
  accessibilityRole: "button",
5104
- style: [styles24.btn, styles24.btnPrimary, { backgroundColor: theme.colors.brand }],
5076
+ style: [styles23.btn, styles23.btnPrimary, { backgroundColor: theme.colors.brand }],
5105
5077
  children: /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.onBrand, fontWeight: "600" }, children: status === "connecting" ? "Joining\u2026" : "Join Voice" })
5106
5078
  }
5107
5079
  )
5108
5080
  ] })
5109
5081
  ] }),
5110
- error && /* @__PURE__ */ jsx(Text, { style: [styles24.error, { color: theme.colors.error }], children: error.message })
5082
+ error && /* @__PURE__ */ jsx(Text, { style: [styles23.error, { color: theme.colors.error }], children: error.message })
5111
5083
  ]
5112
5084
  }
5113
5085
  );
@@ -5118,7 +5090,7 @@ function ParticipantChip({ participant, label }) {
5118
5090
  View,
5119
5091
  {
5120
5092
  style: [
5121
- styles24.chip,
5093
+ styles23.chip,
5122
5094
  {
5123
5095
  backgroundColor: theme.colors.paper,
5124
5096
  // The speaking ring is a border colour swap rather than an
@@ -5137,7 +5109,7 @@ function ParticipantChip({ participant, label }) {
5137
5109
  }
5138
5110
  );
5139
5111
  }
5140
- var styles24 = StyleSheet.create({
5112
+ var styles23 = StyleSheet.create({
5141
5113
  bar: { paddingHorizontal: 14, paddingVertical: 8, borderBottomWidth: StyleSheet.hairlineWidth },
5142
5114
  row: { flexDirection: "row", alignItems: "center", gap: 8, flexWrap: "wrap" },
5143
5115
  dot: { width: 8, height: 8, borderRadius: 4 },
@@ -5156,6 +5128,682 @@ var styles24 = StyleSheet.create({
5156
5128
  hint: { fontSize: 13, flexShrink: 1 },
5157
5129
  error: { fontSize: 12, marginTop: 6 }
5158
5130
  });
5131
+
5132
+ // src/voice/callTheme.ts
5133
+ var callColors = {
5134
+ /** Behind everything. Near-black, warmed very slightly to match the brand. */
5135
+ bg: "#101012",
5136
+ /** Behind a participant tile with no video. */
5137
+ tile: "#1c1c20",
5138
+ /** Scrim under the top/bottom chrome so text stays legible over video. */
5139
+ scrim: "rgba(0,0,0,0.45)",
5140
+ text: "#ffffff",
5141
+ textDim: "rgba(255,255,255,0.72)",
5142
+ textFaint: "rgba(255,255,255,0.5)",
5143
+ /** A control that is off / inactive — translucent over whatever is behind. */
5144
+ control: "rgba(255,255,255,0.16)",
5145
+ /**
5146
+ * A control that is ENGAGED (muted, speaker on, camera off).
5147
+ *
5148
+ * Inverted rather than tinted: at a glance the user needs to see
5149
+ * *which* controls are doing something, and a solid white circle is
5150
+ * unmistakable against five translucent ones in a dim room.
5151
+ */
5152
+ controlActive: "#ffffff",
5153
+ controlActiveIcon: "#101012",
5154
+ danger: "#ef4444",
5155
+ accept: "#22c55e",
5156
+ onAction: "#ffffff"
5157
+ };
5158
+ var CONTROL_SIZE = 58;
5159
+ var CONTROL_SIZE_LARGE = 68;
5160
+ var ICONS2 = {
5161
+ speaker: "speaker",
5162
+ earpiece: "phone",
5163
+ headset: "headphones",
5164
+ bluetooth: "bluetooth",
5165
+ auto: "phone"
5166
+ };
5167
+ function AudioRouteSheet({
5168
+ visible,
5169
+ routes,
5170
+ selectedRouteId,
5171
+ onSelect,
5172
+ onClose
5173
+ }) {
5174
+ return /* @__PURE__ */ jsx(Modal, { visible, transparent: true, animationType: "fade", onRequestClose: onClose, children: /* @__PURE__ */ jsx(Pressable, { style: styles24.backdrop, onPress: onClose, accessibilityRole: "button", children: /* @__PURE__ */ jsxs(Pressable, { style: styles24.sheet, onPress: () => {
5175
+ }, children: [
5176
+ /* @__PURE__ */ jsx(View, { style: styles24.grabber }),
5177
+ /* @__PURE__ */ jsx(Text, { style: styles24.title, children: "Audio output" }),
5178
+ routes.map((route) => {
5179
+ const selected = route.id === selectedRouteId;
5180
+ return /* @__PURE__ */ jsxs(
5181
+ Pressable,
5182
+ {
5183
+ onPress: () => {
5184
+ onSelect(route.id);
5185
+ onClose();
5186
+ },
5187
+ accessibilityRole: "button",
5188
+ accessibilityState: { selected },
5189
+ style: ({ pressed }) => [styles24.row, pressed && styles24.rowPressed],
5190
+ children: [
5191
+ /* @__PURE__ */ jsx(
5192
+ PoolseIcon,
5193
+ {
5194
+ name: ICONS2[route.kind],
5195
+ size: 21,
5196
+ color: selected ? callColors.accept : callColors.text,
5197
+ label: null
5198
+ }
5199
+ ),
5200
+ /* @__PURE__ */ jsx(Text, { style: [styles24.label, selected && styles24.labelSelected], children: route.label }),
5201
+ selected && /* @__PURE__ */ jsx(PoolseIcon, { name: "check", size: 19, color: callColors.accept, label: "selected" })
5202
+ ]
5203
+ },
5204
+ route.id
5205
+ );
5206
+ })
5207
+ ] }) }) });
5208
+ }
5209
+ var styles24 = StyleSheet.create({
5210
+ backdrop: { flex: 1, backgroundColor: "rgba(0,0,0,0.55)", justifyContent: "flex-end" },
5211
+ sheet: {
5212
+ backgroundColor: callColors.tile,
5213
+ borderTopLeftRadius: 22,
5214
+ borderTopRightRadius: 22,
5215
+ paddingHorizontal: 12,
5216
+ paddingTop: 10,
5217
+ paddingBottom: 34,
5218
+ gap: 2
5219
+ },
5220
+ grabber: {
5221
+ alignSelf: "center",
5222
+ width: 38,
5223
+ height: 4,
5224
+ borderRadius: 2,
5225
+ backgroundColor: callColors.textFaint,
5226
+ marginBottom: 12
5227
+ },
5228
+ title: {
5229
+ color: callColors.textDim,
5230
+ fontSize: 12.5,
5231
+ fontWeight: "600",
5232
+ textTransform: "uppercase",
5233
+ letterSpacing: 0.6,
5234
+ paddingHorizontal: 12,
5235
+ paddingBottom: 8
5236
+ },
5237
+ row: {
5238
+ flexDirection: "row",
5239
+ alignItems: "center",
5240
+ gap: 14,
5241
+ paddingHorizontal: 12,
5242
+ paddingVertical: 15,
5243
+ borderRadius: 14
5244
+ },
5245
+ rowPressed: { backgroundColor: "rgba(255,255,255,0.08)" },
5246
+ label: { flex: 1, color: callColors.text, fontSize: 16, fontWeight: "500" },
5247
+ labelSelected: { color: callColors.accept }
5248
+ });
5249
+ function CallAvatar({ name, seed, size, speaking = false }) {
5250
+ const ring = Math.max(2, Math.round(size * 0.045));
5251
+ const outer = size + ring * 4;
5252
+ return /* @__PURE__ */ jsx(
5253
+ View,
5254
+ {
5255
+ style: [
5256
+ styles25.ring,
5257
+ {
5258
+ width: outer,
5259
+ height: outer,
5260
+ borderRadius: outer / 2,
5261
+ borderWidth: ring,
5262
+ borderColor: speaking ? callColors.accept : "transparent"
5263
+ }
5264
+ ],
5265
+ children: /* @__PURE__ */ jsx(
5266
+ View,
5267
+ {
5268
+ style: [
5269
+ styles25.circle,
5270
+ {
5271
+ width: size,
5272
+ height: size,
5273
+ borderRadius: size / 2,
5274
+ backgroundColor: userColor(seed)
5275
+ }
5276
+ ],
5277
+ children: /* @__PURE__ */ jsx(
5278
+ Text,
5279
+ {
5280
+ style: [styles25.initials, { fontSize: Math.round(size * 0.38) }],
5281
+ numberOfLines: 1,
5282
+ allowFontScaling: false,
5283
+ children: initialsOf(name)
5284
+ }
5285
+ )
5286
+ }
5287
+ )
5288
+ }
5289
+ );
5290
+ }
5291
+ function initialsOf(name) {
5292
+ const parts = name.trim().split(/\s+/).filter(Boolean);
5293
+ if (parts.length === 0) return "?";
5294
+ if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
5295
+ return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
5296
+ }
5297
+ var styles25 = StyleSheet.create({
5298
+ ring: { alignItems: "center", justifyContent: "center" },
5299
+ circle: { alignItems: "center", justifyContent: "center" },
5300
+ initials: { color: "#ffffff", fontWeight: "600" }
5301
+ });
5302
+ function CallControlButton({
5303
+ icon,
5304
+ label,
5305
+ active = false,
5306
+ tone = "neutral",
5307
+ large = false,
5308
+ disabled = false,
5309
+ onPress,
5310
+ accessibilityLabel
5311
+ }) {
5312
+ const size = large ? CONTROL_SIZE_LARGE : CONTROL_SIZE;
5313
+ const background = tone === "danger" ? callColors.danger : tone === "accept" ? callColors.accept : active ? callColors.controlActive : callColors.control;
5314
+ const iconColor = tone === "neutral" && active ? callColors.controlActiveIcon : callColors.onAction;
5315
+ return /* @__PURE__ */ jsxs(View, { style: styles26.slot, children: [
5316
+ /* @__PURE__ */ jsx(
5317
+ Pressable,
5318
+ {
5319
+ onPress,
5320
+ disabled,
5321
+ accessibilityRole: "button",
5322
+ accessibilityState: { disabled, selected: active },
5323
+ accessibilityLabel: accessibilityLabel ?? label ?? icon,
5324
+ hitSlop: 8,
5325
+ style: ({ pressed }) => [
5326
+ styles26.button,
5327
+ {
5328
+ width: size,
5329
+ height: size,
5330
+ borderRadius: size / 2,
5331
+ backgroundColor: background,
5332
+ // Feedback without a layout shift — scaling a round button
5333
+ // inside a fixed slot keeps the row from twitching.
5334
+ opacity: disabled ? 0.4 : pressed ? 0.75 : 1
5335
+ }
5336
+ ],
5337
+ children: /* @__PURE__ */ jsx(PoolseIcon, { name: icon, size: large ? 30 : 25, color: iconColor, label: null })
5338
+ }
5339
+ ),
5340
+ label ? /* @__PURE__ */ jsx(Text, { numberOfLines: 1, style: styles26.label, children: label }) : null
5341
+ ] });
5342
+ }
5343
+ var styles26 = StyleSheet.create({
5344
+ // Fixed width so captions of different lengths cannot shift the row.
5345
+ slot: { width: 76, alignItems: "center", gap: 7 },
5346
+ button: { alignItems: "center", justifyContent: "center" },
5347
+ label: { color: callColors.textDim, fontSize: 11.5, fontWeight: "500" }
5348
+ });
5349
+ function CallParticipantTile({
5350
+ participant,
5351
+ VideoTrack,
5352
+ label,
5353
+ style,
5354
+ avatarSize = 72,
5355
+ bleed = false,
5356
+ hideName = false
5357
+ }) {
5358
+ const name = participant.isSelf ? "You" : participant.name ?? label(participant.userId);
5359
+ const hasVideo = Boolean(participant.videoTrackRef && VideoTrack);
5360
+ return /* @__PURE__ */ jsxs(
5361
+ View,
5362
+ {
5363
+ style: [
5364
+ styles27.tile,
5365
+ bleed ? styles27.bleed : styles27.rounded,
5366
+ {
5367
+ backgroundColor: callColors.tile,
5368
+ // A ring rather than a badge: it reads at a glance across a
5369
+ // grid, and it doesn't cover anyone's face.
5370
+ borderColor: participant.speaking ? callColors.accept : "transparent"
5371
+ },
5372
+ style
5373
+ ],
5374
+ children: [
5375
+ hasVideo && VideoTrack ? /* @__PURE__ */ jsx(
5376
+ VideoTrack,
5377
+ {
5378
+ trackRef: participant.videoTrackRef,
5379
+ style: StyleSheet.absoluteFillObject,
5380
+ objectFit: "cover",
5381
+ mirror: participant.isSelf,
5382
+ zOrder: participant.isSelf ? 1 : 0
5383
+ }
5384
+ ) : /* @__PURE__ */ jsx(View, { style: styles27.placeholder, children: /* @__PURE__ */ jsx(
5385
+ CallAvatar,
5386
+ {
5387
+ name,
5388
+ seed: participant.userId,
5389
+ size: avatarSize,
5390
+ speaking: participant.speaking
5391
+ }
5392
+ ) }),
5393
+ !hideName && /* @__PURE__ */ jsxs(View, { style: [styles27.chip, bleed ? styles27.chipBleed : styles27.chipTile], children: [
5394
+ !participant.micEnabled && // Worth showing even for yourself — it is the answer to
5395
+ // "why can nobody hear me".
5396
+ /* @__PURE__ */ jsx(PoolseIcon, { name: "mic-off", size: 13, color: callColors.text, label: "muted" }),
5397
+ /* @__PURE__ */ jsx(Text, { numberOfLines: 1, style: styles27.name, children: name })
5398
+ ] })
5399
+ ]
5400
+ }
5401
+ );
5402
+ }
5403
+ var styles27 = StyleSheet.create({
5404
+ tile: { overflow: "hidden", justifyContent: "flex-end", borderWidth: 2 },
5405
+ rounded: { borderRadius: 18 },
5406
+ bleed: { borderRadius: 0, borderWidth: 0 },
5407
+ placeholder: { ...StyleSheet.absoluteFillObject, alignItems: "center", justifyContent: "center" },
5408
+ chip: {
5409
+ flexDirection: "row",
5410
+ alignItems: "center",
5411
+ gap: 5,
5412
+ alignSelf: "flex-start",
5413
+ borderRadius: 999,
5414
+ // Legible over both a bright video frame and a flat placeholder.
5415
+ backgroundColor: "rgba(0,0,0,0.55)"
5416
+ },
5417
+ chipTile: { margin: 8, paddingHorizontal: 9, paddingVertical: 4.5, maxWidth: "92%" },
5418
+ chipBleed: { margin: 14, paddingHorizontal: 11, paddingVertical: 6, maxWidth: "80%" },
5419
+ name: { color: callColors.text, fontSize: 12.5, fontWeight: "600", flexShrink: 1 }
5420
+ });
5421
+ var PIP_WIDTH = 108;
5422
+ var PIP_HEIGHT = 152;
5423
+ var MARGIN = 14;
5424
+ function LocalVideoPip({
5425
+ participant,
5426
+ VideoTrack,
5427
+ label,
5428
+ bounds,
5429
+ insets,
5430
+ onPress
5431
+ }) {
5432
+ const top = insets?.top ?? 0;
5433
+ const bottom = insets?.bottom ?? 0;
5434
+ const corners = useMemo(() => {
5435
+ const right = Math.max(MARGIN, bounds.width - PIP_WIDTH - MARGIN);
5436
+ const low = Math.max(MARGIN, bounds.height - PIP_HEIGHT - bottom - MARGIN);
5437
+ const high = top + MARGIN;
5438
+ return [
5439
+ { x: MARGIN, y: high },
5440
+ { x: right, y: high },
5441
+ { x: MARGIN, y: low },
5442
+ { x: right, y: low }
5443
+ ];
5444
+ }, [bounds.width, bounds.height, top, bottom]);
5445
+ const [corner, setCorner] = useState(1);
5446
+ const pan = useRef(new Animated.ValueXY(corners[1])).current;
5447
+ const cornersRef = useRef(corners);
5448
+ cornersRef.current = corners;
5449
+ const cornerRef = useRef(corner);
5450
+ cornerRef.current = corner;
5451
+ const dragged = useRef(false);
5452
+ useEffect(() => {
5453
+ if (dragged.current) return;
5454
+ const at = corners[corner];
5455
+ if (at) pan.setValue(at);
5456
+ }, [corners, corner, pan]);
5457
+ const responder = useMemo(
5458
+ () => PanResponder.create({
5459
+ // Claim the gesture only once it is clearly a drag. A lower
5460
+ // threshold would swallow the taps that flip the camera.
5461
+ onMoveShouldSetPanResponder: (_evt, g) => Math.abs(g.dx) > 4 || Math.abs(g.dy) > 4,
5462
+ onPanResponderGrant: () => {
5463
+ dragged.current = true;
5464
+ const at = cornersRef.current[cornerRef.current] ?? cornersRef.current[1];
5465
+ pan.setOffset({ x: at.x, y: at.y });
5466
+ pan.setValue({ x: 0, y: 0 });
5467
+ },
5468
+ onPanResponderMove: Animated.event([null, { dx: pan.x, dy: pan.y }], {
5469
+ useNativeDriver: false
5470
+ }),
5471
+ onPanResponderRelease: (_evt, g) => {
5472
+ pan.flattenOffset();
5473
+ const at = cornersRef.current[cornerRef.current] ?? cornersRef.current[1];
5474
+ const dropped = { x: at.x + g.dx, y: at.y + g.dy };
5475
+ let best = 0;
5476
+ let bestDistance = Number.POSITIVE_INFINITY;
5477
+ cornersRef.current.forEach((c, index) => {
5478
+ const distance = (c.x - dropped.x) ** 2 + (c.y - dropped.y) ** 2;
5479
+ if (distance < bestDistance) {
5480
+ bestDistance = distance;
5481
+ best = index;
5482
+ }
5483
+ });
5484
+ setCorner(best);
5485
+ Animated.spring(pan, {
5486
+ toValue: cornersRef.current[best],
5487
+ useNativeDriver: false,
5488
+ friction: 8,
5489
+ tension: 70
5490
+ }).start(() => {
5491
+ dragged.current = false;
5492
+ });
5493
+ }
5494
+ }),
5495
+ [pan]
5496
+ );
5497
+ return /* @__PURE__ */ jsx(
5498
+ Animated.View,
5499
+ {
5500
+ ...responder.panHandlers,
5501
+ style: [styles28.pip, { transform: pan.getTranslateTransform() }],
5502
+ children: /* @__PURE__ */ jsx(
5503
+ Pressable,
5504
+ {
5505
+ onPress: () => {
5506
+ if (dragged.current) return;
5507
+ onPress?.();
5508
+ },
5509
+ accessibilityRole: "button",
5510
+ accessibilityLabel: "Your camera. Drag to move, tap to switch camera.",
5511
+ style: styles28.fill,
5512
+ children: /* @__PURE__ */ jsx(View, { style: styles28.fill, children: /* @__PURE__ */ jsx(
5513
+ CallParticipantTile,
5514
+ {
5515
+ participant,
5516
+ VideoTrack,
5517
+ label,
5518
+ avatarSize: 48,
5519
+ hideName: true,
5520
+ style: styles28.fill
5521
+ }
5522
+ ) })
5523
+ }
5524
+ )
5525
+ }
5526
+ );
5527
+ }
5528
+ var styles28 = StyleSheet.create({
5529
+ pip: {
5530
+ position: "absolute",
5531
+ top: 0,
5532
+ left: 0,
5533
+ width: PIP_WIDTH,
5534
+ height: PIP_HEIGHT,
5535
+ borderRadius: 16,
5536
+ overflow: "hidden",
5537
+ // Lifted off the video so it reads as floating rather than inset.
5538
+ shadowColor: "#000",
5539
+ shadowOpacity: 0.4,
5540
+ shadowRadius: 12,
5541
+ shadowOffset: { width: 0, height: 4 },
5542
+ elevation: 8
5543
+ },
5544
+ fill: { flex: 1 }
5545
+ });
5546
+ var GAP = 8;
5547
+ var MIN_TILE_HEIGHT = 132;
5548
+ function CallVideoGrid({
5549
+ participants,
5550
+ livekitReactNative,
5551
+ labelFor,
5552
+ onLocalPress,
5553
+ chromeInsets
5554
+ }) {
5555
+ const label = labelFor ?? ((id) => `User ${id.slice(0, 6)}`);
5556
+ const VideoTrack = livekitReactNative?.VideoTrack;
5557
+ const [size, setSize] = useState({ width: 0, height: 0 });
5558
+ const onLayout = (event) => {
5559
+ const { width: width2, height } = event.nativeEvent.layout;
5560
+ setSize(
5561
+ (current) => current.width === width2 && current.height === height ? current : { width: width2, height }
5562
+ );
5563
+ };
5564
+ const self = participants.find((p) => p.isSelf) ?? null;
5565
+ const others = useMemo(() => participants.filter((p) => !p.isSelf), [participants]);
5566
+ const columns = others.length + 1 <= 6 ? 2 : 3;
5567
+ const rows = Math.ceil((others.length + 1) / columns);
5568
+ const tileHeight = size.height > 0 ? (size.height - GAP * (rows - 1)) / rows : 0;
5569
+ const scrolls = tileHeight > 0 && tileHeight < MIN_TILE_HEIGHT;
5570
+ if (participants.length === 0) return /* @__PURE__ */ jsx(View, { style: styles29.stage, onLayout });
5571
+ if (others.length <= 1) {
5572
+ const spotlight = others[0] ?? self;
5573
+ if (!spotlight) return /* @__PURE__ */ jsx(View, { style: styles29.stage, onLayout });
5574
+ return /* @__PURE__ */ jsxs(View, { style: styles29.stage, onLayout, children: [
5575
+ /* @__PURE__ */ jsx(
5576
+ CallParticipantTile,
5577
+ {
5578
+ participant: spotlight,
5579
+ VideoTrack,
5580
+ label,
5581
+ avatarSize: Math.min(148, Math.max(96, Math.round(size.width * 0.36))),
5582
+ bleed: true,
5583
+ hideName: others.length === 1,
5584
+ style: styles29.fill
5585
+ }
5586
+ ),
5587
+ self && others.length === 1 && size.width > 0 && /* @__PURE__ */ jsx(
5588
+ LocalVideoPip,
5589
+ {
5590
+ participant: self,
5591
+ VideoTrack,
5592
+ label,
5593
+ bounds: size,
5594
+ insets: chromeInsets ?? { top: 0, bottom: 0 },
5595
+ onPress: onLocalPress
5596
+ }
5597
+ )
5598
+ ] });
5599
+ }
5600
+ const tiles = [...others, ...self ? [self] : []];
5601
+ const width = size.width > 0 ? (size.width - GAP * (columns - 1)) / columns : 0;
5602
+ const grid = /* @__PURE__ */ jsx(View, { style: [styles29.grid, scrolls ? styles29.gridScrolling : styles29.gridFilling], children: tiles.map((participant) => /* @__PURE__ */ jsx(
5603
+ CallParticipantTile,
5604
+ {
5605
+ participant,
5606
+ VideoTrack,
5607
+ label,
5608
+ avatarSize: columns === 2 ? 66 : 46,
5609
+ style: {
5610
+ width: width || void 0,
5611
+ height: scrolls ? MIN_TILE_HEIGHT : tileHeight || void 0
5612
+ }
5613
+ },
5614
+ participant.userId
5615
+ )) });
5616
+ return /* @__PURE__ */ jsx(View, { style: styles29.stage, onLayout, children: scrolls ? /* @__PURE__ */ jsx(
5617
+ ScrollView,
5618
+ {
5619
+ contentContainerStyle: [
5620
+ styles29.scroll,
5621
+ { paddingBottom: (chromeInsets?.bottom ?? 0) + GAP }
5622
+ ],
5623
+ showsVerticalScrollIndicator: false,
5624
+ children: grid
5625
+ }
5626
+ ) : grid });
5627
+ }
5628
+ var styles29 = StyleSheet.create({
5629
+ stage: { flex: 1, overflow: "hidden" },
5630
+ fill: { ...StyleSheet.absoluteFillObject },
5631
+ grid: { flexDirection: "row", flexWrap: "wrap", gap: GAP },
5632
+ gridFilling: { flex: 1, alignContent: "center" },
5633
+ gridScrolling: { alignContent: "flex-start" },
5634
+ scroll: { paddingVertical: GAP }
5635
+ });
5636
+
5637
+ // src/voice/livekit-rn.ts
5638
+ function describeRoute(id) {
5639
+ switch (id) {
5640
+ case "speaker":
5641
+ case "force_speaker":
5642
+ return { id, kind: "speaker", label: "Speaker" };
5643
+ case "earpiece":
5644
+ return { id, kind: "earpiece", label: "Phone" };
5645
+ case "headset":
5646
+ return { id, kind: "headset", label: "Headphones" };
5647
+ case "bluetooth":
5648
+ return { id, kind: "bluetooth", label: "Bluetooth" };
5649
+ case "default":
5650
+ return { id, kind: "auto", label: "iPhone" };
5651
+ default:
5652
+ return { id, kind: "auto", label: id };
5653
+ }
5654
+ }
5655
+
5656
+ // src/voice/useCallAudio.ts
5657
+ var IOS_AUTO = "default";
5658
+ var IOS_SPEAKER = "force_speaker";
5659
+ function useCallAudio({
5660
+ active,
5661
+ video,
5662
+ livekitReactNative
5663
+ }) {
5664
+ const session = livekitReactNative?.AudioSession;
5665
+ const ios = Platform.OS === "ios";
5666
+ const [routes, setRoutes] = useState([]);
5667
+ const [selectedRouteId, setSelectedRouteId] = useState(null);
5668
+ const preferSpeaker = useRef(video);
5669
+ if (!active) preferSpeaker.current = video;
5670
+ const userChose = useRef(false);
5671
+ if (!active) userChose.current = false;
5672
+ const refreshRoutes = useCallback(() => {
5673
+ if (!session) return;
5674
+ void session.getAudioOutputs().then((ids) => setRoutes(ids.map(describeRoute))).catch(() => {
5675
+ });
5676
+ }, [session]);
5677
+ useEffect(() => {
5678
+ if (!active || !session) return;
5679
+ const wantsSpeaker = preferSpeaker.current;
5680
+ let disposePolicy;
5681
+ let released = false;
5682
+ void (async () => {
5683
+ try {
5684
+ await session.configureAudio({
5685
+ android: {
5686
+ // Best device first. A headset or car kit outranks the
5687
+ // built-in outputs either way; the media type only decides
5688
+ // which built-in output wins when nothing is plugged in.
5689
+ preferredOutputList: wantsSpeaker ? ["bluetooth", "headset", "speaker", "earpiece"] : ["bluetooth", "headset", "earpiece", "speaker"]
5690
+ },
5691
+ ios: { defaultOutput: wantsSpeaker ? "speaker" : "earpiece" }
5692
+ });
5693
+ } catch {
5694
+ }
5695
+ if (released) return;
5696
+ disposePolicy = livekitReactNative?.setupIOSAudioManagement?.(wantsSpeaker);
5697
+ try {
5698
+ await session.startAudioSession();
5699
+ } catch {
5700
+ }
5701
+ if (released) {
5702
+ void session.stopAudioSession().catch(() => {
5703
+ });
5704
+ disposePolicy?.();
5705
+ return;
5706
+ }
5707
+ refreshRoutes();
5708
+ })();
5709
+ return () => {
5710
+ released = true;
5711
+ void session.stopAudioSession().catch(() => {
5712
+ });
5713
+ disposePolicy?.();
5714
+ };
5715
+ }, [active, session, livekitReactNative, refreshRoutes]);
5716
+ useEffect(() => {
5717
+ if (active) return;
5718
+ setSelectedRouteId(null);
5719
+ setRoutes([]);
5720
+ }, [active]);
5721
+ useEffect(() => {
5722
+ if (!active) return;
5723
+ const events = livekitReactNative?.audioDeviceModuleEvents;
5724
+ if (!events) return;
5725
+ const onChange = () => refreshRoutes();
5726
+ try {
5727
+ events.addDevicesUpdatedListener(onChange);
5728
+ } catch {
5729
+ return;
5730
+ }
5731
+ return () => {
5732
+ try {
5733
+ events.removeDevicesUpdatedListener(onChange);
5734
+ } catch {
5735
+ }
5736
+ };
5737
+ }, [active, livekitReactNative, refreshRoutes]);
5738
+ const selectRoute = useCallback(
5739
+ (id) => {
5740
+ userChose.current = true;
5741
+ setSelectedRouteId(id);
5742
+ if (!session) return;
5743
+ void session.selectAudioOutput(id).catch(() => {
5744
+ refreshRoutes();
5745
+ });
5746
+ },
5747
+ [session, refreshRoutes]
5748
+ );
5749
+ const externalDevice = useMemo(
5750
+ () => routes.some((r) => r.kind === "headset" || r.kind === "bluetooth"),
5751
+ [routes]
5752
+ );
5753
+ const speakerOn = selectedRouteId === null ? preferSpeaker.current && !externalDevice : ios ? selectedRouteId === IOS_SPEAKER : selectedRouteId === "speaker";
5754
+ const setSpeaker = useCallback(
5755
+ (on) => {
5756
+ if (ios) {
5757
+ selectRoute(on ? IOS_SPEAKER : IOS_AUTO);
5758
+ return;
5759
+ }
5760
+ if (on) {
5761
+ selectRoute("speaker");
5762
+ return;
5763
+ }
5764
+ const fallback = routes.find((r) => r.kind === "bluetooth") ?? routes.find((r) => r.kind === "headset") ?? routes.find((r) => r.kind === "earpiece");
5765
+ if (fallback) selectRoute(fallback.id);
5766
+ },
5767
+ [ios, routes, selectRoute]
5768
+ );
5769
+ const toggleSpeaker = useCallback(() => setSpeaker(!speakerOn), [setSpeaker, speakerOn]);
5770
+ const openSystemRoutePicker = useCallback(() => {
5771
+ void session?.showAudioRoutePicker?.().catch(() => {
5772
+ });
5773
+ }, [session]);
5774
+ return {
5775
+ speakerOn,
5776
+ toggleSpeaker,
5777
+ setSpeaker,
5778
+ routes,
5779
+ selectedRouteId,
5780
+ selectRoute,
5781
+ externalDevice,
5782
+ canPickSystemRoute: ios && typeof session?.showAudioRoutePicker === "function",
5783
+ openSystemRoutePicker
5784
+ };
5785
+ }
5786
+ function useCallDuration(connected) {
5787
+ const startedAt = useRef(null);
5788
+ const [, tick] = useState(0);
5789
+ if (connected && startedAt.current === null) startedAt.current = Date.now();
5790
+ if (!connected && startedAt.current !== null) startedAt.current = null;
5791
+ useEffect(() => {
5792
+ if (!connected) return;
5793
+ const id = setInterval(() => tick((n) => n + 1), 1e3);
5794
+ return () => clearInterval(id);
5795
+ }, [connected]);
5796
+ if (!connected || startedAt.current === null) return null;
5797
+ return format(Math.max(0, Math.floor((Date.now() - startedAt.current) / 1e3)));
5798
+ }
5799
+ function format(total) {
5800
+ const hours = Math.floor(total / 3600);
5801
+ const minutes = Math.floor(total % 3600 / 60);
5802
+ const seconds = total % 60;
5803
+ const mm = hours > 0 ? String(minutes).padStart(2, "0") : String(minutes);
5804
+ return `${hours > 0 ? `${hours}:` : ""}${mm}:${String(seconds).padStart(2, "0")}`;
5805
+ }
5806
+ var CHROME_IDLE_MS = 4500;
5159
5807
  function CallScreen({
5160
5808
  userId,
5161
5809
  labelFor,
@@ -5168,7 +5816,7 @@ function CallScreen({
5168
5816
  onVideoChange,
5169
5817
  nativeIncomingUi
5170
5818
  }) {
5171
- const theme = usePoolseTheme();
5819
+ const insets = useSafeAreaInsets();
5172
5820
  const ownCalls = useCalls(callsProp ? null : userId);
5173
5821
  const calls = callsProp ?? ownCalls;
5174
5822
  const meshAvailable = isNativeWebRtcAvailable(webrtc);
@@ -5177,19 +5825,21 @@ function CallScreen({
5177
5825
  [meshAvailable, webrtc]
5178
5826
  );
5179
5827
  const conversationId = calls.phase === "active" ? calls.activeConversationId : null;
5828
+ const isVideoCall = calls.media === "video";
5180
5829
  const sfu = useCallRoom(livekit ? conversationId : null, {
5181
5830
  livekit,
5182
- video: calls.media === "video"
5831
+ video: isVideoCall
5183
5832
  });
5184
5833
  const mesh = useVoiceRoom(conversationId, opts);
5185
5834
  const usingSfu = sfu.sfuAvailable === true;
5186
5835
  const status = usingSfu ? toVoiceStatus(sfu.status) : mesh.status;
5836
+ const connected = calls.phase === "active" && status === "connected";
5187
5837
  useEffect(() => {
5188
5838
  if (calls.phase !== "active" || !conversationId) return;
5189
5839
  let cancelled = false;
5190
5840
  void (async () => {
5191
- const connected2 = livekit ? await sfu.join() : false;
5192
- if (cancelled || connected2) return;
5841
+ const joined = livekit ? await sfu.join() : false;
5842
+ if (cancelled || joined) return;
5193
5843
  if (meshAvailable) await mesh.join();
5194
5844
  })();
5195
5845
  return () => {
@@ -5207,79 +5857,219 @@ function CallScreen({
5207
5857
  useEffect(() => {
5208
5858
  onVideoChange?.(videoOn);
5209
5859
  }, [videoOn, onVideoChange]);
5210
- const label = labelFor ?? ((id) => `User ${id.slice(0, 6)}`);
5211
- const ringingIn = calls.phase === "ringing-in";
5212
- const visible = calls.phase !== "idle" && !(nativeIncomingUi && ringingIn);
5860
+ const audio = useCallAudio({
5861
+ active: calls.phase !== "idle" && transport !== "mesh",
5862
+ video: isVideoCall,
5863
+ livekitReactNative
5864
+ });
5865
+ const [routesOpen, setRoutesOpen] = useState(false);
5866
+ const [chromeVisible, setChromeVisible] = useState(true);
5867
+ const [lastWake, setLastWake] = useState(0);
5868
+ const chromeHides = connected && isVideoCall;
5869
+ useEffect(() => {
5870
+ setChromeVisible(true);
5871
+ if (!chromeHides) return;
5872
+ const timer = setTimeout(() => setChromeVisible(false), CHROME_IDLE_MS);
5873
+ return () => clearTimeout(timer);
5874
+ }, [chromeHides, lastWake]);
5875
+ const wakeChrome = useCallback(() => setLastWake(Date.now()), []);
5876
+ const withWake = useCallback(
5877
+ (fn) => () => {
5878
+ wakeChrome();
5879
+ fn();
5880
+ },
5881
+ [wakeChrome]
5882
+ );
5213
5883
  const hangUp = useCallback(() => {
5214
5884
  sfu.leave();
5215
5885
  mesh.leave();
5216
5886
  void calls.hangUp();
5217
5887
  }, [sfu, mesh, calls]);
5888
+ const alone = usingSfu ? sfu.alone : mesh.alone;
5889
+ const endedOnEmpty = useRef(false);
5890
+ if (calls.phase !== "active") endedOnEmpty.current = false;
5891
+ useEffect(() => {
5892
+ if (!alone || calls.phase !== "active" || endedOnEmpty.current) return;
5893
+ endedOnEmpty.current = true;
5894
+ hangUp();
5895
+ }, [alone, calls.phase, hangUp]);
5218
5896
  const micOn = usingSfu ? sfu.micEnabled : !mesh.muted;
5219
5897
  const toggleMic = useCallback(() => {
5220
5898
  if (usingSfu) void sfu.toggleMic();
5221
5899
  else mesh.toggleMute();
5222
5900
  }, [usingSfu, sfu, mesh]);
5901
+ const onSpeakerPress = useCallback(() => {
5902
+ if (audio.routes.length > 2) setRoutesOpen(true);
5903
+ else if (audio.canPickSystemRoute && audio.externalDevice) audio.openSystemRoutePicker();
5904
+ else audio.toggleSpeaker();
5905
+ }, [audio]);
5906
+ const label = useMemo(() => labelFor ?? ((id) => `User ${id.slice(0, 6)}`), [labelFor]);
5907
+ const ringingIn = calls.phase === "ringing-in";
5908
+ const ringingOut = calls.phase === "ringing-out";
5909
+ const ended = calls.phase === "declined" || calls.phase === "busy" || calls.phase === "timed-out";
5910
+ const visible = calls.phase !== "idle" && !(nativeIncomingUi && ringingIn);
5911
+ const duration = useCallDuration(connected);
5912
+ const participants = usingSfu ? sfu.participants : [];
5913
+ const others = participants.filter((p) => !p.isSelf);
5914
+ const isGroup = others.length > 1 || (calls.outgoing?.calleeUserIds.length ?? 0) > 1;
5915
+ const peerIds = ringingIn && calls.incoming ? [calls.incoming.callerUserId] : others.length > 0 ? others.map((p) => p.userId) : calls.outgoing?.calleeUserIds ?? [];
5916
+ const title = peerIds.length === 0 ? "Call" : peerIds.length <= 2 ? peerIds.map(label).join(", ") : `${label(peerIds[0])} and ${peerIds.length - 1} others`;
5917
+ const subtitle = duration ?? statusLine(calls, status);
5918
+ const showStage = connected && usingSfu && participants.length > 0;
5223
5919
  const error = calls.error ?? (usingSfu ? sfu.error : mesh.error);
5224
- const connected = calls.phase === "active" && status === "connected";
5225
- return /* @__PURE__ */ jsx(Modal, { visible, animationType: "slide", transparent: false, onRequestClose: hangUp, children: /* @__PURE__ */ jsxs(View, { style: [styles25.screen, { backgroundColor: theme.colors.paper }], children: [
5226
- connected && usingSfu && sfu.participants.length > 0 ? /* @__PURE__ */ jsx(
5227
- CallVideoGrid,
5228
- {
5229
- participants: sfu.participants,
5230
- livekitReactNative,
5231
- labelFor
5232
- }
5233
- ) : /* @__PURE__ */ jsxs(View, { style: styles25.identity, children: [
5234
- /* @__PURE__ */ jsx(Text, { style: [styles25.who, { color: theme.colors.ink }], children: ringingIn && calls.incoming ? label(calls.incoming.callerUserId) : calls.outgoing ? calls.outgoing.calleeUserIds.map(label).join(", ") : "" }),
5235
- /* @__PURE__ */ jsx(Text, { style: [styles25.state, { color: theme.colors.ink3 }], children: statusLine(calls, status) })
5236
- ] }),
5237
- /* @__PURE__ */ jsx(View, { style: styles25.actions, children: ringingIn ? /* @__PURE__ */ jsxs(Fragment, { children: [
5238
- /* @__PURE__ */ jsx(
5239
- CallButton,
5240
- {
5241
- label: "Decline",
5242
- color: theme.colors.error,
5243
- onPress: () => void calls.decline()
5244
- }
5245
- ),
5246
- /* @__PURE__ */ jsx(
5247
- CallButton,
5248
- {
5249
- label: "Accept",
5250
- color: theme.colors.brand,
5251
- onPress: () => void calls.accept()
5252
- }
5253
- )
5254
- ] }) : calls.phase === "ringing-out" ? /* @__PURE__ */ jsx(
5255
- CallButton,
5256
- {
5257
- label: "Cancel",
5258
- color: theme.colors.error,
5259
- onPress: () => void calls.cancel()
5260
- }
5261
- ) : calls.phase === "declined" || calls.phase === "busy" || calls.phase === "timed-out" ? /* @__PURE__ */ jsx(CallButton, { label: "Close", color: theme.colors.ink3, onPress: calls.reset }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5262
- /* @__PURE__ */ jsx(
5263
- CallButton,
5264
- {
5265
- label: micOn ? "Mute" : "Unmute",
5266
- color: micOn ? theme.colors.ink2 : theme.colors.brand,
5267
- onPress: toggleMic
5268
- }
5269
- ),
5270
- usingSfu && /* @__PURE__ */ jsx(
5271
- CallButton,
5272
- {
5273
- label: videoOn ? "Camera off" : "Camera on",
5274
- color: videoOn ? theme.colors.brand : theme.colors.ink2,
5275
- onPress: () => void sfu.toggleCamera()
5276
- }
5277
- ),
5278
- /* @__PURE__ */ jsx(CallButton, { label: "Hang up", color: theme.colors.error, onPress: hangUp })
5279
- ] }) }),
5280
- !meshAvailable && !livekit && /* @__PURE__ */ jsx(Text, { style: [styles25.warn, { color: theme.colors.error }], children: "No WebRTC module supplied \u2014 this call has no audio." }),
5281
- error && /* @__PURE__ */ jsx(Text, { style: [styles25.warn, { color: theme.colors.error }], children: error.message })
5282
- ] }) });
5920
+ const chromeInsets = {
5921
+ top: insets.top + 72,
5922
+ bottom: insets.bottom + 150
5923
+ };
5924
+ return /* @__PURE__ */ jsxs(
5925
+ Modal,
5926
+ {
5927
+ visible,
5928
+ animationType: "slide",
5929
+ transparent: false,
5930
+ supportedOrientations: ["portrait", "landscape"],
5931
+ onRequestClose: hangUp,
5932
+ children: [
5933
+ /* @__PURE__ */ jsx(StatusBar, { barStyle: "light-content" }),
5934
+ /* @__PURE__ */ jsxs(Pressable, { style: styles30.screen, onPress: wakeChrome, accessible: false, children: [
5935
+ showStage ? /* @__PURE__ */ jsx(
5936
+ CallVideoGrid,
5937
+ {
5938
+ participants,
5939
+ livekitReactNative,
5940
+ labelFor: label,
5941
+ onLocalPress: () => void sfu.switchCamera(),
5942
+ chromeInsets
5943
+ }
5944
+ ) : /* @__PURE__ */ jsx(View, { style: styles30.identity, children: /* @__PURE__ */ jsx(
5945
+ CallAvatar,
5946
+ {
5947
+ name: peerIds.length === 1 ? label(peerIds[0]) : title,
5948
+ seed: peerIds[0] ?? "call",
5949
+ size: 132
5950
+ }
5951
+ ) }),
5952
+ chromeVisible && /* @__PURE__ */ jsxs(View, { style: [styles30.header, { paddingTop: insets.top + 14 }], pointerEvents: "box-none", children: [
5953
+ /* @__PURE__ */ jsx(Text, { numberOfLines: 1, style: styles30.title, children: title }),
5954
+ /* @__PURE__ */ jsxs(View, { style: styles30.subtitleRow, children: [
5955
+ isGroup && showStage && /* @__PURE__ */ jsx(PoolseIcon, { name: "users", size: 14, color: callColors.textDim, label: null }),
5956
+ /* @__PURE__ */ jsx(Text, { style: styles30.subtitle, children: isGroup && showStage ? `${participants.length} \xB7 ${subtitle}` : subtitle })
5957
+ ] })
5958
+ ] }),
5959
+ chromeVisible && /* @__PURE__ */ jsxs(
5960
+ View,
5961
+ {
5962
+ style: [styles30.controls, { paddingBottom: insets.bottom + 22 }],
5963
+ pointerEvents: "box-none",
5964
+ children: [
5965
+ error && /* @__PURE__ */ jsx(Text, { style: styles30.error, children: error.message }),
5966
+ !meshAvailable && !livekit && /* @__PURE__ */ jsx(Text, { style: styles30.error, children: "No WebRTC module supplied \u2014 this call has no audio." }),
5967
+ ringingIn ? /* @__PURE__ */ jsxs(View, { style: styles30.row, children: [
5968
+ /* @__PURE__ */ jsx(
5969
+ CallControlButton,
5970
+ {
5971
+ icon: "phone-off",
5972
+ tone: "danger",
5973
+ large: true,
5974
+ onPress: () => void calls.decline(),
5975
+ accessibilityLabel: "Decline call"
5976
+ }
5977
+ ),
5978
+ /* @__PURE__ */ jsx(
5979
+ CallControlButton,
5980
+ {
5981
+ icon: isVideoCall ? "video" : "phone",
5982
+ tone: "accept",
5983
+ large: true,
5984
+ onPress: () => void calls.accept(),
5985
+ accessibilityLabel: "Accept call"
5986
+ }
5987
+ )
5988
+ ] }) : ended ? /* @__PURE__ */ jsx(View, { style: styles30.row, children: /* @__PURE__ */ jsx(
5989
+ CallControlButton,
5990
+ {
5991
+ icon: "close",
5992
+ onPress: calls.reset,
5993
+ label: "Close",
5994
+ accessibilityLabel: "Close"
5995
+ }
5996
+ ) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5997
+ /* @__PURE__ */ jsxs(View, { style: styles30.row, children: [
5998
+ /* @__PURE__ */ jsx(
5999
+ CallControlButton,
6000
+ {
6001
+ icon: micOn ? "mic" : "mic-off",
6002
+ label: micOn ? "Mute" : "Unmute",
6003
+ active: !micOn,
6004
+ onPress: withWake(toggleMic),
6005
+ accessibilityLabel: micOn ? "Mute microphone" : "Unmute microphone"
6006
+ }
6007
+ ),
6008
+ /* @__PURE__ */ jsx(
6009
+ CallControlButton,
6010
+ {
6011
+ icon: speakerIcon(audio),
6012
+ label: speakerLabel(audio),
6013
+ active: audio.speakerOn,
6014
+ onPress: withWake(onSpeakerPress),
6015
+ accessibilityLabel: "Audio output"
6016
+ }
6017
+ ),
6018
+ usingSfu && /* @__PURE__ */ jsx(
6019
+ CallControlButton,
6020
+ {
6021
+ icon: videoOn ? "video" : "video-off",
6022
+ label: "Camera",
6023
+ active: !videoOn,
6024
+ onPress: withWake(() => void sfu.toggleCamera()),
6025
+ accessibilityLabel: videoOn ? "Turn camera off" : "Turn camera on"
6026
+ }
6027
+ ),
6028
+ usingSfu && videoOn && /* @__PURE__ */ jsx(
6029
+ CallControlButton,
6030
+ {
6031
+ icon: "camera-flip",
6032
+ label: "Flip",
6033
+ onPress: withWake(() => void sfu.switchCamera()),
6034
+ accessibilityLabel: "Switch camera"
6035
+ }
6036
+ )
6037
+ ] }),
6038
+ /* @__PURE__ */ jsx(View, { style: styles30.hangUpRow, children: /* @__PURE__ */ jsx(
6039
+ CallControlButton,
6040
+ {
6041
+ icon: "phone-off",
6042
+ tone: "danger",
6043
+ large: true,
6044
+ onPress: ringingOut ? () => void calls.cancel() : hangUp,
6045
+ accessibilityLabel: ringingOut ? "Cancel call" : "Hang up"
6046
+ }
6047
+ ) })
6048
+ ] })
6049
+ ]
6050
+ }
6051
+ ),
6052
+ /* @__PURE__ */ jsx(
6053
+ AudioRouteSheet,
6054
+ {
6055
+ visible: routesOpen,
6056
+ routes: audio.routes,
6057
+ selectedRouteId: audio.selectedRouteId,
6058
+ onSelect: audio.selectRoute,
6059
+ onClose: () => setRoutesOpen(false)
6060
+ }
6061
+ )
6062
+ ] })
6063
+ ]
6064
+ }
6065
+ );
6066
+ }
6067
+ function speakerIcon(audio) {
6068
+ if (audio.externalDevice) return "headphones";
6069
+ return audio.speakerOn ? "speaker" : "speaker-off";
6070
+ }
6071
+ function speakerLabel(audio) {
6072
+ return audio.externalDevice ? "Audio" : "Speaker";
5283
6073
  }
5284
6074
  function toVoiceStatus(status) {
5285
6075
  switch (status) {
@@ -5296,7 +6086,7 @@ function toVoiceStatus(status) {
5296
6086
  function statusLine(calls, mediaStatus) {
5297
6087
  switch (calls.phase) {
5298
6088
  case "ringing-in":
5299
- return calls.media === "video" ? "Incoming video call\u2026" : "Incoming call\u2026";
6089
+ return calls.media === "video" ? "Incoming video call" : "Incoming call";
5300
6090
  case "ringing-out":
5301
6091
  return "Ringing\u2026";
5302
6092
  case "declined":
@@ -5311,36 +6101,41 @@ function statusLine(calls, mediaStatus) {
5311
6101
  return "";
5312
6102
  }
5313
6103
  }
5314
- function CallButton({
5315
- label,
5316
- color,
5317
- onPress
5318
- }) {
5319
- return /* @__PURE__ */ jsx(
5320
- Pressable,
5321
- {
5322
- onPress,
5323
- accessibilityRole: "button",
5324
- style: [styles25.callBtn, { backgroundColor: color }],
5325
- children: /* @__PURE__ */ jsx(Text, { style: styles25.callBtnText, children: label })
5326
- }
5327
- );
5328
- }
5329
- var styles25 = StyleSheet.create({
5330
- screen: { flex: 1, justifyContent: "space-between", paddingVertical: 72, paddingHorizontal: 24 },
5331
- identity: { alignItems: "center", gap: 8, marginTop: 48 },
5332
- who: { fontSize: 28, fontWeight: "600", textAlign: "center" },
5333
- state: { fontSize: 15 },
5334
- actions: { flexDirection: "row", justifyContent: "center", gap: 12, flexWrap: "wrap" },
5335
- callBtn: {
5336
- paddingHorizontal: 22,
5337
- paddingVertical: 16,
5338
- borderRadius: 999,
5339
- minWidth: 108,
5340
- alignItems: "center"
5341
- },
5342
- callBtnText: { color: "#ffffff", fontSize: 16, fontWeight: "600" },
5343
- warn: { fontSize: 12, textAlign: "center", marginTop: 12 }
6104
+ var styles30 = StyleSheet.create({
6105
+ screen: { flex: 1, backgroundColor: callColors.bg },
6106
+ identity: { flex: 1, alignItems: "center", justifyContent: "center" },
6107
+ header: {
6108
+ position: "absolute",
6109
+ top: 0,
6110
+ left: 0,
6111
+ right: 0,
6112
+ alignItems: "center",
6113
+ gap: 5,
6114
+ paddingHorizontal: 24,
6115
+ paddingBottom: 18,
6116
+ backgroundColor: callColors.scrim
6117
+ },
6118
+ title: { color: callColors.text, fontSize: 22, fontWeight: "600", textAlign: "center" },
6119
+ subtitleRow: { flexDirection: "row", alignItems: "center", gap: 6 },
6120
+ subtitle: { color: callColors.textDim, fontSize: 14.5 },
6121
+ controls: {
6122
+ position: "absolute",
6123
+ left: 0,
6124
+ right: 0,
6125
+ bottom: 0,
6126
+ alignItems: "center",
6127
+ gap: 18,
6128
+ paddingTop: 20,
6129
+ backgroundColor: callColors.scrim
6130
+ },
6131
+ row: { flexDirection: "row", justifyContent: "center", flexWrap: "wrap", rowGap: 16 },
6132
+ hangUpRow: { flexDirection: "row", justifyContent: "center" },
6133
+ error: {
6134
+ color: callColors.danger,
6135
+ fontSize: 12.5,
6136
+ textAlign: "center",
6137
+ paddingHorizontal: 24
6138
+ }
5344
6139
  });
5345
6140
  function isCallOver(phase) {
5346
6141
  if (phase === void 0) return false;
@@ -5520,6 +6315,6 @@ function guessApnsEnvironment() {
5520
6315
  return __DEV__ ? "sandbox" : "production";
5521
6316
  }
5522
6317
 
5523
- export { AttachmentPicker, AttachmentPreview, Avatar, CallScreen, CallVideoGrid, ChatHeader, ConversationList, ConversationView, DaySeparator, GroupDetailsSheet, ImageMosaic, MemberList, MentionInput, MessageActions, MessageBubble, MessageComposer, MessageList, MessageRow, PoolseIcon, PoolseInbox, PoolseLogo, PoolseTheme, primitives as Primitives, ReactionPicker, ReactionStrip, ThreadView, TypingIndicator, UploadQueueStrip, UserName, VoiceRoomBar, createNativeWebRtcAdapter, defaultDarkTheme, defaultLightTheme, isNativeWebRtcAvailable, isStalePush, useAppStateLifecycle, useDisplayName, usePoolseTheme, useVoipCalls };
6318
+ export { AttachmentPicker, AttachmentPreview, AudioRouteSheet, Avatar, CallAvatar, CallControlButton, CallParticipantTile, CallScreen, CallVideoGrid, ChatHeader, ConversationList, ConversationView, DaySeparator, GroupDetailsSheet, ImageMosaic, LocalVideoPip, MemberList, MentionInput, MessageActions, MessageBubble, MessageComposer, MessageList, MessageRow, PoolseIcon, PoolseInbox, PoolseLogo, PoolseTheme, primitives as Primitives, ReactionPicker, ReactionStrip, ThreadView, TypingIndicator, UploadQueueStrip, UserName, VoiceRoomBar, callColors, createNativeWebRtcAdapter, defaultDarkTheme, defaultLightTheme, describeRoute, isNativeWebRtcAvailable, isStalePush, useAppStateLifecycle, useCallAudio, useCallDuration, useDisplayName, usePoolseTheme, useVoipCalls };
5524
6319
  //# sourceMappingURL=index.js.map
5525
6320
  //# sourceMappingURL=index.js.map