@poolse/react-native 2.15.2 → 2.16.1

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 CHANGED
@@ -4,6 +4,40 @@ 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.16.1] — 2026-08-24
8
+
9
+ ### Fixed
10
+
11
+ - `RingToneModule` declared its tone argument optional, which made the
12
+ real `react-native-incall-manager` unassignable to it — function
13
+ parameters are contravariant, so a signature that demands an argument
14
+ cannot stand in for one that does not.
15
+
16
+ ## [2.16.0] — 2026-08-24
17
+
18
+ ### Added
19
+
20
+ - **Ring tones.** `CallScreen` takes an optional `inCallManager` module
21
+ and plays ringback while an outbound call rings, and a ringtone for an
22
+ inbound one — but only when it is actually showing the incoming UI,
23
+ since CallKit already rings and a second tone underneath is noise.
24
+ A silent outbound call is indistinguishable from one that failed to
25
+ place at all.
26
+
27
+ ### Changed
28
+
29
+ - **Media opens while the call rings, not after it is answered.** The
30
+ caller used to stare at a blank screen for the whole ring and then
31
+ watch their own preview pop in a second late — and the microphone was
32
+ not open either, so the first word of every call was clipped. The room
33
+ is now joined on `ringing-out`, so it is warm the moment the far side
34
+ picks up. Safe for the alone-detection: a caller in an empty room has
35
+ never seen a peer, which is the guard that already exists.
36
+ - **Tapping the call screen toggles the controls.** It only ever showed
37
+ them before, and a timer took them away — so there was no way to
38
+ deliberately clear the chrome and look at the video. The idle timer
39
+ remains, on top rather than as the only way they disappear.
40
+
7
41
  ## [2.15.2] — 2026-08-23
8
42
 
9
43
  ### Fixed
package/dist/index.cjs CHANGED
@@ -5846,6 +5846,7 @@ function CallScreen({
5846
5846
  onStatusChange,
5847
5847
  onTransportChange,
5848
5848
  onVideoChange,
5849
+ inCallManager,
5849
5850
  nativeIncomingUi
5850
5851
  }) {
5851
5852
  const insets = reactNativeSafeAreaContext.useSafeAreaInsets();
@@ -5856,7 +5857,7 @@ function CallScreen({
5856
5857
  () => meshAvailable && webrtc ? { webrtc: createNativeWebRtcAdapter(webrtc) } : {},
5857
5858
  [meshAvailable, webrtc]
5858
5859
  );
5859
- const conversationId = calls.phase === "active" ? calls.activeConversationId : null;
5860
+ const conversationId = calls.phase === "active" ? calls.activeConversationId : calls.phase === "ringing-out" ? calls.outgoing?.conversationId ?? null : null;
5860
5861
  const isVideoCall = calls.media === "video";
5861
5862
  const sfu = react$1.useCallRoom(livekit ? conversationId : null, {
5862
5863
  livekit,
@@ -5867,7 +5868,8 @@ function CallScreen({
5867
5868
  const status = usingSfu ? toVoiceStatus(sfu.status) : mesh.status;
5868
5869
  const connected = calls.phase === "active" && status === "connected";
5869
5870
  react.useEffect(() => {
5870
- if (calls.phase !== "active" || !conversationId) return;
5871
+ const wants = calls.phase === "active" || calls.phase === "ringing-out";
5872
+ if (!wants || !conversationId) return;
5871
5873
  let cancelled = false;
5872
5874
  void (async () => {
5873
5875
  const joined = livekit ? await sfu.join() : false;
@@ -5895,6 +5897,23 @@ function CallScreen({
5895
5897
  livekitReactNative
5896
5898
  });
5897
5899
  const [routesOpen, setRoutesOpen] = react.useState(false);
5900
+ const showingIncoming = calls.phase === "ringing-in" && !nativeIncomingUi;
5901
+ react.useEffect(() => {
5902
+ const ringing = calls.phase === "ringing-out";
5903
+ if (!ringing && !showingIncoming) return;
5904
+ try {
5905
+ if (ringing) inCallManager?.startRingback?.("_DEFAULT_");
5906
+ else inCallManager?.startRingtone?.("_DEFAULT_");
5907
+ } catch {
5908
+ }
5909
+ return () => {
5910
+ try {
5911
+ inCallManager?.stopRingback?.();
5912
+ inCallManager?.stopRingtone?.();
5913
+ } catch {
5914
+ }
5915
+ };
5916
+ }, [calls.phase, showingIncoming, inCallManager]);
5898
5917
  const [chromeVisible, setChromeVisible] = react.useState(true);
5899
5918
  const [lastWake, setLastWake] = react.useState(0);
5900
5919
  const chromeHides = connected && isVideoCall;
@@ -5905,6 +5924,13 @@ function CallScreen({
5905
5924
  return () => clearTimeout(timer);
5906
5925
  }, [chromeHides, lastWake]);
5907
5926
  const wakeChrome = react.useCallback(() => setLastWake(Date.now()), []);
5927
+ const toggleChrome = react.useCallback(() => {
5928
+ setChromeVisible((visible2) => {
5929
+ if (visible2) return false;
5930
+ setLastWake(Date.now());
5931
+ return true;
5932
+ });
5933
+ }, []);
5908
5934
  const withWake = react.useCallback(
5909
5935
  (fn) => () => {
5910
5936
  wakeChrome();
@@ -5963,7 +5989,7 @@ function CallScreen({
5963
5989
  onRequestClose: hangUp,
5964
5990
  children: [
5965
5991
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.StatusBar, { barStyle: "light-content" }),
5966
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { style: styles30.screen, onPress: wakeChrome, accessible: false, children: [
5992
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { style: styles30.screen, onPress: toggleChrome, accessible: false, children: [
5967
5993
  showStage ? /* @__PURE__ */ jsxRuntime.jsx(
5968
5994
  CallVideoGrid,
5969
5995
  {