@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/CHANGELOG.md +89 -89
- package/README.md +238 -238
- package/dist/index.cjs +290 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +82 -2
- package/dist/index.d.ts +82 -2
- package/dist/index.js +288 -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,83 @@ declare const defaultDarkTheme: PoolseRNTheme;
|
|
|
976
977
|
*/
|
|
977
978
|
declare function useAppStateLifecycle(): void;
|
|
978
979
|
|
|
979
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|