@poolse/react-native 2.5.0 → 2.7.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.cjs +96 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +89 -38
- package/dist/index.d.ts +89 -38
- package/dist/index.js +96 -26
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode, ReactElement } from 'react';
|
|
3
|
-
import { Conversation, Uuid, Message, AttachmentUploadInput, Membership, Attachment, WebRtcAdapter } from '@poolse/sdk';
|
|
3
|
+
import { Conversation, Uuid, Message, AttachmentUploadInput, Membership, Attachment, WebRtcAdapter, VoiceStatus } from '@poolse/sdk';
|
|
4
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
5
|
import { UseCalls } from '@poolse/react';
|
|
6
6
|
|
|
@@ -1045,6 +1045,15 @@ interface VoiceRoomBarProps {
|
|
|
1045
1045
|
* want in Expo Go.
|
|
1046
1046
|
*/
|
|
1047
1047
|
webrtc?: NativeWebRtcModule | null;
|
|
1048
|
+
/**
|
|
1049
|
+
* Called whenever the room's connection state changes.
|
|
1050
|
+
*
|
|
1051
|
+
* Audio routing is the app's job — speakerphone, the proximity sensor
|
|
1052
|
+
* that blanks the screen at your ear, and holding the audio session
|
|
1053
|
+
* open all need a native module this package deliberately doesn't
|
|
1054
|
+
* depend on. Use this to start and stop it.
|
|
1055
|
+
*/
|
|
1056
|
+
onStatusChange?: (status: VoiceStatus) => void;
|
|
1048
1057
|
/** Resolves a user id to a display label. Defaults to a short id. */
|
|
1049
1058
|
labelFor?: (userId: string) => string;
|
|
1050
1059
|
/**
|
|
@@ -1061,7 +1070,7 @@ interface VoiceRoomBarProps {
|
|
|
1061
1070
|
* always-on voice channel. For ringing a specific person, use
|
|
1062
1071
|
* `<IncomingCallSheet>` together with `useCalls`.
|
|
1063
1072
|
*/
|
|
1064
|
-
declare function VoiceRoomBar({ conversationId, labelFor, unavailableSlot, webrtc, }: VoiceRoomBarProps): react_jsx_runtime.JSX.Element;
|
|
1073
|
+
declare function VoiceRoomBar({ conversationId, labelFor, unavailableSlot, webrtc, onStatusChange, }: VoiceRoomBarProps): react_jsx_runtime.JSX.Element;
|
|
1065
1074
|
|
|
1066
1075
|
interface CallScreenProps {
|
|
1067
1076
|
/** The signed-in user's id — the topic calls are delivered on. */
|
|
@@ -1080,6 +1089,22 @@ interface CallScreenProps {
|
|
|
1080
1089
|
* that there is no audio. See {@link VoiceRoomBarProps.webrtc}.
|
|
1081
1090
|
*/
|
|
1082
1091
|
webrtc?: NativeWebRtcModule | null;
|
|
1092
|
+
/**
|
|
1093
|
+
* Called whenever the call's audio connection state changes. Drive
|
|
1094
|
+
* speakerphone and the proximity sensor from here — see
|
|
1095
|
+
* {@link VoiceRoomBarProps.onStatusChange}.
|
|
1096
|
+
*/
|
|
1097
|
+
onStatusChange?: (status: VoiceStatus) => void;
|
|
1098
|
+
/**
|
|
1099
|
+
* Hide this screen while a call is merely ringing in.
|
|
1100
|
+
*
|
|
1101
|
+
* Set it when `useVoipCalls` is wired: CallKit already shows the
|
|
1102
|
+
* system call UI on the lock screen and in the Dynamic Island, and a
|
|
1103
|
+
* second in-app sheet on top of it means one tap of Call produces two
|
|
1104
|
+
* competing rings. The screen still appears once the call connects, to
|
|
1105
|
+
* carry the in-call controls.
|
|
1106
|
+
*/
|
|
1107
|
+
nativeIncomingUi?: boolean;
|
|
1083
1108
|
}
|
|
1084
1109
|
/**
|
|
1085
1110
|
* WhatsApp-style call UI: a full-screen sheet that covers the app while
|
|
@@ -1096,58 +1121,58 @@ interface CallScreenProps {
|
|
|
1096
1121
|
* to whatever button your UI wants. Pass the same instance in via
|
|
1097
1122
|
* `calls` so both halves share one state machine.
|
|
1098
1123
|
*/
|
|
1099
|
-
declare function CallScreen({ userId, labelFor, calls: external, webrtc }: CallScreenProps): react_jsx_runtime.JSX.Element;
|
|
1124
|
+
declare function CallScreen({ userId, labelFor, calls: external, webrtc, onStatusChange, nativeIncomingUi, }: CallScreenProps): react_jsx_runtime.JSX.Element;
|
|
1100
1125
|
|
|
1101
1126
|
/**
|
|
1102
|
-
* Bridges iOS PushKit + CallKit to poolse calls,
|
|
1103
|
-
*
|
|
1127
|
+
* Bridges iOS PushKit + CallKit to poolse calls, and owns the incoming
|
|
1128
|
+
* call UI end to end.
|
|
1129
|
+
*
|
|
1130
|
+
* ## Why this coordinates rather than just forwards
|
|
1104
1131
|
*
|
|
1105
|
-
*
|
|
1132
|
+
* A call arrives by two routes: `call:incoming` over the WebSocket when
|
|
1133
|
+
* the app is foregrounded, and a VoIP push when it isn't. The server
|
|
1134
|
+
* fires both, always — deciding server-side whether a socket is really
|
|
1135
|
+
* alive is a race it would lose.
|
|
1106
1136
|
*
|
|
1107
|
-
*
|
|
1108
|
-
*
|
|
1109
|
-
*
|
|
1110
|
-
*
|
|
1137
|
+
* Reconciling them is therefore the client's job, and doing it badly is
|
|
1138
|
+
* very visible: forward each route to its own UI and one tap of Call
|
|
1139
|
+
* shows the callee two rings, an in-app sheet fighting a system call
|
|
1140
|
+
* screen.
|
|
1141
|
+
*
|
|
1142
|
+
* So this hook is the single place a call becomes visible. Both routes
|
|
1143
|
+
* feed one map keyed by `callId`, CallKit is the only ring UI, and the
|
|
1144
|
+
* native call is torn down whenever the call reaches a terminal state —
|
|
1145
|
+
* declined, cancelled, timed out, answered. Pass the same `calls` object
|
|
1146
|
+
* to `<CallScreen>` and it stays out of the way while a ring is up.
|
|
1111
1147
|
*
|
|
1112
1148
|
* ## The rule Apple enforces
|
|
1113
1149
|
*
|
|
1114
1150
|
* Every delivered VoIP push MUST report an incoming call to CallKit,
|
|
1115
1151
|
* essentially immediately. Miss it and iOS terminates the app; do it
|
|
1116
|
-
* repeatedly and the system stops delivering VoIP pushes
|
|
1117
|
-
* `reportCall` below is not optional politeness — it is the contract.
|
|
1152
|
+
* repeatedly and the system stops delivering VoIP pushes at all.
|
|
1118
1153
|
*
|
|
1119
1154
|
* ## Dependencies
|
|
1120
1155
|
*
|
|
1121
1156
|
* `react-native-voip-push-notification` and `react-native-callkeep`,
|
|
1122
|
-
* both native
|
|
1123
|
-
*
|
|
1124
|
-
*
|
|
1125
|
-
*
|
|
1126
|
-
* import VoipPushNotification from 'react-native-voip-push-notification';
|
|
1127
|
-
* import RNCallKeep from 'react-native-callkeep';
|
|
1157
|
+
* both native and both requiring a development build. Metro only bundles
|
|
1158
|
+
* what it can see statically, so YOUR APP imports them and passes them
|
|
1159
|
+
* in. The iOS AppDelegate also needs PushKit delegate methods, which
|
|
1160
|
+
* Expo does not generate — see the sample's `plugins/withVoipPush.js`.
|
|
1128
1161
|
*
|
|
1129
1162
|
* useVoipCalls({
|
|
1130
1163
|
* voipPush: VoipPushNotification,
|
|
1131
1164
|
* callKeep: RNCallKeep,
|
|
1132
|
-
*
|
|
1165
|
+
* calls, // the same useCalls() the UI uses
|
|
1166
|
+
* apnsEnvironment: 'sandbox',
|
|
1133
1167
|
* });
|
|
1134
1168
|
*/
|
|
1135
|
-
/**
|
|
1136
|
-
* The slice of `react-native-voip-push-notification` used here.
|
|
1137
|
-
*
|
|
1138
|
-
* Loose on purpose. The real module types `addEventListener` as a
|
|
1139
|
-
* generic whose handler payload varies per event, and pinning exact
|
|
1140
|
-
* signatures here makes `voipPush={VoipPushNotification}` fail to
|
|
1141
|
-
* compile — the prop becomes unusable, which is the whole point of it.
|
|
1142
|
-
* Method syntax (not property arrow syntax) keeps the check bivariant.
|
|
1143
|
-
*/
|
|
1169
|
+
/** The slice of `react-native-voip-push-notification` used here. */
|
|
1144
1170
|
interface VoipPushModule {
|
|
1145
1171
|
addEventListener(event: string, handler: (payload: unknown) => void): void;
|
|
1146
1172
|
removeEventListener(event: string): void;
|
|
1147
1173
|
registerVoipToken(): void;
|
|
1148
1174
|
}
|
|
1149
|
-
/** The slice of `react-native-callkeep` used here.
|
|
1150
|
-
* reason — `setup` resolves to a boolean in the real module. */
|
|
1175
|
+
/** The slice of `react-native-callkeep` used here. */
|
|
1151
1176
|
interface CallKeepModule {
|
|
1152
1177
|
setup(options: unknown): Promise<unknown>;
|
|
1153
1178
|
displayIncomingCall(uuid: string, handle: string, localizedCallerName?: string, handleType?: string, hasVideo?: boolean): unknown;
|
|
@@ -1166,22 +1191,48 @@ interface UseVoipCallsOptions {
|
|
|
1166
1191
|
voipPush: VoipPushModule;
|
|
1167
1192
|
callKeep: CallKeepModule;
|
|
1168
1193
|
/**
|
|
1169
|
-
*
|
|
1170
|
-
*
|
|
1171
|
-
*
|
|
1194
|
+
* The `useCalls(...)` instance driving this screen. Supplying it lets
|
|
1195
|
+
* the hook answer, decline and dismiss through the same state machine
|
|
1196
|
+
* the UI uses, rather than two halves disagreeing about what is
|
|
1197
|
+
* happening.
|
|
1172
1198
|
*/
|
|
1199
|
+
calls?: UseCalls;
|
|
1200
|
+
/** Called when the user answers from the native call UI. */
|
|
1173
1201
|
onAnswer?: (call: VoipIncomingCall) => void;
|
|
1174
|
-
/**
|
|
1202
|
+
/** Called when the user declines from the native call UI. */
|
|
1175
1203
|
onDecline?: (call: VoipIncomingCall) => void;
|
|
1176
1204
|
/** CallKit display name for your app. Shown on the lock screen. */
|
|
1177
1205
|
appName?: string;
|
|
1206
|
+
/**
|
|
1207
|
+
* Which APNs environment issued this build's PushKit token — it must
|
|
1208
|
+
* match the app's `aps-environment` entitlement.
|
|
1209
|
+
*
|
|
1210
|
+
* Pass it explicitly. The fallback guesses from `__DEV__`, which
|
|
1211
|
+
* describes the JS bundle and NOT the entitlement: an ad-hoc or
|
|
1212
|
+
* TestFlight build has `__DEV__ === false` while still carrying the
|
|
1213
|
+
* `development` entitlement, so it receives a sandbox token and
|
|
1214
|
+
* reports it as production. The server then pushes to the wrong
|
|
1215
|
+
* environment, Apple answers BadDeviceToken, and the phone never
|
|
1216
|
+
* rings — with nothing in the app to suggest why.
|
|
1217
|
+
*/
|
|
1218
|
+
apnsEnvironment?: 'production' | 'sandbox';
|
|
1219
|
+
/**
|
|
1220
|
+
* How old a pushed call may be before it is ignored, in ms. Defaults
|
|
1221
|
+
* to 45s, matching `useCalls`' ring timeout — past that the caller has
|
|
1222
|
+
* already given up, so there is nothing live to answer.
|
|
1223
|
+
*
|
|
1224
|
+
* PushKit holds a push that lands before the JS bundle is up and
|
|
1225
|
+
* replays it on launch, so opening the app can ring you for a call the
|
|
1226
|
+
* caller hung up on minutes ago — a ring with nothing live to accept.
|
|
1227
|
+
*/
|
|
1228
|
+
staleAfterMs?: number;
|
|
1178
1229
|
}
|
|
1179
1230
|
/**
|
|
1180
|
-
* Registers this device for VoIP pushes and
|
|
1231
|
+
* Registers this device for VoIP pushes and drives the native call UI.
|
|
1181
1232
|
*
|
|
1182
|
-
*
|
|
1183
|
-
* needs a separate FCM + ConnectionService integration.
|
|
1233
|
+
* Mount once, as high in the signed-in tree as possible. Does nothing on
|
|
1234
|
+
* Android, which needs a separate FCM + ConnectionService integration.
|
|
1184
1235
|
*/
|
|
1185
|
-
declare function useVoipCalls({ voipPush, callKeep, onAnswer, onDecline, appName, }: UseVoipCallsOptions): void;
|
|
1236
|
+
declare function useVoipCalls({ voipPush, callKeep, calls, onAnswer, onDecline, appName, apnsEnvironment, staleAfterMs, }: UseVoipCallsOptions): void;
|
|
1186
1237
|
|
|
1187
1238
|
export { AttachmentPicker, type AttachmentPickerProps, AttachmentPreview, type AttachmentPreviewProps, Avatar, type AvatarProps, type CallKeepModule, 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, type UseVoipCallsOptions, UserName, type UserNameProps, VoiceRoomBar, type VoiceRoomBarProps, type VoipIncomingCall, type VoipPushModule, createNativeWebRtcAdapter, defaultDarkTheme, defaultLightTheme, isNativeWebRtcAvailable, useAppStateLifecycle, useDisplayName, usePoolseTheme, useVoipCalls };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode, ReactElement } from 'react';
|
|
3
|
-
import { Conversation, Uuid, Message, AttachmentUploadInput, Membership, Attachment, WebRtcAdapter } from '@poolse/sdk';
|
|
3
|
+
import { Conversation, Uuid, Message, AttachmentUploadInput, Membership, Attachment, WebRtcAdapter, VoiceStatus } from '@poolse/sdk';
|
|
4
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
5
|
import { UseCalls } from '@poolse/react';
|
|
6
6
|
|
|
@@ -1045,6 +1045,15 @@ interface VoiceRoomBarProps {
|
|
|
1045
1045
|
* want in Expo Go.
|
|
1046
1046
|
*/
|
|
1047
1047
|
webrtc?: NativeWebRtcModule | null;
|
|
1048
|
+
/**
|
|
1049
|
+
* Called whenever the room's connection state changes.
|
|
1050
|
+
*
|
|
1051
|
+
* Audio routing is the app's job — speakerphone, the proximity sensor
|
|
1052
|
+
* that blanks the screen at your ear, and holding the audio session
|
|
1053
|
+
* open all need a native module this package deliberately doesn't
|
|
1054
|
+
* depend on. Use this to start and stop it.
|
|
1055
|
+
*/
|
|
1056
|
+
onStatusChange?: (status: VoiceStatus) => void;
|
|
1048
1057
|
/** Resolves a user id to a display label. Defaults to a short id. */
|
|
1049
1058
|
labelFor?: (userId: string) => string;
|
|
1050
1059
|
/**
|
|
@@ -1061,7 +1070,7 @@ interface VoiceRoomBarProps {
|
|
|
1061
1070
|
* always-on voice channel. For ringing a specific person, use
|
|
1062
1071
|
* `<IncomingCallSheet>` together with `useCalls`.
|
|
1063
1072
|
*/
|
|
1064
|
-
declare function VoiceRoomBar({ conversationId, labelFor, unavailableSlot, webrtc, }: VoiceRoomBarProps): react_jsx_runtime.JSX.Element;
|
|
1073
|
+
declare function VoiceRoomBar({ conversationId, labelFor, unavailableSlot, webrtc, onStatusChange, }: VoiceRoomBarProps): react_jsx_runtime.JSX.Element;
|
|
1065
1074
|
|
|
1066
1075
|
interface CallScreenProps {
|
|
1067
1076
|
/** The signed-in user's id — the topic calls are delivered on. */
|
|
@@ -1080,6 +1089,22 @@ interface CallScreenProps {
|
|
|
1080
1089
|
* that there is no audio. See {@link VoiceRoomBarProps.webrtc}.
|
|
1081
1090
|
*/
|
|
1082
1091
|
webrtc?: NativeWebRtcModule | null;
|
|
1092
|
+
/**
|
|
1093
|
+
* Called whenever the call's audio connection state changes. Drive
|
|
1094
|
+
* speakerphone and the proximity sensor from here — see
|
|
1095
|
+
* {@link VoiceRoomBarProps.onStatusChange}.
|
|
1096
|
+
*/
|
|
1097
|
+
onStatusChange?: (status: VoiceStatus) => void;
|
|
1098
|
+
/**
|
|
1099
|
+
* Hide this screen while a call is merely ringing in.
|
|
1100
|
+
*
|
|
1101
|
+
* Set it when `useVoipCalls` is wired: CallKit already shows the
|
|
1102
|
+
* system call UI on the lock screen and in the Dynamic Island, and a
|
|
1103
|
+
* second in-app sheet on top of it means one tap of Call produces two
|
|
1104
|
+
* competing rings. The screen still appears once the call connects, to
|
|
1105
|
+
* carry the in-call controls.
|
|
1106
|
+
*/
|
|
1107
|
+
nativeIncomingUi?: boolean;
|
|
1083
1108
|
}
|
|
1084
1109
|
/**
|
|
1085
1110
|
* WhatsApp-style call UI: a full-screen sheet that covers the app while
|
|
@@ -1096,58 +1121,58 @@ interface CallScreenProps {
|
|
|
1096
1121
|
* to whatever button your UI wants. Pass the same instance in via
|
|
1097
1122
|
* `calls` so both halves share one state machine.
|
|
1098
1123
|
*/
|
|
1099
|
-
declare function CallScreen({ userId, labelFor, calls: external, webrtc }: CallScreenProps): react_jsx_runtime.JSX.Element;
|
|
1124
|
+
declare function CallScreen({ userId, labelFor, calls: external, webrtc, onStatusChange, nativeIncomingUi, }: CallScreenProps): react_jsx_runtime.JSX.Element;
|
|
1100
1125
|
|
|
1101
1126
|
/**
|
|
1102
|
-
* Bridges iOS PushKit + CallKit to poolse calls,
|
|
1103
|
-
*
|
|
1127
|
+
* Bridges iOS PushKit + CallKit to poolse calls, and owns the incoming
|
|
1128
|
+
* call UI end to end.
|
|
1129
|
+
*
|
|
1130
|
+
* ## Why this coordinates rather than just forwards
|
|
1104
1131
|
*
|
|
1105
|
-
*
|
|
1132
|
+
* A call arrives by two routes: `call:incoming` over the WebSocket when
|
|
1133
|
+
* the app is foregrounded, and a VoIP push when it isn't. The server
|
|
1134
|
+
* fires both, always — deciding server-side whether a socket is really
|
|
1135
|
+
* alive is a race it would lose.
|
|
1106
1136
|
*
|
|
1107
|
-
*
|
|
1108
|
-
*
|
|
1109
|
-
*
|
|
1110
|
-
*
|
|
1137
|
+
* Reconciling them is therefore the client's job, and doing it badly is
|
|
1138
|
+
* very visible: forward each route to its own UI and one tap of Call
|
|
1139
|
+
* shows the callee two rings, an in-app sheet fighting a system call
|
|
1140
|
+
* screen.
|
|
1141
|
+
*
|
|
1142
|
+
* So this hook is the single place a call becomes visible. Both routes
|
|
1143
|
+
* feed one map keyed by `callId`, CallKit is the only ring UI, and the
|
|
1144
|
+
* native call is torn down whenever the call reaches a terminal state —
|
|
1145
|
+
* declined, cancelled, timed out, answered. Pass the same `calls` object
|
|
1146
|
+
* to `<CallScreen>` and it stays out of the way while a ring is up.
|
|
1111
1147
|
*
|
|
1112
1148
|
* ## The rule Apple enforces
|
|
1113
1149
|
*
|
|
1114
1150
|
* Every delivered VoIP push MUST report an incoming call to CallKit,
|
|
1115
1151
|
* essentially immediately. Miss it and iOS terminates the app; do it
|
|
1116
|
-
* repeatedly and the system stops delivering VoIP pushes
|
|
1117
|
-
* `reportCall` below is not optional politeness — it is the contract.
|
|
1152
|
+
* repeatedly and the system stops delivering VoIP pushes at all.
|
|
1118
1153
|
*
|
|
1119
1154
|
* ## Dependencies
|
|
1120
1155
|
*
|
|
1121
1156
|
* `react-native-voip-push-notification` and `react-native-callkeep`,
|
|
1122
|
-
* both native
|
|
1123
|
-
*
|
|
1124
|
-
*
|
|
1125
|
-
*
|
|
1126
|
-
* import VoipPushNotification from 'react-native-voip-push-notification';
|
|
1127
|
-
* import RNCallKeep from 'react-native-callkeep';
|
|
1157
|
+
* both native and both requiring a development build. Metro only bundles
|
|
1158
|
+
* what it can see statically, so YOUR APP imports them and passes them
|
|
1159
|
+
* in. The iOS AppDelegate also needs PushKit delegate methods, which
|
|
1160
|
+
* Expo does not generate — see the sample's `plugins/withVoipPush.js`.
|
|
1128
1161
|
*
|
|
1129
1162
|
* useVoipCalls({
|
|
1130
1163
|
* voipPush: VoipPushNotification,
|
|
1131
1164
|
* callKeep: RNCallKeep,
|
|
1132
|
-
*
|
|
1165
|
+
* calls, // the same useCalls() the UI uses
|
|
1166
|
+
* apnsEnvironment: 'sandbox',
|
|
1133
1167
|
* });
|
|
1134
1168
|
*/
|
|
1135
|
-
/**
|
|
1136
|
-
* The slice of `react-native-voip-push-notification` used here.
|
|
1137
|
-
*
|
|
1138
|
-
* Loose on purpose. The real module types `addEventListener` as a
|
|
1139
|
-
* generic whose handler payload varies per event, and pinning exact
|
|
1140
|
-
* signatures here makes `voipPush={VoipPushNotification}` fail to
|
|
1141
|
-
* compile — the prop becomes unusable, which is the whole point of it.
|
|
1142
|
-
* Method syntax (not property arrow syntax) keeps the check bivariant.
|
|
1143
|
-
*/
|
|
1169
|
+
/** The slice of `react-native-voip-push-notification` used here. */
|
|
1144
1170
|
interface VoipPushModule {
|
|
1145
1171
|
addEventListener(event: string, handler: (payload: unknown) => void): void;
|
|
1146
1172
|
removeEventListener(event: string): void;
|
|
1147
1173
|
registerVoipToken(): void;
|
|
1148
1174
|
}
|
|
1149
|
-
/** The slice of `react-native-callkeep` used here.
|
|
1150
|
-
* reason — `setup` resolves to a boolean in the real module. */
|
|
1175
|
+
/** The slice of `react-native-callkeep` used here. */
|
|
1151
1176
|
interface CallKeepModule {
|
|
1152
1177
|
setup(options: unknown): Promise<unknown>;
|
|
1153
1178
|
displayIncomingCall(uuid: string, handle: string, localizedCallerName?: string, handleType?: string, hasVideo?: boolean): unknown;
|
|
@@ -1166,22 +1191,48 @@ interface UseVoipCallsOptions {
|
|
|
1166
1191
|
voipPush: VoipPushModule;
|
|
1167
1192
|
callKeep: CallKeepModule;
|
|
1168
1193
|
/**
|
|
1169
|
-
*
|
|
1170
|
-
*
|
|
1171
|
-
*
|
|
1194
|
+
* The `useCalls(...)` instance driving this screen. Supplying it lets
|
|
1195
|
+
* the hook answer, decline and dismiss through the same state machine
|
|
1196
|
+
* the UI uses, rather than two halves disagreeing about what is
|
|
1197
|
+
* happening.
|
|
1172
1198
|
*/
|
|
1199
|
+
calls?: UseCalls;
|
|
1200
|
+
/** Called when the user answers from the native call UI. */
|
|
1173
1201
|
onAnswer?: (call: VoipIncomingCall) => void;
|
|
1174
|
-
/**
|
|
1202
|
+
/** Called when the user declines from the native call UI. */
|
|
1175
1203
|
onDecline?: (call: VoipIncomingCall) => void;
|
|
1176
1204
|
/** CallKit display name for your app. Shown on the lock screen. */
|
|
1177
1205
|
appName?: string;
|
|
1206
|
+
/**
|
|
1207
|
+
* Which APNs environment issued this build's PushKit token — it must
|
|
1208
|
+
* match the app's `aps-environment` entitlement.
|
|
1209
|
+
*
|
|
1210
|
+
* Pass it explicitly. The fallback guesses from `__DEV__`, which
|
|
1211
|
+
* describes the JS bundle and NOT the entitlement: an ad-hoc or
|
|
1212
|
+
* TestFlight build has `__DEV__ === false` while still carrying the
|
|
1213
|
+
* `development` entitlement, so it receives a sandbox token and
|
|
1214
|
+
* reports it as production. The server then pushes to the wrong
|
|
1215
|
+
* environment, Apple answers BadDeviceToken, and the phone never
|
|
1216
|
+
* rings — with nothing in the app to suggest why.
|
|
1217
|
+
*/
|
|
1218
|
+
apnsEnvironment?: 'production' | 'sandbox';
|
|
1219
|
+
/**
|
|
1220
|
+
* How old a pushed call may be before it is ignored, in ms. Defaults
|
|
1221
|
+
* to 45s, matching `useCalls`' ring timeout — past that the caller has
|
|
1222
|
+
* already given up, so there is nothing live to answer.
|
|
1223
|
+
*
|
|
1224
|
+
* PushKit holds a push that lands before the JS bundle is up and
|
|
1225
|
+
* replays it on launch, so opening the app can ring you for a call the
|
|
1226
|
+
* caller hung up on minutes ago — a ring with nothing live to accept.
|
|
1227
|
+
*/
|
|
1228
|
+
staleAfterMs?: number;
|
|
1178
1229
|
}
|
|
1179
1230
|
/**
|
|
1180
|
-
* Registers this device for VoIP pushes and
|
|
1231
|
+
* Registers this device for VoIP pushes and drives the native call UI.
|
|
1181
1232
|
*
|
|
1182
|
-
*
|
|
1183
|
-
* needs a separate FCM + ConnectionService integration.
|
|
1233
|
+
* Mount once, as high in the signed-in tree as possible. Does nothing on
|
|
1234
|
+
* Android, which needs a separate FCM + ConnectionService integration.
|
|
1184
1235
|
*/
|
|
1185
|
-
declare function useVoipCalls({ voipPush, callKeep, onAnswer, onDecline, appName, }: UseVoipCallsOptions): void;
|
|
1236
|
+
declare function useVoipCalls({ voipPush, callKeep, calls, onAnswer, onDecline, appName, apnsEnvironment, staleAfterMs, }: UseVoipCallsOptions): void;
|
|
1186
1237
|
|
|
1187
1238
|
export { AttachmentPicker, type AttachmentPickerProps, AttachmentPreview, type AttachmentPreviewProps, Avatar, type AvatarProps, type CallKeepModule, 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, type UseVoipCallsOptions, UserName, type UserNameProps, VoiceRoomBar, type VoiceRoomBarProps, type VoipIncomingCall, type VoipPushModule, createNativeWebRtcAdapter, defaultDarkTheme, defaultLightTheme, isNativeWebRtcAvailable, useAppStateLifecycle, useDisplayName, usePoolseTheme, useVoipCalls };
|
package/dist/index.js
CHANGED
|
@@ -4945,7 +4945,8 @@ function VoiceRoomBar({
|
|
|
4945
4945
|
conversationId,
|
|
4946
4946
|
labelFor,
|
|
4947
4947
|
unavailableSlot,
|
|
4948
|
-
webrtc
|
|
4948
|
+
webrtc,
|
|
4949
|
+
onStatusChange
|
|
4949
4950
|
}) {
|
|
4950
4951
|
const theme = usePoolseTheme();
|
|
4951
4952
|
const available = isNativeWebRtcAvailable(webrtc);
|
|
@@ -4957,6 +4958,9 @@ function VoiceRoomBar({
|
|
|
4957
4958
|
available ? conversationId : null,
|
|
4958
4959
|
opts
|
|
4959
4960
|
);
|
|
4961
|
+
useEffect(() => {
|
|
4962
|
+
onStatusChange?.(status);
|
|
4963
|
+
}, [status, onStatusChange]);
|
|
4960
4964
|
const label = labelFor ?? ((id) => `User ${id.slice(0, 6)}`);
|
|
4961
4965
|
const connected = status === "connected";
|
|
4962
4966
|
if (!available) {
|
|
@@ -5079,7 +5083,14 @@ var styles23 = StyleSheet.create({
|
|
|
5079
5083
|
hint: { fontSize: 13, flexShrink: 1 },
|
|
5080
5084
|
error: { fontSize: 12, marginTop: 6 }
|
|
5081
5085
|
});
|
|
5082
|
-
function CallScreen({
|
|
5086
|
+
function CallScreen({
|
|
5087
|
+
userId,
|
|
5088
|
+
labelFor,
|
|
5089
|
+
calls: external,
|
|
5090
|
+
webrtc,
|
|
5091
|
+
onStatusChange,
|
|
5092
|
+
nativeIncomingUi = false
|
|
5093
|
+
}) {
|
|
5083
5094
|
const theme = usePoolseTheme();
|
|
5084
5095
|
const own = useCalls(external ? null : userId);
|
|
5085
5096
|
const calls = external ?? own;
|
|
@@ -5095,8 +5106,12 @@ function CallScreen({ userId, labelFor, calls: external, webrtc }) {
|
|
|
5095
5106
|
useEffect(() => {
|
|
5096
5107
|
if (calls.phase === "active" && voice.status === "idle") void voice.join();
|
|
5097
5108
|
}, [calls.phase, voice]);
|
|
5109
|
+
useEffect(() => {
|
|
5110
|
+
onStatusChange?.(voice.status);
|
|
5111
|
+
}, [voice.status, onStatusChange]);
|
|
5098
5112
|
const label = labelFor ?? ((id) => `User ${id.slice(0, 6)}`);
|
|
5099
|
-
const
|
|
5113
|
+
const ringingIn = calls.phase === "ringing-in";
|
|
5114
|
+
const visible = calls.phase !== "idle" && !(nativeIncomingUi && ringingIn);
|
|
5100
5115
|
const hangUp = () => {
|
|
5101
5116
|
voice.leave();
|
|
5102
5117
|
calls.reset();
|
|
@@ -5197,28 +5212,50 @@ var styles24 = StyleSheet.create({
|
|
|
5197
5212
|
function useVoipCalls({
|
|
5198
5213
|
voipPush,
|
|
5199
5214
|
callKeep,
|
|
5215
|
+
calls,
|
|
5200
5216
|
onAnswer,
|
|
5201
5217
|
onDecline,
|
|
5202
|
-
appName = "poolse"
|
|
5218
|
+
appName = "poolse",
|
|
5219
|
+
apnsEnvironment,
|
|
5220
|
+
staleAfterMs = 45e3
|
|
5203
5221
|
}) {
|
|
5204
5222
|
const poolse = usePoolse();
|
|
5205
|
-
const
|
|
5206
|
-
const
|
|
5207
|
-
handlers
|
|
5223
|
+
const shown = useRef(/* @__PURE__ */ new Map());
|
|
5224
|
+
const seen = useRef(/* @__PURE__ */ new Set());
|
|
5225
|
+
const handlers = useRef({ onAnswer, onDecline, calls });
|
|
5226
|
+
handlers.current = { onAnswer, onDecline, calls };
|
|
5227
|
+
const staleRef = useRef(staleAfterMs);
|
|
5228
|
+
staleRef.current = staleAfterMs;
|
|
5229
|
+
const dismiss = useRef((callId) => {
|
|
5230
|
+
if (!shown.current.has(callId)) return;
|
|
5231
|
+
shown.current.delete(callId);
|
|
5232
|
+
try {
|
|
5233
|
+
callKeep.endCall(callId);
|
|
5234
|
+
} catch {
|
|
5235
|
+
}
|
|
5236
|
+
});
|
|
5237
|
+
const present = useRef((call, sentAt) => {
|
|
5238
|
+
if (seen.current.has(call.callId)) return;
|
|
5239
|
+
if (typeof sentAt === "number" && Date.now() - sentAt > staleRef.current) return;
|
|
5240
|
+
seen.current.add(call.callId);
|
|
5241
|
+
shown.current.set(call.callId, call);
|
|
5242
|
+
callKeep.displayIncomingCall(call.callId, call.callerUserId, call.callerName, "generic", false);
|
|
5243
|
+
});
|
|
5208
5244
|
useEffect(() => {
|
|
5209
5245
|
let cancelled = false;
|
|
5210
|
-
const
|
|
5246
|
+
const fromPush = (payload) => {
|
|
5211
5247
|
const callId = payload.call_id;
|
|
5212
5248
|
const conversationId = payload.conversation_id;
|
|
5213
5249
|
if (!callId || !conversationId) return;
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5250
|
+
present.current(
|
|
5251
|
+
{
|
|
5252
|
+
callId,
|
|
5253
|
+
conversationId,
|
|
5254
|
+
callerUserId: payload.caller_user_id ?? "",
|
|
5255
|
+
callerName: payload.caller_name ?? "Incoming call"
|
|
5256
|
+
},
|
|
5257
|
+
payload.sent_at
|
|
5258
|
+
);
|
|
5222
5259
|
};
|
|
5223
5260
|
void callKeep.setup({
|
|
5224
5261
|
ios: { appName },
|
|
@@ -5233,27 +5270,44 @@ function useVoipCalls({
|
|
|
5233
5270
|
});
|
|
5234
5271
|
voipPush.addEventListener("register", ((token) => {
|
|
5235
5272
|
if (cancelled || !token) return;
|
|
5236
|
-
void poolse.devices.register({
|
|
5273
|
+
void poolse.devices.register({
|
|
5274
|
+
token,
|
|
5275
|
+
platform: "ios",
|
|
5276
|
+
environment: apnsEnvironment ?? guessApnsEnvironment()
|
|
5277
|
+
}).catch(() => {
|
|
5237
5278
|
});
|
|
5238
5279
|
}));
|
|
5239
5280
|
voipPush.addEventListener("notification", ((payload) => {
|
|
5240
|
-
|
|
5281
|
+
fromPush(payload);
|
|
5241
5282
|
}));
|
|
5242
5283
|
voipPush.addEventListener("didLoadWithEvents", ((events) => {
|
|
5243
5284
|
for (const event of events ?? []) {
|
|
5244
5285
|
if (event.name === "RNVoipPushRemoteNotificationReceivedEvent") {
|
|
5245
|
-
|
|
5286
|
+
fromPush(event.data);
|
|
5246
5287
|
}
|
|
5247
5288
|
}
|
|
5248
5289
|
}));
|
|
5249
5290
|
callKeep.addEventListener("answerCall", (({ callUUID }) => {
|
|
5250
|
-
const call =
|
|
5251
|
-
if (call)
|
|
5291
|
+
const call = shown.current.get(callUUID);
|
|
5292
|
+
if (!call) return;
|
|
5293
|
+
shown.current.delete(callUUID);
|
|
5294
|
+
void handlers.current.calls?.accept({
|
|
5295
|
+
callId: call.callId,
|
|
5296
|
+
conversationId: call.conversationId,
|
|
5297
|
+
callerUserId: call.callerUserId
|
|
5298
|
+
});
|
|
5299
|
+
handlers.current.onAnswer?.(call);
|
|
5252
5300
|
}));
|
|
5253
5301
|
callKeep.addEventListener("endCall", (({ callUUID }) => {
|
|
5254
|
-
const call =
|
|
5255
|
-
|
|
5256
|
-
if (call)
|
|
5302
|
+
const call = shown.current.get(callUUID);
|
|
5303
|
+
shown.current.delete(callUUID);
|
|
5304
|
+
if (!call) return;
|
|
5305
|
+
void handlers.current.calls?.decline({
|
|
5306
|
+
callId: call.callId,
|
|
5307
|
+
conversationId: call.conversationId,
|
|
5308
|
+
callerUserId: call.callerUserId
|
|
5309
|
+
});
|
|
5310
|
+
handlers.current.onDecline?.(call);
|
|
5257
5311
|
}));
|
|
5258
5312
|
voipPush.registerVoipToken();
|
|
5259
5313
|
return () => {
|
|
@@ -5264,9 +5318,25 @@ function useVoipCalls({
|
|
|
5264
5318
|
callKeep.removeEventListener("answerCall");
|
|
5265
5319
|
callKeep.removeEventListener("endCall");
|
|
5266
5320
|
};
|
|
5267
|
-
}, [poolse, voipPush, callKeep, appName]);
|
|
5321
|
+
}, [poolse, voipPush, callKeep, appName, apnsEnvironment]);
|
|
5322
|
+
const incoming = calls?.incoming ?? null;
|
|
5323
|
+
const phase = calls?.phase;
|
|
5324
|
+
useEffect(() => {
|
|
5325
|
+
if (phase === "ringing-in" && incoming) {
|
|
5326
|
+
present.current({
|
|
5327
|
+
callId: incoming.callId,
|
|
5328
|
+
conversationId: incoming.conversationId,
|
|
5329
|
+
callerUserId: incoming.callerUserId,
|
|
5330
|
+
callerName: incoming.callerUserId
|
|
5331
|
+
});
|
|
5332
|
+
}
|
|
5333
|
+
}, [phase, incoming]);
|
|
5334
|
+
useEffect(() => {
|
|
5335
|
+
if (phase === "ringing-in" || phase === "ringing-out") return;
|
|
5336
|
+
for (const callId of [...shown.current.keys()]) dismiss.current(callId);
|
|
5337
|
+
}, [phase]);
|
|
5268
5338
|
}
|
|
5269
|
-
function
|
|
5339
|
+
function guessApnsEnvironment() {
|
|
5270
5340
|
return __DEV__ ? "sandbox" : "production";
|
|
5271
5341
|
}
|
|
5272
5342
|
|