@poolse/react-native 2.17.1 → 2.19.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 +44 -0
- package/dist/index.cjs +62 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -26
- package/dist/index.d.ts +0 -26
- package/dist/index.js +62 -22
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,50 @@ All notable changes to `@poolse/react-native` are documented here.
|
|
|
4
4
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
|
|
5
5
|
versions follow [semver](https://semver.org).
|
|
6
6
|
|
|
7
|
+
## [2.19.0] — 2026-08-25
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **The end of a call now travels by push, like its start.** A device
|
|
12
|
+
woken by a VoIP push is ringing on a lock screen with a socket that
|
|
13
|
+
may not be connected, so the broadcast that ends a call reaches
|
|
14
|
+
nothing there. `useVoipCalls` now understands a teardown push
|
|
15
|
+
(`event: "ended"`) and tears the ring down instead of starting one.
|
|
16
|
+
Needs a server that sends them, and an AppDelegate that reports the
|
|
17
|
+
call before ending it — see the sample's `plugins/withVoipPush.js`.
|
|
18
|
+
|
|
19
|
+
- `readPush()` — exported and pure, so what a push MEANS can be tested
|
|
20
|
+
apart from what CallKit does about it. A missing `event` reads as a
|
|
21
|
+
ring, which is what servers predating the field send.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- **A ring that arrived by push is now registered with `useCalls`**, so
|
|
26
|
+
every signal that ends a call can match it. Previously only CallKit
|
|
27
|
+
knew about it. See `@poolse/react` 2.19.0.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- A finished call — declined, busy, no answer — closes itself after a
|
|
32
|
+
short pause instead of waiting behind a Close button.
|
|
33
|
+
|
|
34
|
+
## [2.18.0] — 2026-08-24
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- **The device was often never registered for VoIP push, so no call
|
|
39
|
+
ever rang a backgrounded or closed app.** `useVoipCalls` registered
|
|
40
|
+
the PushKit token the instant iOS issued it — which is moments after
|
|
41
|
+
launch, and routinely before the host app has a session. Registering
|
|
42
|
+
is an authenticated call, so it 401'd, and the error was swallowed as
|
|
43
|
+
"offline at launch". No device row meant poolse had nothing to push
|
|
44
|
+
to.
|
|
45
|
+
|
|
46
|
+
The token is now held until a signed-in user exists, and registered
|
|
47
|
+
when both are present — in whichever order they arrive. A failure no
|
|
48
|
+
longer marks the pair as done, so it retries rather than giving up
|
|
49
|
+
until the next launch.
|
|
50
|
+
|
|
7
51
|
## [2.17.1] — 2026-08-24
|
|
8
52
|
|
|
9
53
|
### Fixed
|
package/dist/index.cjs
CHANGED
|
@@ -5836,6 +5836,10 @@ function format(total) {
|
|
|
5836
5836
|
return `${hours > 0 ? `${hours}:` : ""}${mm}:${String(seconds).padStart(2, "0")}`;
|
|
5837
5837
|
}
|
|
5838
5838
|
var CHROME_IDLE_MS = 4500;
|
|
5839
|
+
var ENDED_LINGER_MS = 2500;
|
|
5840
|
+
function isOver(phase) {
|
|
5841
|
+
return phase === "declined" || phase === "busy" || phase === "timed-out";
|
|
5842
|
+
}
|
|
5839
5843
|
function CallScreen({
|
|
5840
5844
|
userId,
|
|
5841
5845
|
labelFor,
|
|
@@ -5951,6 +5955,11 @@ function CallScreen({
|
|
|
5951
5955
|
endedOnEmpty.current = true;
|
|
5952
5956
|
hangUp();
|
|
5953
5957
|
}, [alone, calls.phase, hangUp]);
|
|
5958
|
+
react.useEffect(() => {
|
|
5959
|
+
if (!isOver(calls.phase)) return;
|
|
5960
|
+
const timer = setTimeout(calls.reset, ENDED_LINGER_MS);
|
|
5961
|
+
return () => clearTimeout(timer);
|
|
5962
|
+
}, [calls.phase, calls.reset]);
|
|
5954
5963
|
const micOn = usingSfu ? sfu.micEnabled : !mesh.muted;
|
|
5955
5964
|
const toggleMic = react.useCallback(() => {
|
|
5956
5965
|
if (usingSfu) void sfu.toggleMic();
|
|
@@ -5964,7 +5973,7 @@ function CallScreen({
|
|
|
5964
5973
|
const label = react.useMemo(() => labelFor ?? ((id) => `User ${id.slice(0, 6)}`), [labelFor]);
|
|
5965
5974
|
const ringingIn = calls.phase === "ringing-in";
|
|
5966
5975
|
const ringingOut = calls.phase === "ringing-out";
|
|
5967
|
-
const ended = calls.phase
|
|
5976
|
+
const ended = isOver(calls.phase);
|
|
5968
5977
|
const visible = calls.phase !== "idle" && !(nativeIncomingUi && ringingIn);
|
|
5969
5978
|
const duration = useCallDuration(connected);
|
|
5970
5979
|
const participants = usingSfu ? sfu.participants : [];
|
|
@@ -6206,6 +6215,26 @@ function isStalePush(sentAt, now, staleAfterMs) {
|
|
|
6206
6215
|
if (age < 0) return false;
|
|
6207
6216
|
return age > staleAfterMs;
|
|
6208
6217
|
}
|
|
6218
|
+
function readPush(payload) {
|
|
6219
|
+
const callId = payload.call_id;
|
|
6220
|
+
if (!callId) return { kind: "ignore" };
|
|
6221
|
+
if (payload.event === "ended") return { kind: "teardown", callId };
|
|
6222
|
+
const conversationId = payload.conversation_id;
|
|
6223
|
+
if (!conversationId) return { kind: "ignore" };
|
|
6224
|
+
return {
|
|
6225
|
+
kind: "ring",
|
|
6226
|
+
sentAt: payload.sent_at,
|
|
6227
|
+
call: {
|
|
6228
|
+
callId,
|
|
6229
|
+
conversationId,
|
|
6230
|
+
callerUserId: payload.caller_user_id ?? "",
|
|
6231
|
+
callerName: payload.caller_name ?? "Incoming call",
|
|
6232
|
+
// `has_video` is what older servers sent; `media` supersedes it.
|
|
6233
|
+
// Either saying video means the system UI offers video.
|
|
6234
|
+
media: payload.media === "video" || payload.has_video ? "video" : "audio"
|
|
6235
|
+
}
|
|
6236
|
+
};
|
|
6237
|
+
}
|
|
6209
6238
|
function useVoipCalls({
|
|
6210
6239
|
voipPush,
|
|
6211
6240
|
callKeep,
|
|
@@ -6218,6 +6247,9 @@ function useVoipCalls({
|
|
|
6218
6247
|
}) {
|
|
6219
6248
|
const poolse = react$1.usePoolse();
|
|
6220
6249
|
const shown = react.useRef(/* @__PURE__ */ new Map());
|
|
6250
|
+
const [voipToken, setVoipToken] = react.useState(null);
|
|
6251
|
+
const { me } = react$1.useMe();
|
|
6252
|
+
const registered = react.useRef(null);
|
|
6221
6253
|
const handlers = react.useRef({ onAnswer, onDecline, calls });
|
|
6222
6254
|
handlers.current = { onAnswer, onDecline, calls };
|
|
6223
6255
|
const staleRef = react.useRef(staleAfterMs);
|
|
@@ -6257,24 +6289,37 @@ function useVoipCalls({
|
|
|
6257
6289
|
void ready.current.then(done, done);
|
|
6258
6290
|
if (isStalePush(sentAt, Date.now(), staleRef.current)) dismiss.current(call.callId);
|
|
6259
6291
|
});
|
|
6292
|
+
react.useEffect(() => {
|
|
6293
|
+
const userId = me?.id;
|
|
6294
|
+
if (!voipToken || !userId) return;
|
|
6295
|
+
const pair = `${userId}:${voipToken}`;
|
|
6296
|
+
if (registered.current === pair) return;
|
|
6297
|
+
void poolse.devices.register({
|
|
6298
|
+
token: voipToken,
|
|
6299
|
+
platform: "ios",
|
|
6300
|
+
environment: apnsEnvironment ?? guessApnsEnvironment()
|
|
6301
|
+
}).then(() => {
|
|
6302
|
+
registered.current = pair;
|
|
6303
|
+
}).catch(() => {
|
|
6304
|
+
});
|
|
6305
|
+
}, [poolse, voipToken, me?.id, apnsEnvironment]);
|
|
6260
6306
|
react.useEffect(() => {
|
|
6261
6307
|
let cancelled = false;
|
|
6262
6308
|
const fromPush = (payload) => {
|
|
6263
|
-
const
|
|
6264
|
-
|
|
6265
|
-
if (
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
);
|
|
6309
|
+
const intent = readPush(payload);
|
|
6310
|
+
if (intent.kind === "ignore") return;
|
|
6311
|
+
if (intent.kind === "teardown") {
|
|
6312
|
+
dismiss.current(intent.callId);
|
|
6313
|
+
handlers.current.calls?.dropped(intent.callId);
|
|
6314
|
+
return;
|
|
6315
|
+
}
|
|
6316
|
+
present.current(intent.call, intent.sentAt);
|
|
6317
|
+
handlers.current.calls?.ring({
|
|
6318
|
+
callId: intent.call.callId,
|
|
6319
|
+
conversationId: intent.call.conversationId,
|
|
6320
|
+
callerUserId: intent.call.callerUserId,
|
|
6321
|
+
media: intent.call.media
|
|
6322
|
+
});
|
|
6278
6323
|
};
|
|
6279
6324
|
ready.current = callKeep.setup({
|
|
6280
6325
|
ios: { appName },
|
|
@@ -6289,12 +6334,7 @@ function useVoipCalls({
|
|
|
6289
6334
|
});
|
|
6290
6335
|
voipPush.addEventListener("register", ((token) => {
|
|
6291
6336
|
if (cancelled || !token) return;
|
|
6292
|
-
|
|
6293
|
-
token,
|
|
6294
|
-
platform: "ios",
|
|
6295
|
-
environment: apnsEnvironment ?? guessApnsEnvironment()
|
|
6296
|
-
}).catch(() => {
|
|
6297
|
-
});
|
|
6337
|
+
setVoipToken(token);
|
|
6298
6338
|
}));
|
|
6299
6339
|
voipPush.addEventListener("notification", ((payload) => {
|
|
6300
6340
|
fromPush(payload);
|