@desklink/react-native 0.5.4 → 0.5.7

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 CHANGED
@@ -277,8 +277,9 @@ view.
277
277
  network is still down. Renegotiation transients from a restart the session
278
278
  asked for itself are expected, not a new outage: they neither flip the
279
279
  status nor trigger another restart for a short grace window. A picture
280
- that keeps moving holds off the heartbeat watchdog for up to ~20 s, since
281
- the control channel lags the video after an outage.
280
+ that keeps moving holds off the heartbeat watchdog for up to ~20 s from
281
+ the later of the last heartbeat and the picture's return, since the
282
+ control channel lags the video after an outage.
282
283
  - **Roaming keeps the session.** The signalling carrier is not the session:
283
284
  when it closes, or a restart request on it gets no reply within 1.5 s, the
284
285
  session drops that carrier for good (calling its optional `close()`) and
@@ -289,7 +290,10 @@ view.
289
290
  fires. While the loop runs, a session that has shown a picture never reports
290
291
  `failed` or a transport failure; one that never showed a picture reports the
291
292
  first-frame transport failure after 20 s and the loop stops. A new carrier
292
- picks up the same session after a 300 ms grace for the old ICE pair: if the
293
+ picks up the same session after a short grace for the old ICE pair, sized by
294
+ how long the picture has been still: stills under 4 s wait out the blip
295
+ (200 ms), stills past it take one stats beat (50 ms) and ask for the restart
296
+ at once. If the
293
297
  picture advanced during it, there is no restart (and a `reconnecting`
294
298
  session returns to `live`); otherwise the session moves to `reconnecting`
295
299
  and asks for an ICE restart. Failures caused
@@ -474,8 +474,9 @@ class DesktopView(context: Context, appContext: AppContext) : ExpoView(context,
474
474
  private fun fillScale(): Float {
475
475
  // Portrait fits the width unless the app opted into cover; landscape
476
476
  // covers. Either way the fill never goes below the fit or above the zoom cap.
477
- val wide = if (width <= height && !cover) safeWidth() else width.toFloat()
478
- val fill = max(wide / surfaceWidth, (height - insetTop) / surfaceHeight)
477
+ val covers = width > height || cover
478
+ val fill = if (!covers) safeWidth() / surfaceWidth
479
+ else max(width.toFloat() / surfaceWidth, (height - insetTop) / surfaceHeight)
479
480
  return max(fitScale(), min(fill, MAX_SCALE))
480
481
  }
481
482
 
package/package.json CHANGED
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "name": "@desklink/react-native",
3
- "version": "0.5.4",
3
+ "version": "0.5.7",
4
4
  "license": "Apache-2.0",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/umeranjum17/desklink.git",
8
+ "directory": "packages/desktop-client"
9
+ },
5
10
  "description": "Render and drive a desktop session from a React Native app",
6
11
  "main": "index.ts",
7
12
  "types": "index.ts",
@@ -37,6 +42,7 @@
37
42
  },
38
43
  "scripts": {
39
44
  "test:reconnect": "node test/reconnect-flow.mjs",
40
- "test:ios": "node test/ios-flow.mjs"
45
+ "test:ios": "node test/ios-flow.mjs",
46
+ "test:reattach-race": "node test/reattach-grace-race-flow.mjs"
41
47
  }
42
48
  }
@@ -56,7 +56,12 @@ const STALL_AFTER_MS = 1200;
56
56
  * trigger another restart while the window lasts.
57
57
  */
58
58
  const RESTART_GRACE_MS = 2500;
59
- /** How long a moving picture outlasts silent heartbeats: after an outage the data channel lags the video. */
59
+ /**
60
+ * How long a moving picture outlasts silent heartbeats, from the later of the
61
+ * last heartbeat and the picture's return from a freeze: after an outage the
62
+ * data channel lags the video, as SCTP's retransmissions back off (to 15 s,
63
+ * then 31 s, after the first loss).
64
+ */
60
65
  const QUIET_PICTURE_MS = 20_000;
61
66
  /** How long a restart offer may go unanswered before the next attempt. */
62
67
  const RESTART_REPLY_MS = 5000;
@@ -68,8 +73,24 @@ const DIAL_RETRY_MS = 250;
68
73
  const DIAL_TIMEOUT_MS = 5000;
69
74
  /** How long the host holds a session with no carrier (the bridge's `reattachMs`); past it, reopen. */
70
75
  const REATTACH_WINDOW_MS = 30_000;
71
- /** How long a reattached carrier waits for the old ICE pair before asking for a restart. */
72
- const REATTACH_GRACE_MS = 300;
76
+ /**
77
+ * How long a reattached carrier waits for the old ICE pair before asking for
78
+ * a restart — the wait for a brief blip: on an unchanged path the old pair
79
+ * resumes in ~100 ms and only the carrier was missing, so the wait keeps the
80
+ * restart unspent while the picture comes back on its own.
81
+ */
82
+ const REATTACH_GRACE_MS = 200;
83
+ /**
84
+ * A picture still this long is a real outage, not a blip: the old pair is
85
+ * not resuming, so the wait that protects a blip would only cost the recovery
86
+ * its 2 s bar. The line separates the two measured regimes — blips of about
87
+ * a second heal on the old pair in ~100 ms; by 12 s the pair is gone. The
88
+ * picture's own last motion is the clock: a re-dialled carrier's age resets
89
+ * with every refused attempt, so it cannot tell the two apart.
90
+ */
91
+ const REATTACH_GRACE_BLIP_MAX_MS = 4000;
92
+ /** A real outage's wait: one stats beat — enough to see a picture already moving. */
93
+ const REATTACH_GRACE_SETTLE_MS = 50;
73
94
  /**
74
95
  * How long the media path may carry nothing at all before a session that says
75
96
  * `live` stops saying so. The engine's transport state reaches the client over
@@ -372,6 +393,8 @@ export function useDesktopSession(options: DesktopSessionOptions): DesktopSessio
372
393
  const mediaMovingAt = useRef<number | null>(null);
373
394
  /** Whether the picture of an open session has stopped advancing. */
374
395
  const frozen = useRef(false);
396
+ /** When a frozen picture last moved again, or 0 while it has not. */
397
+ const pictureBackAt = useRef(0);
375
398
  /** Renegotiation transients are not a new outage before this time. */
376
399
  const restartGraceUntil = useRef(0);
377
400
  /** ICE-restart attempts spent on the current drop. */
@@ -723,6 +746,7 @@ export function useDesktopSession(options: DesktopSessionOptions): DesktopSessio
723
746
  mediaMovingAt.current = Date.now();
724
747
  if (!frozen.current) return;
725
748
  frozen.current = false;
749
+ pictureBackAt.current = Date.now();
726
750
  // The picture advances again, so the transport can be believed
727
751
  // for this session: live returns without waiting for it to say so.
728
752
  if (statusRef.current === 'reconnecting' || (statusRef.current === 'failed' && heldIceFailureAt.current !== null)) onTransportState('connected', false);
@@ -763,7 +787,14 @@ export function useDesktopSession(options: DesktopSessionOptions): DesktopSessio
763
787
  stallTimer.current = setTimeout(() => checkStallRef.current(), restartGraceUntil.current - Date.now());
764
788
  return;
765
789
  }
766
- if (pictureMoving(STALL_AFTER_MS) && !frozen.current && quietMs < QUIET_PICTURE_MS) {
790
+ // The outage's own silence is not the data channel lagging the
791
+ // picture: that lag starts when the picture comes back. Until the
792
+ // heartbeat catches up, the picture is held to the freeze bar: a
793
+ // still desktop's keepalive frames, a second apart, read up to
794
+ // 1.5 s apart through the stats poll.
795
+ const lagging = pictureBackAt.current > lastPingAt.current;
796
+ const lagMs = Date.now() - Math.max(lastPingAt.current, pictureBackAt.current);
797
+ if (pictureMoving(lagging ? FROZEN_AFTER_MS : STALL_AFTER_MS) && !frozen.current && lagMs < QUIET_PICTURE_MS) {
767
798
  stallTimer.current = setTimeout(() => checkStallRef.current(), STALL_AFTER_MS);
768
799
  return;
769
800
  }
@@ -885,6 +916,7 @@ export function useDesktopSession(options: DesktopSessionOptions): DesktopSessio
885
916
  mediaRef.current = null;
886
917
  mediaMovingAt.current = null;
887
918
  frozen.current = false;
919
+ pictureBackAt.current = 0;
888
920
  opened.current = null;
889
921
  dropCarrier(false);
890
922
  setNativeId(null);
@@ -1154,9 +1186,16 @@ export function useDesktopSession(options: DesktopSessionOptions): DesktopSessio
1154
1186
  void establish(authorization);
1155
1187
  return;
1156
1188
  }
1157
- // The old pair on an unchanged path resumes in ~100 ms: give it a moment before spending a restart.
1189
+ // A picture that has only just stopped may still be riding its
1190
+ // old ICE pair back: on an unchanged path that pair resumes in
1191
+ // ~100 ms, so a brief blip waits that moment out before a restart
1192
+ // is spent. A picture still past the line is a real outage — the
1193
+ // wait shortens to one stats beat, because waiting out a blip
1194
+ // that never comes would cost the recovery its bar.
1158
1195
  const held = opened.current, before = mediaRef.current;
1159
1196
  cancelRestart();
1197
+ const stillMs = mediaMovingAt.current === null ? Infinity : Date.now() - mediaMovingAt.current;
1198
+ const graceMs = stillMs < REATTACH_GRACE_BLIP_MAX_MS ? REATTACH_GRACE_MS : REATTACH_GRACE_SETTLE_MS;
1160
1199
  restartTimer.current = setTimeout(() => {
1161
1200
  restartTimer.current = null;
1162
1201
  void getStats().catch(() => []).then((rows) => {
@@ -1168,9 +1207,14 @@ export function useDesktopSession(options: DesktopSessionOptions): DesktopSessio
1168
1207
  }
1169
1208
  if (statusRef.current !== 'reconnecting') update({ status: 'reconnecting' });
1170
1209
  restartAttempts.current = restartCycles.current = 0;
1210
+ // A transport `disconnected` that landed while the stats read
1211
+ // above was in flight armed `scheduleRestart(RESTART_AFTER_MS)`;
1212
+ // without clearing it first the request here would return early
1213
+ // on the armed timer and the restart would slip by up to 2 s.
1214
+ cancelRestart();
1171
1215
  scheduleRestart(0);
1172
1216
  });
1173
- }, REATTACH_GRACE_MS);
1217
+ }, graceMs);
1174
1218
  })();
1175
1219
  }, [adoptCarrier, cancelReconnect, cancelRestart, discardSession, establish, getStats, onTransportState, scheduleRestart, stopDialing, teardown, update]);
1176
1220