@poolse/react-native 2.1.6 → 2.3.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,125 @@ 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
+ /**
981
+ * React Native WebRTC binding, backed by `react-native-webrtc`.
982
+ *
983
+ * The module is passed IN rather than required here, and that is
984
+ * deliberate. Metro resolves dependencies statically, and this package
985
+ * is published through tsup, which rewrites a literal `require(...)`
986
+ * into a `__require(...)` call it cannot see — so bundling
987
+ * `react-native-webrtc` from inside this file never worked. It wasn't
988
+ * merely unavailable in Expo Go; it was absent from every bundle,
989
+ * development builds included, and surfaced as
990
+ * "Requiring unknown module react-native-webrtc" at runtime.
991
+ *
992
+ * Importing it in the app fixes that: Metro sees a static import in
993
+ * your source and bundles it. Optionality then lives where it belongs —
994
+ * an app that doesn't want voice simply never imports it, and never
995
+ * installs the native dependency.
996
+ *
997
+ * import * as webrtc from 'react-native-webrtc';
998
+ * <VoiceRoomBar conversationId={id} webrtc={webrtc} />
999
+ *
1000
+ * Media streams on native aren't attached to an element the way they
1001
+ * are on the web — audio-only calls route to the device's audio session
1002
+ * once the peer connection is live, so the sink is a no-op. Video would
1003
+ * render the stream through `<RTCView>` via the `toURL()` it exposes.
1004
+ */
1005
+
1006
+ /**
1007
+ * The shape this binding needs from `react-native-webrtc`. The module's
1008
+ * own namespace satisfies it, so `import * as webrtc` type-checks
1009
+ * straight through.
1010
+ */
1011
+ interface NativeWebRtcModule {
1012
+ RTCPeerConnection: new (...args: never[]) => unknown;
1013
+ mediaDevices: {
1014
+ getUserMedia: (...args: never[]) => Promise<unknown>;
1015
+ };
1016
+ }
1017
+ /**
1018
+ * True when `mod` looks like a usable `react-native-webrtc`.
1019
+ *
1020
+ * Guards against a half-linked native module: in a bare Expo Go client
1021
+ * the JS may load while the native side is missing, leaving
1022
+ * `RTCPeerConnection` undefined.
1023
+ */
1024
+ declare function isNativeWebRtcAvailable(mod: NativeWebRtcModule | null | undefined): boolean;
1025
+ /**
1026
+ * Adapter for `useVoiceRoom`'s `webrtc` option.
1027
+ *
1028
+ * @param mod the `react-native-webrtc` module, imported by your app.
1029
+ * @throws when `mod` isn't a usable binding — check
1030
+ * {@link isNativeWebRtcAvailable} first if that's reachable in
1031
+ * your UI.
1032
+ */
1033
+ declare function createNativeWebRtcAdapter(mod: NativeWebRtcModule): WebRtcAdapter;
1034
+
1035
+ interface VoiceRoomBarProps {
1036
+ conversationId: string;
1037
+ /**
1038
+ * The `react-native-webrtc` module, imported by your app:
1039
+ *
1040
+ * import * as webrtc from 'react-native-webrtc';
1041
+ *
1042
+ * Required for audio. Metro only bundles what it can see statically,
1043
+ * so this package cannot import it for you — see `voice/webrtc-native`.
1044
+ * Omit it and the bar renders its unavailable state, which is what you
1045
+ * want in Expo Go.
1046
+ */
1047
+ webrtc?: NativeWebRtcModule | null;
1048
+ /** Resolves a user id to a display label. Defaults to a short id. */
1049
+ labelFor?: (userId: string) => string;
1050
+ /**
1051
+ * Rendered instead of the join button when `react-native-webrtc`
1052
+ * isn't installed. Defaults to an inline explanation.
1053
+ */
1054
+ unavailableSlot?: React.ReactNode;
1055
+ }
1056
+ /**
1057
+ * Discord-style voice bar: one "Join Voice" button that expands into a
1058
+ * roster of who's in, with a ring around whoever is talking.
1059
+ *
1060
+ * Drop it above a `<ConversationView>` to give a conversation an
1061
+ * always-on voice channel. For ringing a specific person, use
1062
+ * `<IncomingCallSheet>` together with `useCalls`.
1063
+ */
1064
+ declare function VoiceRoomBar({ conversationId, labelFor, unavailableSlot, webrtc, }: VoiceRoomBarProps): react_jsx_runtime.JSX.Element;
1065
+
1066
+ interface CallScreenProps {
1067
+ /** The signed-in user's id — the topic calls are delivered on. */
1068
+ userId: string;
1069
+ /** Resolves a user id to a display label. Defaults to a short id. */
1070
+ labelFor?: (userId: string) => string;
1071
+ /**
1072
+ * Escape hatch for apps that already own a `useCalls` instance (for
1073
+ * example to place calls from elsewhere in the UI). When omitted the
1074
+ * component manages its own.
1075
+ */
1076
+ calls?: UseCalls;
1077
+ /**
1078
+ * The `react-native-webrtc` module, imported by your app. Required for
1079
+ * call audio; without it the screen still rings and connects but warns
1080
+ * that there is no audio. See {@link VoiceRoomBarProps.webrtc}.
1081
+ */
1082
+ webrtc?: NativeWebRtcModule | null;
1083
+ }
1084
+ /**
1085
+ * WhatsApp-style call UI: a full-screen sheet that covers the app while
1086
+ * a call is ringing or connected, and renders nothing when idle.
1087
+ *
1088
+ * Mount it once, high in the tree, so a call can arrive on any screen:
1089
+ *
1090
+ * <PoolseProvider config={config}>
1091
+ * <CallScreen userId={me.id} />
1092
+ * <YourApp />
1093
+ * </PoolseProvider>
1094
+ *
1095
+ * Placing a call is separate — `useCalls().call(conversationId)`, wired
1096
+ * to whatever button your UI wants. Pass the same instance in via
1097
+ * `calls` so both halves share one state machine.
1098
+ */
1099
+ declare function CallScreen({ userId, labelFor, calls: external, webrtc }: CallScreenProps): react_jsx_runtime.JSX.Element;
1100
+
1101
+ 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, type NativeWebRtcModule, 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,125 @@ 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
+ /**
981
+ * React Native WebRTC binding, backed by `react-native-webrtc`.
982
+ *
983
+ * The module is passed IN rather than required here, and that is
984
+ * deliberate. Metro resolves dependencies statically, and this package
985
+ * is published through tsup, which rewrites a literal `require(...)`
986
+ * into a `__require(...)` call it cannot see — so bundling
987
+ * `react-native-webrtc` from inside this file never worked. It wasn't
988
+ * merely unavailable in Expo Go; it was absent from every bundle,
989
+ * development builds included, and surfaced as
990
+ * "Requiring unknown module react-native-webrtc" at runtime.
991
+ *
992
+ * Importing it in the app fixes that: Metro sees a static import in
993
+ * your source and bundles it. Optionality then lives where it belongs —
994
+ * an app that doesn't want voice simply never imports it, and never
995
+ * installs the native dependency.
996
+ *
997
+ * import * as webrtc from 'react-native-webrtc';
998
+ * <VoiceRoomBar conversationId={id} webrtc={webrtc} />
999
+ *
1000
+ * Media streams on native aren't attached to an element the way they
1001
+ * are on the web — audio-only calls route to the device's audio session
1002
+ * once the peer connection is live, so the sink is a no-op. Video would
1003
+ * render the stream through `<RTCView>` via the `toURL()` it exposes.
1004
+ */
1005
+
1006
+ /**
1007
+ * The shape this binding needs from `react-native-webrtc`. The module's
1008
+ * own namespace satisfies it, so `import * as webrtc` type-checks
1009
+ * straight through.
1010
+ */
1011
+ interface NativeWebRtcModule {
1012
+ RTCPeerConnection: new (...args: never[]) => unknown;
1013
+ mediaDevices: {
1014
+ getUserMedia: (...args: never[]) => Promise<unknown>;
1015
+ };
1016
+ }
1017
+ /**
1018
+ * True when `mod` looks like a usable `react-native-webrtc`.
1019
+ *
1020
+ * Guards against a half-linked native module: in a bare Expo Go client
1021
+ * the JS may load while the native side is missing, leaving
1022
+ * `RTCPeerConnection` undefined.
1023
+ */
1024
+ declare function isNativeWebRtcAvailable(mod: NativeWebRtcModule | null | undefined): boolean;
1025
+ /**
1026
+ * Adapter for `useVoiceRoom`'s `webrtc` option.
1027
+ *
1028
+ * @param mod the `react-native-webrtc` module, imported by your app.
1029
+ * @throws when `mod` isn't a usable binding — check
1030
+ * {@link isNativeWebRtcAvailable} first if that's reachable in
1031
+ * your UI.
1032
+ */
1033
+ declare function createNativeWebRtcAdapter(mod: NativeWebRtcModule): WebRtcAdapter;
1034
+
1035
+ interface VoiceRoomBarProps {
1036
+ conversationId: string;
1037
+ /**
1038
+ * The `react-native-webrtc` module, imported by your app:
1039
+ *
1040
+ * import * as webrtc from 'react-native-webrtc';
1041
+ *
1042
+ * Required for audio. Metro only bundles what it can see statically,
1043
+ * so this package cannot import it for you — see `voice/webrtc-native`.
1044
+ * Omit it and the bar renders its unavailable state, which is what you
1045
+ * want in Expo Go.
1046
+ */
1047
+ webrtc?: NativeWebRtcModule | null;
1048
+ /** Resolves a user id to a display label. Defaults to a short id. */
1049
+ labelFor?: (userId: string) => string;
1050
+ /**
1051
+ * Rendered instead of the join button when `react-native-webrtc`
1052
+ * isn't installed. Defaults to an inline explanation.
1053
+ */
1054
+ unavailableSlot?: React.ReactNode;
1055
+ }
1056
+ /**
1057
+ * Discord-style voice bar: one "Join Voice" button that expands into a
1058
+ * roster of who's in, with a ring around whoever is talking.
1059
+ *
1060
+ * Drop it above a `<ConversationView>` to give a conversation an
1061
+ * always-on voice channel. For ringing a specific person, use
1062
+ * `<IncomingCallSheet>` together with `useCalls`.
1063
+ */
1064
+ declare function VoiceRoomBar({ conversationId, labelFor, unavailableSlot, webrtc, }: VoiceRoomBarProps): react_jsx_runtime.JSX.Element;
1065
+
1066
+ interface CallScreenProps {
1067
+ /** The signed-in user's id — the topic calls are delivered on. */
1068
+ userId: string;
1069
+ /** Resolves a user id to a display label. Defaults to a short id. */
1070
+ labelFor?: (userId: string) => string;
1071
+ /**
1072
+ * Escape hatch for apps that already own a `useCalls` instance (for
1073
+ * example to place calls from elsewhere in the UI). When omitted the
1074
+ * component manages its own.
1075
+ */
1076
+ calls?: UseCalls;
1077
+ /**
1078
+ * The `react-native-webrtc` module, imported by your app. Required for
1079
+ * call audio; without it the screen still rings and connects but warns
1080
+ * that there is no audio. See {@link VoiceRoomBarProps.webrtc}.
1081
+ */
1082
+ webrtc?: NativeWebRtcModule | null;
1083
+ }
1084
+ /**
1085
+ * WhatsApp-style call UI: a full-screen sheet that covers the app while
1086
+ * a call is ringing or connected, and renders nothing when idle.
1087
+ *
1088
+ * Mount it once, high in the tree, so a call can arrive on any screen:
1089
+ *
1090
+ * <PoolseProvider config={config}>
1091
+ * <CallScreen userId={me.id} />
1092
+ * <YourApp />
1093
+ * </PoolseProvider>
1094
+ *
1095
+ * Placing a call is separate — `useCalls().call(conversationId)`, wired
1096
+ * to whatever button your UI wants. Pass the same instance in via
1097
+ * `calls` so both halves share one state machine.
1098
+ */
1099
+ declare function CallScreen({ userId, labelFor, calls: external, webrtc }: CallScreenProps): react_jsx_runtime.JSX.Element;
1100
+
1101
+ 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, type NativeWebRtcModule, 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,287 @@ 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
+ function isNativeWebRtcAvailable(mod) {
4919
+ return typeof mod?.RTCPeerConnection === "function" && mod?.mediaDevices != null;
4920
+ }
4921
+ function createNativeWebRtcAdapter(mod) {
4922
+ if (!isNativeWebRtcAvailable(mod)) {
4923
+ throw new Error(
4924
+ "react-native-webrtc is missing or not linked. Install it and rebuild:\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."
4925
+ );
4926
+ }
4927
+ const rtc = mod;
4928
+ return {
4929
+ createPeerConnection(iceServers) {
4930
+ return new rtc.RTCPeerConnection({ iceServers });
4931
+ },
4932
+ getUserMedia() {
4933
+ return rtc.mediaDevices.getUserMedia({
4934
+ audio: { echoCancellation: true, noiseSuppression: true },
4935
+ video: false
4936
+ });
4937
+ },
4938
+ playRemoteStream() {
4939
+ return () => {
4940
+ };
4941
+ }
4942
+ };
4943
+ }
4944
+ function VoiceRoomBar({
4945
+ conversationId,
4946
+ labelFor,
4947
+ unavailableSlot,
4948
+ webrtc
4949
+ }) {
4950
+ const theme = usePoolseTheme();
4951
+ const available = isNativeWebRtcAvailable(webrtc);
4952
+ const opts = useMemo(
4953
+ () => available && webrtc ? { webrtc: createNativeWebRtcAdapter(webrtc) } : {},
4954
+ [available, webrtc]
4955
+ );
4956
+ const { status, participants, muted, join, leave, toggleMute, error } = useVoiceRoom(
4957
+ available ? conversationId : null,
4958
+ opts
4959
+ );
4960
+ const label = labelFor ?? ((id) => `User ${id.slice(0, 6)}`);
4961
+ const connected = status === "connected";
4962
+ if (!available) {
4963
+ return /* @__PURE__ */ jsx(
4964
+ View,
4965
+ {
4966
+ style: [
4967
+ styles23.bar,
4968
+ { backgroundColor: theme.colors.surface, borderBottomColor: theme.colors.border }
4969
+ ],
4970
+ children: unavailableSlot ?? /* @__PURE__ */ jsx(Text, { style: [styles23.hint, { color: theme.colors.ink3 }], children: "Voice needs react-native-webrtc and a development build." })
4971
+ }
4972
+ );
4973
+ }
4974
+ return /* @__PURE__ */ jsxs(
4975
+ View,
4976
+ {
4977
+ style: [
4978
+ styles23.bar,
4979
+ { backgroundColor: theme.colors.surface, borderBottomColor: theme.colors.border }
4980
+ ],
4981
+ children: [
4982
+ /* @__PURE__ */ jsxs(View, { style: styles23.row, children: [
4983
+ /* @__PURE__ */ jsx(
4984
+ View,
4985
+ {
4986
+ style: [
4987
+ styles23.dot,
4988
+ { backgroundColor: connected ? theme.colors.brand : theme.colors.ink3 }
4989
+ ]
4990
+ }
4991
+ ),
4992
+ connected ? /* @__PURE__ */ jsxs(Fragment, { children: [
4993
+ /* @__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)) }),
4994
+ /* @__PURE__ */ jsx(
4995
+ Pressable,
4996
+ {
4997
+ onPress: toggleMute,
4998
+ accessibilityRole: "button",
4999
+ accessibilityState: { selected: muted },
5000
+ style: [
5001
+ styles23.btn,
5002
+ {
5003
+ borderColor: muted ? theme.colors.brand : theme.colors.border,
5004
+ backgroundColor: muted ? theme.colors.brandSoft : theme.colors.paper
5005
+ }
5006
+ ],
5007
+ children: /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.ink2 }, children: muted ? "Unmute" : "Mute" })
5008
+ }
5009
+ ),
5010
+ /* @__PURE__ */ jsx(
5011
+ Pressable,
5012
+ {
5013
+ onPress: leave,
5014
+ accessibilityRole: "button",
5015
+ style: [styles23.btn, { borderColor: theme.colors.error }],
5016
+ children: /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.error }, children: "Leave" })
5017
+ }
5018
+ )
5019
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5020
+ /* @__PURE__ */ jsx(Text, { style: [styles23.hint, { color: theme.colors.ink3 }], children: status === "connecting" ? "Connecting\u2026" : "Talk to everyone here" }),
5021
+ /* @__PURE__ */ jsx(
5022
+ Pressable,
5023
+ {
5024
+ onPress: () => void join(),
5025
+ disabled: status === "connecting",
5026
+ accessibilityRole: "button",
5027
+ style: [styles23.btn, styles23.btnPrimary, { backgroundColor: theme.colors.brand }],
5028
+ children: /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.onBrand, fontWeight: "600" }, children: status === "connecting" ? "Joining\u2026" : "Join Voice" })
5029
+ }
5030
+ )
5031
+ ] })
5032
+ ] }),
5033
+ error && /* @__PURE__ */ jsx(Text, { style: [styles23.error, { color: theme.colors.error }], children: error.message })
5034
+ ]
5035
+ }
5036
+ );
5037
+ }
5038
+ function ParticipantChip({ participant, label }) {
5039
+ const theme = usePoolseTheme();
5040
+ return /* @__PURE__ */ jsxs(
5041
+ View,
5042
+ {
5043
+ style: [
5044
+ styles23.chip,
5045
+ {
5046
+ backgroundColor: theme.colors.paper,
5047
+ // The speaking ring is a border colour swap rather than an
5048
+ // extra element, so the row never reflows mid-sentence.
5049
+ borderColor: participant.speaking ? theme.colors.brand : theme.colors.border,
5050
+ opacity: participant.muted ? 0.62 : 1
5051
+ }
5052
+ ],
5053
+ children: [
5054
+ /* @__PURE__ */ jsxs(Text, { style: { color: theme.colors.ink2, fontSize: 13 }, numberOfLines: 1, children: [
5055
+ label,
5056
+ participant.isSelf ? " (you)" : ""
5057
+ ] }),
5058
+ participant.muted && /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.error, fontSize: 11, fontWeight: "700" }, children: "\u2715" })
5059
+ ]
5060
+ }
5061
+ );
5062
+ }
5063
+ var styles23 = StyleSheet.create({
5064
+ bar: { paddingHorizontal: 14, paddingVertical: 8, borderBottomWidth: StyleSheet.hairlineWidth },
5065
+ row: { flexDirection: "row", alignItems: "center", gap: 8, flexWrap: "wrap" },
5066
+ dot: { width: 8, height: 8, borderRadius: 4 },
5067
+ people: { flexDirection: "row", alignItems: "center", gap: 6, flexShrink: 1, flexWrap: "wrap" },
5068
+ chip: {
5069
+ flexDirection: "row",
5070
+ alignItems: "center",
5071
+ gap: 5,
5072
+ paddingHorizontal: 10,
5073
+ paddingVertical: 4,
5074
+ borderRadius: 999,
5075
+ borderWidth: 1
5076
+ },
5077
+ btn: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 999, borderWidth: 1 },
5078
+ btnPrimary: { borderWidth: 0 },
5079
+ hint: { fontSize: 13, flexShrink: 1 },
5080
+ error: { fontSize: 12, marginTop: 6 }
5081
+ });
5082
+ function CallScreen({ userId, labelFor, calls: external, webrtc }) {
5083
+ const theme = usePoolseTheme();
5084
+ const own = useCalls(external ? null : userId);
5085
+ const calls = external ?? own;
5086
+ const available = isNativeWebRtcAvailable(webrtc);
5087
+ const opts = useMemo(
5088
+ () => available && webrtc ? { webrtc: createNativeWebRtcAdapter(webrtc) } : {},
5089
+ [available, webrtc]
5090
+ );
5091
+ const voice = useVoiceRoom(
5092
+ calls.phase === "active" && available ? calls.activeConversationId : null,
5093
+ opts
5094
+ );
5095
+ useEffect(() => {
5096
+ if (calls.phase === "active" && voice.status === "idle") void voice.join();
5097
+ }, [calls.phase, voice]);
5098
+ const label = labelFor ?? ((id) => `User ${id.slice(0, 6)}`);
5099
+ const visible = calls.phase !== "idle";
5100
+ const hangUp = () => {
5101
+ voice.leave();
5102
+ calls.reset();
5103
+ };
5104
+ return /* @__PURE__ */ jsx(Modal, { visible, animationType: "slide", transparent: false, onRequestClose: hangUp, children: /* @__PURE__ */ jsxs(View, { style: [styles24.screen, { backgroundColor: theme.colors.paper }], children: [
5105
+ /* @__PURE__ */ jsxs(View, { style: styles24.identity, children: [
5106
+ /* @__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(", ") : "" }),
5107
+ /* @__PURE__ */ jsx(Text, { style: [styles24.state, { color: theme.colors.ink3 }], children: statusLine(calls, voice.status) })
5108
+ ] }),
5109
+ /* @__PURE__ */ jsx(View, { style: styles24.actions, children: calls.phase === "ringing-in" ? /* @__PURE__ */ jsxs(Fragment, { children: [
5110
+ /* @__PURE__ */ jsx(
5111
+ CallButton,
5112
+ {
5113
+ label: "Decline",
5114
+ color: theme.colors.error,
5115
+ onPress: () => void calls.decline()
5116
+ }
5117
+ ),
5118
+ /* @__PURE__ */ jsx(
5119
+ CallButton,
5120
+ {
5121
+ label: "Accept",
5122
+ color: theme.colors.brand,
5123
+ onPress: () => void calls.accept()
5124
+ }
5125
+ )
5126
+ ] }) : calls.phase === "ringing-out" ? /* @__PURE__ */ jsx(
5127
+ CallButton,
5128
+ {
5129
+ label: "Cancel",
5130
+ color: theme.colors.error,
5131
+ onPress: () => void calls.cancel()
5132
+ }
5133
+ ) : 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: [
5134
+ /* @__PURE__ */ jsx(
5135
+ CallButton,
5136
+ {
5137
+ label: voice.muted ? "Unmute" : "Mute",
5138
+ color: theme.colors.ink2,
5139
+ onPress: voice.toggleMute
5140
+ }
5141
+ ),
5142
+ /* @__PURE__ */ jsx(CallButton, { label: "Hang up", color: theme.colors.error, onPress: hangUp })
5143
+ ] }) }),
5144
+ !available && /* @__PURE__ */ jsx(Text, { style: [styles24.warn, { color: theme.colors.error }], children: "react-native-webrtc is not installed \u2014 this call has no audio." }),
5145
+ (calls.error ?? voice.error) && /* @__PURE__ */ jsx(Text, { style: [styles24.warn, { color: theme.colors.error }], children: (calls.error ?? voice.error)?.message })
5146
+ ] }) });
5147
+ }
5148
+ function statusLine(calls, voiceStatus) {
5149
+ switch (calls.phase) {
5150
+ case "ringing-in":
5151
+ return "Incoming call\u2026";
5152
+ case "ringing-out":
5153
+ return "Ringing\u2026";
5154
+ case "declined":
5155
+ return "Call declined";
5156
+ case "busy":
5157
+ return "Already on another call";
5158
+ case "timed-out":
5159
+ return "No answer";
5160
+ case "active":
5161
+ return voiceStatus === "connected" ? "Connected" : "Connecting\u2026";
5162
+ default:
5163
+ return "";
5164
+ }
5165
+ }
5166
+ function CallButton({
5167
+ label,
5168
+ color,
5169
+ onPress
5170
+ }) {
5171
+ return /* @__PURE__ */ jsx(
5172
+ Pressable,
5173
+ {
5174
+ onPress,
5175
+ accessibilityRole: "button",
5176
+ style: [styles24.callBtn, { backgroundColor: color }],
5177
+ children: /* @__PURE__ */ jsx(Text, { style: styles24.callBtnText, children: label })
5178
+ }
5179
+ );
5180
+ }
5181
+ var styles24 = StyleSheet.create({
5182
+ screen: { flex: 1, justifyContent: "space-between", paddingVertical: 72, paddingHorizontal: 24 },
5183
+ identity: { alignItems: "center", gap: 8, marginTop: 48 },
5184
+ who: { fontSize: 28, fontWeight: "600", textAlign: "center" },
5185
+ state: { fontSize: 15 },
5186
+ actions: { flexDirection: "row", justifyContent: "center", gap: 16 },
5187
+ callBtn: {
5188
+ paddingHorizontal: 26,
5189
+ paddingVertical: 16,
5190
+ borderRadius: 999,
5191
+ minWidth: 128,
5192
+ alignItems: "center"
5193
+ },
5194
+ callBtnText: { color: "#ffffff", fontSize: 16, fontWeight: "600" },
5195
+ warn: { fontSize: 12, textAlign: "center", marginTop: 12 }
5196
+ });
5197
+
5198
+ 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
5199
  //# sourceMappingURL=index.js.map
4919
5200
  //# sourceMappingURL=index.js.map