@purplecallio/react-native 0.1.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/README.md +266 -0
- package/dist/components/VideoView.d.ts +31 -0
- package/dist/components/VideoView.d.ts.map +1 -0
- package/dist/components/VideoView.js +45 -0
- package/dist/components/VideoView.js.map +1 -0
- package/dist/context.d.ts +79 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +247 -0
- package/dist/context.js.map +1 -0
- package/dist/errors.d.ts +13 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +24 -0
- package/dist/errors.js.map +1 -0
- package/dist/hooks.d.ts +53 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +98 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/optional/inCallManager.d.ts +46 -0
- package/dist/optional/inCallManager.d.ts.map +1 -0
- package/dist/optional/inCallManager.js +79 -0
- package/dist/optional/inCallManager.js.map +1 -0
- package/dist/optional/netInfo.d.ts +38 -0
- package/dist/optional/netInfo.d.ts.map +1 -0
- package/dist/optional/netInfo.js +69 -0
- package/dist/optional/netInfo.js.map +1 -0
- package/dist/permissions.d.ts +29 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +41 -0
- package/dist/permissions.js.map +1 -0
- package/package.json +65 -0
package/README.md
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# @purplecallio/react-native
|
|
2
|
+
|
|
3
|
+
Official React Native components and hooks for [PurpleCallio](https://purplecallio.com), a
|
|
4
|
+
developer-first real-time communication platform for integrating voice, video and WebRTC
|
|
5
|
+
communication into applications.
|
|
6
|
+
|
|
7
|
+
This package is a **framework adapter**, not a WebRTC reimplementation: it wraps
|
|
8
|
+
[`@purplecallio/sdk`](../sdk)'s headless `PurpleCallioMeeting` engine — used here completely
|
|
9
|
+
unmodified — with the things that are genuinely different about React Native: runtime
|
|
10
|
+
permissions, app lifecycle, native video rendering, camera switching, and call audio routing.
|
|
11
|
+
The actual signaling/WebRTC logic all lives in `@purplecallio/sdk`, running against
|
|
12
|
+
[`react-native-webrtc`](https://github.com/jitsi/react-native-webrtc)'s polyfilled globals.
|
|
13
|
+
|
|
14
|
+
## How this works
|
|
15
|
+
|
|
16
|
+
`PurpleCallioMeeting` is written directly against bare global WebRTC APIs
|
|
17
|
+
(`navigator.mediaDevices.getUserMedia`, `RTCPeerConnection`, `MediaStream`, ...) — it doesn't
|
|
18
|
+
import them from anywhere, it assumes they exist as globals. `react-native-webrtc`'s
|
|
19
|
+
`registerGlobals()` installs native-backed implementations of exactly those globals (the same
|
|
20
|
+
pattern used by LiveKit, Twilio Video, Daily, etc.), so the same engine that powers
|
|
21
|
+
`@purplecallio/react` on web runs unmodified on React Native.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npm install @purplecallio/react-native @purplecallio/sdk react-native-webrtc
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Optional, but recommended for production call UX:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
npm install react-native-incall-manager @react-native-community/netinfo
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 1. `react-native-webrtc` setup
|
|
36
|
+
|
|
37
|
+
Follow [react-native-webrtc's installation guide](https://github.com/jitsi/react-native-webrtc)
|
|
38
|
+
for your RN version (autolinking + a `pod install` on iOS). Then, **once, at your app's entry
|
|
39
|
+
point** (e.g. `index.js`, before anything else touches WebRTC):
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
import { registerGlobals } from 'react-native-webrtc';
|
|
43
|
+
registerGlobals();
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This installs `RTCPeerConnection`, `mediaDevices.getUserMedia`, `MediaStream`,
|
|
47
|
+
`RTCSessionDescription`, `RTCIceCandidate`, etc. as globals — without this call,
|
|
48
|
+
`@purplecallio/sdk`'s engine has nothing to run against on React Native.
|
|
49
|
+
|
|
50
|
+
### 2. iOS — Info.plist
|
|
51
|
+
|
|
52
|
+
iOS prompts for camera/microphone access automatically the first time `getUserMedia()` is
|
|
53
|
+
called, but only if your `Info.plist` declares why:
|
|
54
|
+
|
|
55
|
+
```xml
|
|
56
|
+
<key>NSCameraUsageDescription</key>
|
|
57
|
+
<string>Used for video calls</string>
|
|
58
|
+
<key>NSMicrophoneUsageDescription</key>
|
|
59
|
+
<string>Used for audio in calls</string>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
This is app-level native configuration this package cannot inject for you — without these keys,
|
|
63
|
+
`getUserMedia()` will reject at runtime instead of prompting.
|
|
64
|
+
|
|
65
|
+
### 3. Android — AndroidManifest.xml
|
|
66
|
+
|
|
67
|
+
Android requires an explicit *runtime* permission request (handled for you by
|
|
68
|
+
`requestMediaPermissions()` / the provider, see below) on top of declaring the permissions:
|
|
69
|
+
|
|
70
|
+
```xml
|
|
71
|
+
<uses-permission android:name="android.permission.CAMERA" />
|
|
72
|
+
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
|
73
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
74
|
+
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
|
75
|
+
<uses-permission android:name="android.permission.BLUETOOTH" />
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 4. Optional: `react-native-incall-manager`
|
|
79
|
+
|
|
80
|
+
`react-native-webrtc` doesn't manage call audio routing (speaker/earpiece/Bluetooth) on its
|
|
81
|
+
own. If `react-native-incall-manager` is installed, `<PurpleCallioProvider>` automatically calls
|
|
82
|
+
its `start()`/`stop()` on `join()`/`leave()`, and `setSpeakerphoneOn()` becomes functional.
|
|
83
|
+
**If it isn't installed, nothing crashes** — audio routing is simply left to the OS default, and
|
|
84
|
+
a one-time console warning is logged the first time it would have been used.
|
|
85
|
+
|
|
86
|
+
### 5. Optional: `@react-native-community/netinfo`
|
|
87
|
+
|
|
88
|
+
If installed, `useNetworkState()` surfaces live connectivity info (`isConnected`,
|
|
89
|
+
`isInternetReachable`, `type`) for your own "you're offline" UI. If it isn't installed,
|
|
90
|
+
`useNetworkState()` returns `{ supported: false, isConnected: null, isInternetReachable: null,
|
|
91
|
+
type: null }` and logs a one-time console warning the first time it's used.
|
|
92
|
+
|
|
93
|
+
## Usage
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
import React, { useCallback } from 'react';
|
|
97
|
+
import { View, Button, Text } from 'react-native';
|
|
98
|
+
import {
|
|
99
|
+
PurpleCallioProvider,
|
|
100
|
+
PurpleCallioVideoView,
|
|
101
|
+
useMeeting,
|
|
102
|
+
useParticipants,
|
|
103
|
+
useConnection,
|
|
104
|
+
useNetworkState,
|
|
105
|
+
} from '@purplecallio/react-native';
|
|
106
|
+
|
|
107
|
+
// `token` comes from YOUR OWN backend (a per-participant call token it
|
|
108
|
+
// mints via @purplecallio/sdk's server-side PurpleCallioClient) — never an
|
|
109
|
+
// API key, and never something this package or your client app generates.
|
|
110
|
+
function CallScreen({ token, callId, signalUrl }: { token: string; callId: string; signalUrl: string }) {
|
|
111
|
+
return (
|
|
112
|
+
<PurpleCallioProvider token={token} callId={callId} signalUrl={signalUrl} video audio>
|
|
113
|
+
<CallUI />
|
|
114
|
+
</PurpleCallioProvider>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function CallUI() {
|
|
119
|
+
const {
|
|
120
|
+
join,
|
|
121
|
+
leave,
|
|
122
|
+
localStream,
|
|
123
|
+
remoteStream,
|
|
124
|
+
media,
|
|
125
|
+
toggleCamera,
|
|
126
|
+
toggleMicrophone,
|
|
127
|
+
switchCamera,
|
|
128
|
+
setSpeakerphoneOn,
|
|
129
|
+
} = useMeeting();
|
|
130
|
+
const participants = useParticipants();
|
|
131
|
+
const connectionState = useConnection();
|
|
132
|
+
const network = useNetworkState();
|
|
133
|
+
|
|
134
|
+
const handleJoin = useCallback(async () => {
|
|
135
|
+
// <PurpleCallioProvider> already calls requestMediaPermissions()
|
|
136
|
+
// automatically before join() for you. Calling it earlier yourself
|
|
137
|
+
// (e.g. on a "grant permissions" screen before showing the "Join"
|
|
138
|
+
// button) is also fine and often better UX.
|
|
139
|
+
await join();
|
|
140
|
+
}, [join]);
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<View style={{ flex: 1 }}>
|
|
144
|
+
{network.supported && network.isConnected === false && <Text>You're offline</Text>}
|
|
145
|
+
<Text>Connection: {connectionState}</Text>
|
|
146
|
+
<Text>Participants: {participants.length}</Text>
|
|
147
|
+
|
|
148
|
+
<PurpleCallioVideoView stream={remoteStream} style={{ flex: 1 }} objectFit="cover" />
|
|
149
|
+
<PurpleCallioVideoView
|
|
150
|
+
stream={localStream}
|
|
151
|
+
style={{ width: 120, height: 160, position: 'absolute', top: 16, right: 16 }}
|
|
152
|
+
objectFit="cover"
|
|
153
|
+
mirror
|
|
154
|
+
/>
|
|
155
|
+
|
|
156
|
+
<Button title="Join" onPress={handleJoin} />
|
|
157
|
+
<Button title={media.camera ? 'Camera off' : 'Camera on'} onPress={toggleCamera} />
|
|
158
|
+
<Button title={media.microphone ? 'Mute' : 'Unmute'} onPress={toggleMicrophone} />
|
|
159
|
+
<Button title="Switch camera" onPress={switchCamera} />
|
|
160
|
+
<Button title="Speaker" onPress={() => setSpeakerphoneOn(true)} />
|
|
161
|
+
<Button title="Leave" onPress={() => leave()} />
|
|
162
|
+
</View>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
See [`examples/CallScreen.tsx`](./examples/CallScreen.tsx) for a fuller, self-contained version
|
|
168
|
+
of this flow.
|
|
169
|
+
|
|
170
|
+
## API
|
|
171
|
+
|
|
172
|
+
- **`<PurpleCallioProvider token callId signalUrl video? audio? iceServers? overrideIceServers?
|
|
173
|
+
pauseVideoInBackground? children>`** — constructs and owns the `PurpleCallioMeeting` engine.
|
|
174
|
+
`pauseVideoInBackground` (default `true`): automatically disables the camera when the app is
|
|
175
|
+
backgrounded (via `AppState`) and restores it on foreground, but only if it was your app — not
|
|
176
|
+
the user — that turned it off. Set to `false` to manage this yourself.
|
|
177
|
+
- **`useMeeting()`** — the full context value: `engine`, `connectionState`, `participants`,
|
|
178
|
+
`media`, `localStream`, `remoteStream`, `permissionsGranted`, `join()`, `leave()`,
|
|
179
|
+
`toggleCamera()`, `toggleMicrophone()`, `enableCamera()`/`disableCamera()`,
|
|
180
|
+
`enableMicrophone()`/`disableMicrophone()`, `screenShare` (see Known Limitations),
|
|
181
|
+
`switchCamera()`, `setSpeakerphoneOn()`.
|
|
182
|
+
- **`useParticipants()`** / **`useParticipant(id)`** / **`useConnection()`** — same names and
|
|
183
|
+
meaning as `@purplecallio/react`, for cross-platform consistency.
|
|
184
|
+
- **`useDevices()`** — `{ audioInputs, videoInputs, audioOutputs, loading, refresh }` from
|
|
185
|
+
`react-native-webrtc`'s `mediaDevices.enumerateDevices()`. `audioOutputs` is always `[]` — there
|
|
186
|
+
is no meaningful `audiooutput` *device* to select on mobile the way there is on web; that's an
|
|
187
|
+
OS-level speaker/earpiece/Bluetooth routing concern. Use `switchCamera()` (front/back camera)
|
|
188
|
+
and `setSpeakerphoneOn()` (speaker vs. earpiece) instead.
|
|
189
|
+
- **`useNetworkState()`** — `{ supported, isConnected, isInternetReachable, type }` from the
|
|
190
|
+
optional `@react-native-community/netinfo` peer dependency. Does **not** drive any
|
|
191
|
+
reconnection logic — purely informational, for your own UI.
|
|
192
|
+
- **`<PurpleCallioVideoView stream={MediaStream | null} objectFit="cover"|"contain" mirror?
|
|
193
|
+
style? />`** — renders a `MediaStream` via `react-native-webrtc`'s `RTCView` (which takes
|
|
194
|
+
`stream.toURL()`, not `srcObject` like web's `<video>`). Renders an empty placeholder `View`
|
|
195
|
+
when `stream` is `null`/`undefined`.
|
|
196
|
+
- **`requestMediaPermissions({ audio?, video? })`** — Android: requests `RECORD_AUDIO`/`CAMERA`
|
|
197
|
+
via `PermissionsAndroid.requestMultiple`. iOS: no-op that resolves `true` (see above).
|
|
198
|
+
`<PurpleCallioProvider>` calls this automatically before `join()`.
|
|
199
|
+
- **`switchCamera()`** — swaps the front/back camera on the live local video track
|
|
200
|
+
(`react-native-webrtc`'s native `_switchCamera()`). This is a mobile-only concept; there is no
|
|
201
|
+
equivalent in `@purplecallio/react`.
|
|
202
|
+
- **`setSpeakerphoneOn(enabled)`** — forces call audio to the loudspeaker or the earpiece/default
|
|
203
|
+
route, via the optional `react-native-incall-manager` peer dependency.
|
|
204
|
+
- **`PurpleCallioUnsupportedFeatureError`** — thrown by `screenShare.start()` (see below).
|
|
205
|
+
|
|
206
|
+
## Known Limitations
|
|
207
|
+
|
|
208
|
+
### Screen sharing is not implemented on React Native
|
|
209
|
+
|
|
210
|
+
Mobile screen capture requires OS-level native integration entirely different from web's
|
|
211
|
+
`getDisplayMedia`: iOS needs a **Broadcast Upload Extension** target in your Xcode project;
|
|
212
|
+
Android needs a foreground **`MediaProjection`** service. That's real native-project scaffolding
|
|
213
|
+
outside the scope of a pure JS/TS adapter package.
|
|
214
|
+
|
|
215
|
+
Calling `screenShare.start()` (from `useMeeting()`) always rejects with a
|
|
216
|
+
`PurpleCallioUnsupportedFeatureError`, and `screenShare.isActive()` always returns `false`. This
|
|
217
|
+
is not silently no-op'd, and it is not "coming soon" — it is a real, currently-unimplemented gap.
|
|
218
|
+
If your app needs mobile screen sharing today, you'll need to build the native extension/service
|
|
219
|
+
yourself and integrate it directly with `@purplecallio/sdk`'s lower-level `screenShare` controller
|
|
220
|
+
in `packages/sdk`, or track this package for future support.
|
|
221
|
+
|
|
222
|
+
### The `'reconnecting'` connection state
|
|
223
|
+
|
|
224
|
+
`ConnectionState` includes `'reconnecting'`, but the current core engine
|
|
225
|
+
(`packages/sdk/src/meeting/meeting.ts`) never actually sets it — a `reconnected` server event
|
|
226
|
+
sets the state directly back to `'connected'`. This package deliberately does not paper over
|
|
227
|
+
that by inventing its own reconnection state machine on top of the engine (that would duplicate
|
|
228
|
+
core-SDK logic and could drift from what actually happened on the wire). `connectionState`
|
|
229
|
+
faithfully passes through whatever `@purplecallio/sdk` reports today. If/when the core engine
|
|
230
|
+
starts emitting `'reconnecting'`, it will show up here automatically, with no changes needed in
|
|
231
|
+
this package.
|
|
232
|
+
|
|
233
|
+
### `useNetworkState()` doesn't drive reconnection
|
|
234
|
+
|
|
235
|
+
It's a read-only signal for your own UI (e.g. an offline banner). Actual reconnection behavior
|
|
236
|
+
is entirely up to `@purplecallio/sdk`'s signaling transport.
|
|
237
|
+
|
|
238
|
+
## Testing scope (what was actually verified)
|
|
239
|
+
|
|
240
|
+
This package was built and unit-tested with `vitest` — `react-native`, `react-native-webrtc`,
|
|
241
|
+
and `@purplecallio/sdk` are all mocked at the module level (see `tests/mocks/` and
|
|
242
|
+
`tests/fake-meeting.ts`), so these run in plain Node with no native RN toolchain, simulator, or
|
|
243
|
+
physical device involved. Coverage includes: permission-request branching (Android vs. iOS,
|
|
244
|
+
granted/denied/"never ask again"), the provider's state wiring against a fake engine (join/leave,
|
|
245
|
+
correct `toggle()` semantics, permission gating before `join()`), `AppState` background/foreground
|
|
246
|
+
camera pause + restore logic, the screen-share rejection behavior, `PurpleCallioVideoView`'s
|
|
247
|
+
`RTCView`/placeholder rendering, the optional InCallManager/NetInfo bridges (both "installed" and
|
|
248
|
+
"not installed" branches, via dependency injection rather than relying on real module presence),
|
|
249
|
+
and the device-enumeration/network-state hooks.
|
|
250
|
+
|
|
251
|
+
There is no Xcode/Android Studio project, simulator, or physical device available in this
|
|
252
|
+
environment, so this has **not** been run in a real React Native app — only unit-tested in Node
|
|
253
|
+
and smoke-tested via `npm pack` + a plain `require()` of the packed tarball (see the build
|
|
254
|
+
process) to confirm the published entry point resolves and exposes the expected exports.
|
|
255
|
+
|
|
256
|
+
## Why `tsconfig.json` includes `"DOM"` in `lib`
|
|
257
|
+
|
|
258
|
+
There's no `window`/`document` in React Native's JS engine, so this package's runtime code never
|
|
259
|
+
uses DOM APIs. However, `@purplecallio/sdk`'s public types (`EngineConfig`, `MeetingSnapshot`,
|
|
260
|
+
etc.) reference a few raw ambient DOM lib types as-is (`MediaStream`, `RTCIceServer`,
|
|
261
|
+
`RTCPeerConnection`) rather than SDK-defined ones. Without `"DOM"` in `lib`, those names simply
|
|
262
|
+
wouldn't resolve during type-checking. Adding `"DOM"` only feeds the TypeScript compiler — it has
|
|
263
|
+
zero effect on what's actually available at runtime on-device (`react-native-webrtc`'s
|
|
264
|
+
`registerGlobals()` is what provides the real implementations). This is the same pragmatic
|
|
265
|
+
approach commonly used around `react-native-webrtc`'s own TypeScript types. `packages/sdk`'s
|
|
266
|
+
types are not modified to work around this.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
export interface PurpleCallioVideoViewProps {
|
|
4
|
+
/**
|
|
5
|
+
* The stream to render — typically `localStream` or a participant's
|
|
6
|
+
* remote stream from `useMeeting()`. When `null`/`undefined`, nothing is
|
|
7
|
+
* rendered (an empty placeholder `View` keeping `style`'s layout box, so
|
|
8
|
+
* surrounding layouts don't jump around while a stream is still loading).
|
|
9
|
+
*/
|
|
10
|
+
stream: MediaStream | null | undefined;
|
|
11
|
+
/** Matches the CSS `object-fit` semantics. Default: 'cover'. */
|
|
12
|
+
objectFit?: 'cover' | 'contain';
|
|
13
|
+
/** Mirror the video horizontally — typically used for the front camera. */
|
|
14
|
+
mirror?: boolean;
|
|
15
|
+
/** z-order among overlapping `PurpleCallioVideoView`s (see react-native-webrtc's `RTCView`). */
|
|
16
|
+
zOrder?: number;
|
|
17
|
+
style?: StyleProp<ViewStyle>;
|
|
18
|
+
testID?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Renders a `MediaStream` on React Native.
|
|
22
|
+
*
|
|
23
|
+
* Unlike web, where a stream is attached via `<video srcObject={stream} />`,
|
|
24
|
+
* `react-native-webrtc` renders via its native `<RTCView streamURL={...} />`
|
|
25
|
+
* component, which takes the stream's `.toURL()` id rather than the stream
|
|
26
|
+
* object itself. This component wraps that difference away so consumers can
|
|
27
|
+
* work with the same `MediaStream` objects `@purplecallio/sdk` already
|
|
28
|
+
* exposes (`localStreamRef`, `remoteStream`, `on('remote.stream', ...)`).
|
|
29
|
+
*/
|
|
30
|
+
export declare function PurpleCallioVideoView({ stream, objectFit, mirror, zOrder, style, testID, }: PurpleCallioVideoViewProps): React.JSX.Element;
|
|
31
|
+
//# sourceMappingURL=VideoView.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoView.d.ts","sourceRoot":"","sources":["../../src/components/VideoView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAGtE,MAAM,WAAW,0BAA0B;IACzC;;;;;OAKG;IACH,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,CAAC;IACvC,gEAAgE;IAChE,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAChC,2EAA2E;IAC3E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gGAAgG;IAChG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,MAAM,EACN,SAAmB,EACnB,MAAc,EACd,MAAM,EACN,KAAK,EACL,MAAM,GACP,EAAE,0BAA0B,qBAwB5B"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PurpleCallioVideoView = PurpleCallioVideoView;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const react_native_1 = require("react-native");
|
|
9
|
+
const react_native_webrtc_1 = require("react-native-webrtc");
|
|
10
|
+
/**
|
|
11
|
+
* Renders a `MediaStream` on React Native.
|
|
12
|
+
*
|
|
13
|
+
* Unlike web, where a stream is attached via `<video srcObject={stream} />`,
|
|
14
|
+
* `react-native-webrtc` renders via its native `<RTCView streamURL={...} />`
|
|
15
|
+
* component, which takes the stream's `.toURL()` id rather than the stream
|
|
16
|
+
* object itself. This component wraps that difference away so consumers can
|
|
17
|
+
* work with the same `MediaStream` objects `@purplecallio/sdk` already
|
|
18
|
+
* exposes (`localStreamRef`, `remoteStream`, `on('remote.stream', ...)`).
|
|
19
|
+
*/
|
|
20
|
+
function PurpleCallioVideoView({ stream, objectFit = 'cover', mirror = false, zOrder, style, testID, }) {
|
|
21
|
+
if (!stream) {
|
|
22
|
+
return react_1.default.createElement(react_native_1.View, { style: [styles.placeholder, style], testID: testID });
|
|
23
|
+
}
|
|
24
|
+
// `stream` is typed as the ambient DOM `MediaStream` (that's what
|
|
25
|
+
// @purplecallio/sdk's public types use — see tsconfig.json's note on the
|
|
26
|
+
// "DOM" lib). At runtime on React Native it is always actually a
|
|
27
|
+
// react-native-webrtc `MediaStream` instance (once `registerGlobals()`
|
|
28
|
+
// has run), which additionally exposes `.toURL()` for `RTCView`. That
|
|
29
|
+
// extra method isn't part of the DOM lib's `MediaStream` shape, hence the
|
|
30
|
+
// cast.
|
|
31
|
+
const streamURL = stream.toURL();
|
|
32
|
+
return (react_1.default.createElement(react_native_webrtc_1.RTCView, { streamURL: streamURL, objectFit: objectFit, mirror: mirror, zOrder: zOrder, style: [styles.video, style], testID: testID }));
|
|
33
|
+
}
|
|
34
|
+
const styles = react_native_1.StyleSheet.create({
|
|
35
|
+
video: {
|
|
36
|
+
width: '100%',
|
|
37
|
+
height: '100%',
|
|
38
|
+
},
|
|
39
|
+
placeholder: {
|
|
40
|
+
width: '100%',
|
|
41
|
+
height: '100%',
|
|
42
|
+
backgroundColor: '#0D1425',
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=VideoView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoView.js","sourceRoot":"","sources":["../../src/components/VideoView.tsx"],"names":[],"mappings":";;;;;AAgCA,sDA+BC;AA/DD,kDAA0B;AAC1B,+CAAsE;AACtE,6DAA8C;AAoB9C;;;;;;;;;GASG;AACH,SAAgB,qBAAqB,CAAC,EACpC,MAAM,EACN,SAAS,GAAG,OAAO,EACnB,MAAM,GAAG,KAAK,EACd,MAAM,EACN,KAAK,EACL,MAAM,GACqB;IAC3B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,8BAAC,mBAAI,IAAC,KAAK,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,GAAI,CAAC;IACtE,CAAC;IAED,kEAAkE;IAClE,yEAAyE;IACzE,iEAAiE;IACjE,uEAAuE;IACvE,sEAAsE;IACtE,0EAA0E;IAC1E,QAAQ;IACR,MAAM,SAAS,GAAI,MAAyC,CAAC,KAAK,EAAE,CAAC;IAErE,OAAO,CACL,8BAAC,6BAAO,IACN,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAC5B,MAAM,EAAE,MAAM,GACd,CACH,CAAC;AACJ,CAAC;AAED,MAAM,MAAM,GAAG,yBAAU,CAAC,MAAM,CAAC;IAC/B,KAAK,EAAE;QACL,KAAK,EAAE,MAAM;QACb,MAAM,EAAE,MAAM;KACf;IACD,WAAW,EAAE;QACX,KAAK,EAAE,MAAM;QACb,MAAM,EAAE,MAAM;QACd,eAAe,EAAE,SAAS;KAC3B;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ConnectionState, Participant, PurpleCallioMeeting } from '@purplecallio/sdk';
|
|
3
|
+
export interface PurpleCallioProviderProps {
|
|
4
|
+
token: string;
|
|
5
|
+
callId: string;
|
|
6
|
+
signalUrl: string;
|
|
7
|
+
/** Default: video on. */
|
|
8
|
+
video?: boolean;
|
|
9
|
+
/** Default: audio on. */
|
|
10
|
+
audio?: boolean;
|
|
11
|
+
/** Custom ICE servers passed through to the engine. */
|
|
12
|
+
iceServers?: RTCIceServer[];
|
|
13
|
+
/** Override the backend by setting this to true — only custom servers are used. */
|
|
14
|
+
overrideIceServers?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Automatically disable the camera when the app is backgrounded and
|
|
17
|
+
* restore it on foreground (video calls shouldn't keep sending camera
|
|
18
|
+
* frames while backgrounded; some app stores flag this too). Default:
|
|
19
|
+
* true. Set to false to manage this yourself.
|
|
20
|
+
*/
|
|
21
|
+
pauseVideoInBackground?: boolean;
|
|
22
|
+
onStateChange?: (state: MeetingContextValue) => void;
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
}
|
|
25
|
+
export interface ScreenShareControls {
|
|
26
|
+
/**
|
|
27
|
+
* Always rejects with {@link PurpleCallioUnsupportedFeatureError} on
|
|
28
|
+
* React Native. Screen sharing requires native platform integration
|
|
29
|
+
* (iOS Broadcast Upload Extension / Android MediaProjection) that this
|
|
30
|
+
* package does not implement. See README "Known Limitations".
|
|
31
|
+
*/
|
|
32
|
+
start: () => Promise<never>;
|
|
33
|
+
/** No-op — screen sharing can never be active on this platform. */
|
|
34
|
+
stop: () => void;
|
|
35
|
+
/** Always returns `false`. */
|
|
36
|
+
isActive: () => boolean;
|
|
37
|
+
}
|
|
38
|
+
export interface MeetingContextValue {
|
|
39
|
+
engine: PurpleCallioMeeting | null;
|
|
40
|
+
callId: string;
|
|
41
|
+
participantId: string | null;
|
|
42
|
+
connectionState: ConnectionState;
|
|
43
|
+
participants: Participant[];
|
|
44
|
+
media: {
|
|
45
|
+
camera: boolean;
|
|
46
|
+
microphone: boolean;
|
|
47
|
+
screenShare: boolean;
|
|
48
|
+
};
|
|
49
|
+
remoteStream: MediaStream | null;
|
|
50
|
+
localStream: MediaStream | null;
|
|
51
|
+
/** `null` until `requestMediaPermissions()` has resolved at least once. */
|
|
52
|
+
permissionsGranted: boolean | null;
|
|
53
|
+
join: () => Promise<void>;
|
|
54
|
+
leave: () => Promise<void>;
|
|
55
|
+
toggleCamera: () => void;
|
|
56
|
+
toggleMicrophone: () => void;
|
|
57
|
+
enableCamera: () => void;
|
|
58
|
+
disableCamera: () => void;
|
|
59
|
+
enableMicrophone: () => void;
|
|
60
|
+
disableMicrophone: () => void;
|
|
61
|
+
screenShare: ScreenShareControls;
|
|
62
|
+
/**
|
|
63
|
+
* Switches between the front and back camera. This is a mobile-only
|
|
64
|
+
* concept with no web equivalent (there is no `switchCamera()` on
|
|
65
|
+
* `@purplecallio/react`) — it operates directly on the local video track
|
|
66
|
+
* via `react-native-webrtc`'s native camera-switch API.
|
|
67
|
+
*/
|
|
68
|
+
switchCamera: () => void;
|
|
69
|
+
/**
|
|
70
|
+
* Force call audio to the loudspeaker (`true`) or the earpiece/default
|
|
71
|
+
* route (`false`). Requires the optional `react-native-incall-manager`
|
|
72
|
+
* peer dependency to be installed — a no-op (with a one-time console
|
|
73
|
+
* warning) otherwise.
|
|
74
|
+
*/
|
|
75
|
+
setSpeakerphoneOn: (enabled: boolean) => void;
|
|
76
|
+
}
|
|
77
|
+
export declare function useMeetingContext(): MeetingContextValue;
|
|
78
|
+
export declare function PurpleCallioProvider({ token, callId, signalUrl, video, audio, iceServers, overrideIceServers, pauseVideoInBackground, onStateChange, children, }: PurpleCallioProviderProps): React.JSX.Element;
|
|
79
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AAGf,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAMtF,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,yBAAyB;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,yBAAyB;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,uDAAuD;IACvD,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;IAC5B,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACrD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5B,mEAAmE;IACnE,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,EAAE,eAAe,CAAC;IACjC,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,KAAK,EAAE;QACL,MAAM,EAAE,OAAO,CAAC;QAChB,UAAU,EAAE,OAAO,CAAC;QACpB,WAAW,EAAE,OAAO,CAAC;KACtB,CAAC;IACF,YAAY,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,2EAA2E;IAC3E,kBAAkB,EAAE,OAAO,GAAG,IAAI,CAAC;IACnC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,WAAW,EAAE,mBAAmB,CAAC;IACjC;;;;;OAKG;IACH,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB;;;;;OAKG;IACH,iBAAiB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CAC/C;AAID,wBAAgB,iBAAiB,IAAI,mBAAmB,CAMvD;AAOD,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,MAAM,EACN,SAAS,EACT,KAAY,EACZ,KAAY,EACZ,UAAU,EACV,kBAAkB,EAClB,sBAA6B,EAC7B,aAAa,EACb,QAAQ,GACT,EAAE,yBAAyB,qBAuN3B"}
|