@poolse/react-native 2.1.6 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,7 +1,8 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ReactElement } from 'react';
3
- import { Conversation, Uuid, Message, AttachmentUploadInput, Membership, Attachment } from '@poolse/sdk';
3
+ import { Conversation, Uuid, Message, AttachmentUploadInput, Membership, Attachment, WebRtcAdapter } from '@poolse/sdk';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
+ import { UseCalls } from '@poolse/react';
5
6
 
6
7
  interface InboxUser {
7
8
  /** The user's external_id (the same id you mint JWTs with). */
@@ -976,4 +977,83 @@ declare const defaultDarkTheme: PoolseRNTheme;
976
977
  */
977
978
  declare function useAppStateLifecycle(): void;
978
979
 
979
- export { AttachmentPicker, type AttachmentPickerProps, AttachmentPreview, type AttachmentPreviewProps, Avatar, type AvatarProps, ChatHeader, type ChatHeaderProps, ConversationList, type ConversationListProps, ConversationView, type ConversationViewProps, DaySeparator, type DaySeparatorProps, GroupDetailsSheet, type GroupDetailsSheetProps, type IconName, ImageMosaic, type ImageMosaicProps, type InboxUser, MemberList, type MemberListProps, MentionInput, type MentionInputProps, MessageActions, type MessageActionsProps, MessageBubble, type MessageBubbleProps, MessageComposer, type MessageComposerProps, MessageList, type MessageListItem, type MessageListProps, MessageRow, type MessageRowProps, PoolseIcon, type PoolseIconProps, PoolseInbox, type PoolseInboxHandle, type PoolseInboxProps, PoolseLogo, type PoolseLogoProps, type PoolseLogoVariant, type PoolseRNTheme, type PoolseRNThemeOverrides, PoolseTheme, type PoolseThemeProps, primitives as Primitives, ReactionPicker, type ReactionPickerProps, ReactionStrip, type ReactionStripProps, type ThemeColors, type ThemeRadii, type ThemeShadows, type ThemeSpacing, type ThemeType, ThreadView, type ThreadViewProps, TypingIndicator, type TypingIndicatorProps, UploadQueueStrip, UserName, type UserNameProps, defaultDarkTheme, defaultLightTheme, useAppStateLifecycle, useDisplayName, usePoolseTheme };
980
+ interface VoiceRoomBarProps {
981
+ conversationId: string;
982
+ /** Resolves a user id to a display label. Defaults to a short id. */
983
+ labelFor?: (userId: string) => string;
984
+ /**
985
+ * Rendered instead of the join button when `react-native-webrtc`
986
+ * isn't installed. Defaults to an inline explanation.
987
+ */
988
+ unavailableSlot?: React.ReactNode;
989
+ }
990
+ /**
991
+ * Discord-style voice bar: one "Join Voice" button that expands into a
992
+ * roster of who's in, with a ring around whoever is talking.
993
+ *
994
+ * Drop it above a `<ConversationView>` to give a conversation an
995
+ * always-on voice channel. For ringing a specific person, use
996
+ * `<IncomingCallSheet>` together with `useCalls`.
997
+ */
998
+ declare function VoiceRoomBar({ conversationId, labelFor, unavailableSlot }: VoiceRoomBarProps): react_jsx_runtime.JSX.Element;
999
+
1000
+ interface CallScreenProps {
1001
+ /** The signed-in user's id — the topic calls are delivered on. */
1002
+ userId: string;
1003
+ /** Resolves a user id to a display label. Defaults to a short id. */
1004
+ labelFor?: (userId: string) => string;
1005
+ /**
1006
+ * Escape hatch for apps that already own a `useCalls` instance (for
1007
+ * example to place calls from elsewhere in the UI). When omitted the
1008
+ * component manages its own.
1009
+ */
1010
+ calls?: UseCalls;
1011
+ }
1012
+ /**
1013
+ * WhatsApp-style call UI: a full-screen sheet that covers the app while
1014
+ * a call is ringing or connected, and renders nothing when idle.
1015
+ *
1016
+ * Mount it once, high in the tree, so a call can arrive on any screen:
1017
+ *
1018
+ * <PoolseProvider config={config}>
1019
+ * <CallScreen userId={me.id} />
1020
+ * <YourApp />
1021
+ * </PoolseProvider>
1022
+ *
1023
+ * Placing a call is separate — `useCalls().call(conversationId)`, wired
1024
+ * to whatever button your UI wants. Pass the same instance in via
1025
+ * `calls` so both halves share one state machine.
1026
+ */
1027
+ declare function CallScreen({ userId, labelFor, calls: external }: CallScreenProps): react_jsx_runtime.JSX.Element;
1028
+
1029
+ /**
1030
+ * React Native WebRTC binding, backed by `react-native-webrtc`.
1031
+ *
1032
+ * That package ships native code, so it can't run in Expo Go and not
1033
+ * every app wants it. It's therefore an OPTIONAL peer dependency,
1034
+ * resolved with a guarded `require` at call time rather than a static
1035
+ * import: an app that never places a call doesn't need it installed,
1036
+ * and one that does gets a clear message instead of a redbox at import.
1037
+ *
1038
+ * Media streams on native aren't attached to an element the way they
1039
+ * are on the web — audio-only calls route to the device's audio session
1040
+ * automatically once the peer connection is live, so the sink is a
1041
+ * no-op. Video would render the stream through `<RTCView>` using the
1042
+ * `toURL()` the stream exposes.
1043
+ */
1044
+
1045
+ /**
1046
+ * Whether this app can actually carry call audio. Use it to disable a
1047
+ * call button rather than letting the user tap into a failure.
1048
+ */
1049
+ declare function isNativeWebRtcAvailable(): boolean;
1050
+ /**
1051
+ * Adapter for {@link useVoiceRoom}'s `webrtc` option.
1052
+ *
1053
+ * @throws when `react-native-webrtc` isn't installed — check
1054
+ * {@link isNativeWebRtcAvailable} first if that's reachable in
1055
+ * your UI.
1056
+ */
1057
+ declare function createNativeWebRtcAdapter(): WebRtcAdapter;
1058
+
1059
+ export { AttachmentPicker, type AttachmentPickerProps, AttachmentPreview, type AttachmentPreviewProps, Avatar, type AvatarProps, CallScreen, type CallScreenProps, ChatHeader, type ChatHeaderProps, ConversationList, type ConversationListProps, ConversationView, type ConversationViewProps, DaySeparator, type DaySeparatorProps, GroupDetailsSheet, type GroupDetailsSheetProps, type IconName, ImageMosaic, type ImageMosaicProps, type InboxUser, MemberList, type MemberListProps, MentionInput, type MentionInputProps, MessageActions, type MessageActionsProps, MessageBubble, type MessageBubbleProps, MessageComposer, type MessageComposerProps, MessageList, type MessageListItem, type MessageListProps, MessageRow, type MessageRowProps, PoolseIcon, type PoolseIconProps, PoolseInbox, type PoolseInboxHandle, type PoolseInboxProps, PoolseLogo, type PoolseLogoProps, type PoolseLogoVariant, type PoolseRNTheme, type PoolseRNThemeOverrides, PoolseTheme, type PoolseThemeProps, primitives as Primitives, ReactionPicker, type ReactionPickerProps, ReactionStrip, type ReactionStripProps, type ThemeColors, type ThemeRadii, type ThemeShadows, type ThemeSpacing, type ThemeType, ThreadView, type ThreadViewProps, TypingIndicator, type TypingIndicatorProps, UploadQueueStrip, UserName, type UserNameProps, VoiceRoomBar, type VoiceRoomBarProps, createNativeWebRtcAdapter, defaultDarkTheme, defaultLightTheme, isNativeWebRtcAvailable, useAppStateLifecycle, useDisplayName, usePoolseTheme };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ReactElement } from 'react';
3
- import { Conversation, Uuid, Message, AttachmentUploadInput, Membership, Attachment } from '@poolse/sdk';
3
+ import { Conversation, Uuid, Message, AttachmentUploadInput, Membership, Attachment, WebRtcAdapter } from '@poolse/sdk';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
+ import { UseCalls } from '@poolse/react';
5
6
 
6
7
  interface InboxUser {
7
8
  /** The user's external_id (the same id you mint JWTs with). */
@@ -976,4 +977,83 @@ declare const defaultDarkTheme: PoolseRNTheme;
976
977
  */
977
978
  declare function useAppStateLifecycle(): void;
978
979
 
979
- export { AttachmentPicker, type AttachmentPickerProps, AttachmentPreview, type AttachmentPreviewProps, Avatar, type AvatarProps, ChatHeader, type ChatHeaderProps, ConversationList, type ConversationListProps, ConversationView, type ConversationViewProps, DaySeparator, type DaySeparatorProps, GroupDetailsSheet, type GroupDetailsSheetProps, type IconName, ImageMosaic, type ImageMosaicProps, type InboxUser, MemberList, type MemberListProps, MentionInput, type MentionInputProps, MessageActions, type MessageActionsProps, MessageBubble, type MessageBubbleProps, MessageComposer, type MessageComposerProps, MessageList, type MessageListItem, type MessageListProps, MessageRow, type MessageRowProps, PoolseIcon, type PoolseIconProps, PoolseInbox, type PoolseInboxHandle, type PoolseInboxProps, PoolseLogo, type PoolseLogoProps, type PoolseLogoVariant, type PoolseRNTheme, type PoolseRNThemeOverrides, PoolseTheme, type PoolseThemeProps, primitives as Primitives, ReactionPicker, type ReactionPickerProps, ReactionStrip, type ReactionStripProps, type ThemeColors, type ThemeRadii, type ThemeShadows, type ThemeSpacing, type ThemeType, ThreadView, type ThreadViewProps, TypingIndicator, type TypingIndicatorProps, UploadQueueStrip, UserName, type UserNameProps, defaultDarkTheme, defaultLightTheme, useAppStateLifecycle, useDisplayName, usePoolseTheme };
980
+ interface VoiceRoomBarProps {
981
+ conversationId: string;
982
+ /** Resolves a user id to a display label. Defaults to a short id. */
983
+ labelFor?: (userId: string) => string;
984
+ /**
985
+ * Rendered instead of the join button when `react-native-webrtc`
986
+ * isn't installed. Defaults to an inline explanation.
987
+ */
988
+ unavailableSlot?: React.ReactNode;
989
+ }
990
+ /**
991
+ * Discord-style voice bar: one "Join Voice" button that expands into a
992
+ * roster of who's in, with a ring around whoever is talking.
993
+ *
994
+ * Drop it above a `<ConversationView>` to give a conversation an
995
+ * always-on voice channel. For ringing a specific person, use
996
+ * `<IncomingCallSheet>` together with `useCalls`.
997
+ */
998
+ declare function VoiceRoomBar({ conversationId, labelFor, unavailableSlot }: VoiceRoomBarProps): react_jsx_runtime.JSX.Element;
999
+
1000
+ interface CallScreenProps {
1001
+ /** The signed-in user's id — the topic calls are delivered on. */
1002
+ userId: string;
1003
+ /** Resolves a user id to a display label. Defaults to a short id. */
1004
+ labelFor?: (userId: string) => string;
1005
+ /**
1006
+ * Escape hatch for apps that already own a `useCalls` instance (for
1007
+ * example to place calls from elsewhere in the UI). When omitted the
1008
+ * component manages its own.
1009
+ */
1010
+ calls?: UseCalls;
1011
+ }
1012
+ /**
1013
+ * WhatsApp-style call UI: a full-screen sheet that covers the app while
1014
+ * a call is ringing or connected, and renders nothing when idle.
1015
+ *
1016
+ * Mount it once, high in the tree, so a call can arrive on any screen:
1017
+ *
1018
+ * <PoolseProvider config={config}>
1019
+ * <CallScreen userId={me.id} />
1020
+ * <YourApp />
1021
+ * </PoolseProvider>
1022
+ *
1023
+ * Placing a call is separate — `useCalls().call(conversationId)`, wired
1024
+ * to whatever button your UI wants. Pass the same instance in via
1025
+ * `calls` so both halves share one state machine.
1026
+ */
1027
+ declare function CallScreen({ userId, labelFor, calls: external }: CallScreenProps): react_jsx_runtime.JSX.Element;
1028
+
1029
+ /**
1030
+ * React Native WebRTC binding, backed by `react-native-webrtc`.
1031
+ *
1032
+ * That package ships native code, so it can't run in Expo Go and not
1033
+ * every app wants it. It's therefore an OPTIONAL peer dependency,
1034
+ * resolved with a guarded `require` at call time rather than a static
1035
+ * import: an app that never places a call doesn't need it installed,
1036
+ * and one that does gets a clear message instead of a redbox at import.
1037
+ *
1038
+ * Media streams on native aren't attached to an element the way they
1039
+ * are on the web — audio-only calls route to the device's audio session
1040
+ * automatically once the peer connection is live, so the sink is a
1041
+ * no-op. Video would render the stream through `<RTCView>` using the
1042
+ * `toURL()` the stream exposes.
1043
+ */
1044
+
1045
+ /**
1046
+ * Whether this app can actually carry call audio. Use it to disable a
1047
+ * call button rather than letting the user tap into a failure.
1048
+ */
1049
+ declare function isNativeWebRtcAvailable(): boolean;
1050
+ /**
1051
+ * Adapter for {@link useVoiceRoom}'s `webrtc` option.
1052
+ *
1053
+ * @throws when `react-native-webrtc` isn't installed — check
1054
+ * {@link isNativeWebRtcAvailable} first if that's reachable in
1055
+ * your UI.
1056
+ */
1057
+ declare function createNativeWebRtcAdapter(): WebRtcAdapter;
1058
+
1059
+ export { AttachmentPicker, type AttachmentPickerProps, AttachmentPreview, type AttachmentPreviewProps, Avatar, type AvatarProps, CallScreen, type CallScreenProps, ChatHeader, type ChatHeaderProps, ConversationList, type ConversationListProps, ConversationView, type ConversationViewProps, DaySeparator, type DaySeparatorProps, GroupDetailsSheet, type GroupDetailsSheetProps, type IconName, ImageMosaic, type ImageMosaicProps, type InboxUser, MemberList, type MemberListProps, MentionInput, type MentionInputProps, MessageActions, type MessageActionsProps, MessageBubble, type MessageBubbleProps, MessageComposer, type MessageComposerProps, MessageList, type MessageListItem, type MessageListProps, MessageRow, type MessageRowProps, PoolseIcon, type PoolseIconProps, PoolseInbox, type PoolseInboxHandle, type PoolseInboxProps, PoolseLogo, type PoolseLogoProps, type PoolseLogoVariant, type PoolseRNTheme, type PoolseRNThemeOverrides, PoolseTheme, type PoolseThemeProps, primitives as Primitives, ReactionPicker, type ReactionPickerProps, ReactionStrip, type ReactionStripProps, type ThemeColors, type ThemeRadii, type ThemeShadows, type ThemeSpacing, type ThemeType, ThreadView, type ThreadViewProps, TypingIndicator, type TypingIndicatorProps, UploadQueueStrip, UserName, type UserNameProps, VoiceRoomBar, type VoiceRoomBarProps, createNativeWebRtcAdapter, defaultDarkTheme, defaultLightTheme, isNativeWebRtcAvailable, useAppStateLifecycle, useDisplayName, usePoolseTheme };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { useUser, useMe, useConversations, useAttachmentUpload, useConversation, useMembers, usePresence, useMessages, useTyping, useRealtimeStatus, computeGroupPosition, formatDayLabel, useReactions, sameDay, useThread, useAttachmentUrl, usePoolse } from '@poolse/react';
1
+ import { useUser, useMe, useConversations, useAttachmentUpload, useConversation, useMembers, usePresence, useMessages, useTyping, useRealtimeStatus, computeGroupPosition, formatDayLabel, useReactions, sameDay, useThread, useAttachmentUrl, usePoolse, useVoiceRoom, useCalls } from '@poolse/react';
2
2
  import { createContext, forwardRef, useRef, useState, useEffect, useImperativeHandle, useMemo, useCallback, useContext, createElement } from 'react';
3
3
  import { StyleSheet, Platform, Keyboard, View, Pressable, TextInput, Text, useWindowDimensions, Animated, Easing, PanResponder, Image, ScrollView, ActivityIndicator, FlatList, AppState, KeyboardAvoidingView, Modal, Dimensions, Linking } from 'react-native';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -4914,6 +4914,292 @@ function useAppStateLifecycle() {
4914
4914
  }, [poolse]);
4915
4915
  }
4916
4916
 
4917
- export { AttachmentPicker, AttachmentPreview, Avatar, ChatHeader, ConversationList, ConversationView, DaySeparator, GroupDetailsSheet, ImageMosaic, MemberList, MentionInput, MessageActions, MessageBubble, MessageComposer, MessageList, MessageRow, PoolseIcon, PoolseInbox, PoolseLogo, PoolseTheme, primitives as Primitives, ReactionPicker, ReactionStrip, ThreadView, TypingIndicator, UploadQueueStrip, UserName, defaultDarkTheme, defaultLightTheme, useAppStateLifecycle, useDisplayName, usePoolseTheme };
4917
+ // src/voice/webrtc-native.ts
4918
+ var cached;
4919
+ function loadNativeWebRtc() {
4920
+ if (cached !== void 0) return cached;
4921
+ try {
4922
+ cached = __require("react-native-webrtc");
4923
+ } catch {
4924
+ cached = null;
4925
+ }
4926
+ return cached;
4927
+ }
4928
+ function isNativeWebRtcAvailable() {
4929
+ return loadNativeWebRtc() != null;
4930
+ }
4931
+ function createNativeWebRtcAdapter() {
4932
+ const native = loadNativeWebRtc();
4933
+ if (!native) {
4934
+ throw new Error(
4935
+ "react-native-webrtc is not installed. Add it to enable poolse voice calls:\n npx expo install react-native-webrtc\nIt contains native code, so it needs a development build \u2014 it will not run in Expo Go."
4936
+ );
4937
+ }
4938
+ return {
4939
+ createPeerConnection(iceServers) {
4940
+ return new native.RTCPeerConnection({ iceServers });
4941
+ },
4942
+ getUserMedia() {
4943
+ return native.mediaDevices.getUserMedia({
4944
+ audio: { echoCancellation: true, noiseSuppression: true },
4945
+ video: false
4946
+ });
4947
+ },
4948
+ playRemoteStream() {
4949
+ return () => {
4950
+ };
4951
+ }
4952
+ };
4953
+ }
4954
+ function VoiceRoomBar({ conversationId, labelFor, unavailableSlot }) {
4955
+ const theme = usePoolseTheme();
4956
+ const available = isNativeWebRtcAvailable();
4957
+ const opts = useMemo(
4958
+ () => available ? { webrtc: createNativeWebRtcAdapter() } : {},
4959
+ [available]
4960
+ );
4961
+ const { status, participants, muted, join, leave, toggleMute, error } = useVoiceRoom(
4962
+ available ? conversationId : null,
4963
+ opts
4964
+ );
4965
+ const label = labelFor ?? ((id) => `User ${id.slice(0, 6)}`);
4966
+ const connected = status === "connected";
4967
+ if (!available) {
4968
+ return /* @__PURE__ */ jsx(
4969
+ View,
4970
+ {
4971
+ style: [
4972
+ styles23.bar,
4973
+ { backgroundColor: theme.colors.surface, borderBottomColor: theme.colors.border }
4974
+ ],
4975
+ children: unavailableSlot ?? /* @__PURE__ */ jsx(Text, { style: [styles23.hint, { color: theme.colors.ink3 }], children: "Voice needs react-native-webrtc and a development build." })
4976
+ }
4977
+ );
4978
+ }
4979
+ return /* @__PURE__ */ jsxs(
4980
+ View,
4981
+ {
4982
+ style: [
4983
+ styles23.bar,
4984
+ { backgroundColor: theme.colors.surface, borderBottomColor: theme.colors.border }
4985
+ ],
4986
+ children: [
4987
+ /* @__PURE__ */ jsxs(View, { style: styles23.row, children: [
4988
+ /* @__PURE__ */ jsx(
4989
+ View,
4990
+ {
4991
+ style: [
4992
+ styles23.dot,
4993
+ { backgroundColor: connected ? theme.colors.brand : theme.colors.ink3 }
4994
+ ]
4995
+ }
4996
+ ),
4997
+ connected ? /* @__PURE__ */ jsxs(Fragment, { children: [
4998
+ /* @__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)) }),
4999
+ /* @__PURE__ */ jsx(
5000
+ Pressable,
5001
+ {
5002
+ onPress: toggleMute,
5003
+ accessibilityRole: "button",
5004
+ accessibilityState: { selected: muted },
5005
+ style: [
5006
+ styles23.btn,
5007
+ {
5008
+ borderColor: muted ? theme.colors.brand : theme.colors.border,
5009
+ backgroundColor: muted ? theme.colors.brandSoft : theme.colors.paper
5010
+ }
5011
+ ],
5012
+ children: /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.ink2 }, children: muted ? "Unmute" : "Mute" })
5013
+ }
5014
+ ),
5015
+ /* @__PURE__ */ jsx(
5016
+ Pressable,
5017
+ {
5018
+ onPress: leave,
5019
+ accessibilityRole: "button",
5020
+ style: [styles23.btn, { borderColor: theme.colors.error }],
5021
+ children: /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.error }, children: "Leave" })
5022
+ }
5023
+ )
5024
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5025
+ /* @__PURE__ */ jsx(Text, { style: [styles23.hint, { color: theme.colors.ink3 }], children: status === "connecting" ? "Connecting\u2026" : "Talk to everyone here" }),
5026
+ /* @__PURE__ */ jsx(
5027
+ Pressable,
5028
+ {
5029
+ onPress: () => void join(),
5030
+ disabled: status === "connecting",
5031
+ accessibilityRole: "button",
5032
+ style: [styles23.btn, styles23.btnPrimary, { backgroundColor: theme.colors.brand }],
5033
+ children: /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.onBrand, fontWeight: "600" }, children: status === "connecting" ? "Joining\u2026" : "Join Voice" })
5034
+ }
5035
+ )
5036
+ ] })
5037
+ ] }),
5038
+ error && /* @__PURE__ */ jsx(Text, { style: [styles23.error, { color: theme.colors.error }], children: error.message })
5039
+ ]
5040
+ }
5041
+ );
5042
+ }
5043
+ function ParticipantChip({ participant, label }) {
5044
+ const theme = usePoolseTheme();
5045
+ return /* @__PURE__ */ jsxs(
5046
+ View,
5047
+ {
5048
+ style: [
5049
+ styles23.chip,
5050
+ {
5051
+ backgroundColor: theme.colors.paper,
5052
+ // The speaking ring is a border colour swap rather than an
5053
+ // extra element, so the row never reflows mid-sentence.
5054
+ borderColor: participant.speaking ? theme.colors.brand : theme.colors.border,
5055
+ opacity: participant.muted ? 0.62 : 1
5056
+ }
5057
+ ],
5058
+ children: [
5059
+ /* @__PURE__ */ jsxs(Text, { style: { color: theme.colors.ink2, fontSize: 13 }, numberOfLines: 1, children: [
5060
+ label,
5061
+ participant.isSelf ? " (you)" : ""
5062
+ ] }),
5063
+ participant.muted && /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.error, fontSize: 11, fontWeight: "700" }, children: "\u2715" })
5064
+ ]
5065
+ }
5066
+ );
5067
+ }
5068
+ var styles23 = StyleSheet.create({
5069
+ bar: { paddingHorizontal: 14, paddingVertical: 8, borderBottomWidth: StyleSheet.hairlineWidth },
5070
+ row: { flexDirection: "row", alignItems: "center", gap: 8, flexWrap: "wrap" },
5071
+ dot: { width: 8, height: 8, borderRadius: 4 },
5072
+ people: { flexDirection: "row", alignItems: "center", gap: 6, flexShrink: 1, flexWrap: "wrap" },
5073
+ chip: {
5074
+ flexDirection: "row",
5075
+ alignItems: "center",
5076
+ gap: 5,
5077
+ paddingHorizontal: 10,
5078
+ paddingVertical: 4,
5079
+ borderRadius: 999,
5080
+ borderWidth: 1
5081
+ },
5082
+ btn: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 999, borderWidth: 1 },
5083
+ btnPrimary: { borderWidth: 0 },
5084
+ hint: { fontSize: 13, flexShrink: 1 },
5085
+ error: { fontSize: 12, marginTop: 6 }
5086
+ });
5087
+ function CallScreen({ userId, labelFor, calls: external }) {
5088
+ const theme = usePoolseTheme();
5089
+ const own = useCalls(external ? null : userId);
5090
+ const calls = external ?? own;
5091
+ const available = isNativeWebRtcAvailable();
5092
+ const opts = useMemo(
5093
+ () => available ? { webrtc: createNativeWebRtcAdapter() } : {},
5094
+ [available]
5095
+ );
5096
+ const voice = useVoiceRoom(
5097
+ calls.phase === "active" && available ? calls.activeConversationId : null,
5098
+ opts
5099
+ );
5100
+ useEffect(() => {
5101
+ if (calls.phase === "active" && voice.status === "idle") void voice.join();
5102
+ }, [calls.phase, voice]);
5103
+ const label = labelFor ?? ((id) => `User ${id.slice(0, 6)}`);
5104
+ const visible = calls.phase !== "idle";
5105
+ const hangUp = () => {
5106
+ voice.leave();
5107
+ calls.reset();
5108
+ };
5109
+ return /* @__PURE__ */ jsx(Modal, { visible, animationType: "slide", transparent: false, onRequestClose: hangUp, children: /* @__PURE__ */ jsxs(View, { style: [styles24.screen, { backgroundColor: theme.colors.paper }], children: [
5110
+ /* @__PURE__ */ jsxs(View, { style: styles24.identity, children: [
5111
+ /* @__PURE__ */ jsx(Text, { style: [styles24.who, { color: theme.colors.ink }], children: calls.phase === "ringing-in" && calls.incoming ? label(calls.incoming.callerUserId) : calls.outgoing ? calls.outgoing.calleeUserIds.map(label).join(", ") : "" }),
5112
+ /* @__PURE__ */ jsx(Text, { style: [styles24.state, { color: theme.colors.ink3 }], children: statusLine(calls, voice.status) })
5113
+ ] }),
5114
+ /* @__PURE__ */ jsx(View, { style: styles24.actions, children: calls.phase === "ringing-in" ? /* @__PURE__ */ jsxs(Fragment, { children: [
5115
+ /* @__PURE__ */ jsx(
5116
+ CallButton,
5117
+ {
5118
+ label: "Decline",
5119
+ color: theme.colors.error,
5120
+ onPress: () => void calls.decline()
5121
+ }
5122
+ ),
5123
+ /* @__PURE__ */ jsx(
5124
+ CallButton,
5125
+ {
5126
+ label: "Accept",
5127
+ color: theme.colors.brand,
5128
+ onPress: () => void calls.accept()
5129
+ }
5130
+ )
5131
+ ] }) : calls.phase === "ringing-out" ? /* @__PURE__ */ jsx(
5132
+ CallButton,
5133
+ {
5134
+ label: "Cancel",
5135
+ color: theme.colors.error,
5136
+ onPress: () => void calls.cancel()
5137
+ }
5138
+ ) : 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: [
5139
+ /* @__PURE__ */ jsx(
5140
+ CallButton,
5141
+ {
5142
+ label: voice.muted ? "Unmute" : "Mute",
5143
+ color: theme.colors.ink2,
5144
+ onPress: voice.toggleMute
5145
+ }
5146
+ ),
5147
+ /* @__PURE__ */ jsx(CallButton, { label: "Hang up", color: theme.colors.error, onPress: hangUp })
5148
+ ] }) }),
5149
+ !available && /* @__PURE__ */ jsx(Text, { style: [styles24.warn, { color: theme.colors.error }], children: "react-native-webrtc is not installed \u2014 this call has no audio." }),
5150
+ (calls.error ?? voice.error) && /* @__PURE__ */ jsx(Text, { style: [styles24.warn, { color: theme.colors.error }], children: (calls.error ?? voice.error)?.message })
5151
+ ] }) });
5152
+ }
5153
+ function statusLine(calls, voiceStatus) {
5154
+ switch (calls.phase) {
5155
+ case "ringing-in":
5156
+ return "Incoming call\u2026";
5157
+ case "ringing-out":
5158
+ return "Ringing\u2026";
5159
+ case "declined":
5160
+ return "Call declined";
5161
+ case "busy":
5162
+ return "Already on another call";
5163
+ case "timed-out":
5164
+ return "No answer";
5165
+ case "active":
5166
+ return voiceStatus === "connected" ? "Connected" : "Connecting\u2026";
5167
+ default:
5168
+ return "";
5169
+ }
5170
+ }
5171
+ function CallButton({
5172
+ label,
5173
+ color,
5174
+ onPress
5175
+ }) {
5176
+ return /* @__PURE__ */ jsx(
5177
+ Pressable,
5178
+ {
5179
+ onPress,
5180
+ accessibilityRole: "button",
5181
+ style: [styles24.callBtn, { backgroundColor: color }],
5182
+ children: /* @__PURE__ */ jsx(Text, { style: styles24.callBtnText, children: label })
5183
+ }
5184
+ );
5185
+ }
5186
+ var styles24 = StyleSheet.create({
5187
+ screen: { flex: 1, justifyContent: "space-between", paddingVertical: 72, paddingHorizontal: 24 },
5188
+ identity: { alignItems: "center", gap: 8, marginTop: 48 },
5189
+ who: { fontSize: 28, fontWeight: "600", textAlign: "center" },
5190
+ state: { fontSize: 15 },
5191
+ actions: { flexDirection: "row", justifyContent: "center", gap: 16 },
5192
+ callBtn: {
5193
+ paddingHorizontal: 26,
5194
+ paddingVertical: 16,
5195
+ borderRadius: 999,
5196
+ minWidth: 128,
5197
+ alignItems: "center"
5198
+ },
5199
+ callBtnText: { color: "#ffffff", fontSize: 16, fontWeight: "600" },
5200
+ warn: { fontSize: 12, textAlign: "center", marginTop: 12 }
5201
+ });
5202
+
5203
+ export { AttachmentPicker, AttachmentPreview, Avatar, CallScreen, 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, useAppStateLifecycle, useDisplayName, usePoolseTheme };
4918
5204
  //# sourceMappingURL=index.js.map
4919
5205
  //# sourceMappingURL=index.js.map