@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/CHANGELOG.md +89 -89
- package/README.md +238 -238
- package/dist/index.cjs +285 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +124 -2
- package/dist/index.d.ts +124 -2
- package/dist/index.js +283 -2
- package/dist/index.js.map +1 -1
- package/package.json +18 -11
- package/LICENSE +0 -21
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|