@swiftbrowser/web 0.10.0 → 0.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swiftbrowser/web",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "type": "module",
5
5
  "description": "SwiftBrowser web renderer and dev server: applies render ops from a Swift/Wasm app to the DOM inside an iPhone frame.",
6
6
  "repository": {
@@ -54,10 +54,19 @@ function shown(win: Window, el: HTMLElement): boolean {
54
54
  return win.getComputedStyle(el).visibility !== 'hidden';
55
55
  }
56
56
 
57
+ /** How long a back the browser made waits for the app to pop a level before what it shows is recorded as it is. */
58
+ const DISMISS_WAIT_MS = 1000;
59
+
57
60
  export class BrowserHistory {
58
61
  private readonly win: Window;
59
- /** Levels the latest `popstate` still asks to dismiss, one commit at a time. */
60
- private pendingPops = 0;
62
+ /**
63
+ * A back the browser made, while the app pops to it: the depth the entry
64
+ * names, the depth the latest dismissal started from, and until when the
65
+ * app's pop is waited for. A pop can take more than one commit (a compact
66
+ * split view clears its sidebar selection first, and the detail goes on
67
+ * the next), so a commit that still shows the level is not a new one.
68
+ */
69
+ private dismissing: { target: number; from: number; until: number } | null = null;
61
70
  /** `popstate` events to ignore: the ones our own `history.go` causes. */
62
71
  private ignorePops = 0;
63
72
  /**
@@ -205,6 +214,15 @@ export class BrowserHistory {
205
214
  return top ? Number(top.dataset['sbDepth'] ?? '0') || 0 : 0;
206
215
  }
207
216
 
217
+ /** Dismisses the level above `depth` for the back in progress, and waits for the app's pop. */
218
+ private dismissFrom(depth: number): void {
219
+ const dismissing = this.dismissing;
220
+ if (!dismissing) return;
221
+ dismissing.from = depth;
222
+ dismissing.until = performance.now() + DISMISS_WAIT_MS;
223
+ if (!this.dismissOne()) this.dismissing = null;
224
+ }
225
+
208
226
  /** Asks the app to dismiss one level: the top sheet when nothing is pushed inside it, else the deepest visible stack's top screen. */
209
227
  private dismissOne(): boolean {
210
228
  const sheets = this.sheets();
@@ -282,13 +300,16 @@ export class BrowserHistory {
282
300
  const depth = this.depth();
283
301
  const recorded = this.recorded();
284
302
  const selection = this.selection();
285
- if (this.pendingPops > 0) {
303
+ const dismissing = this.dismissing;
304
+ if (dismissing) {
286
305
  // Dismissing for a `popstate`: the entry already names the target depth.
287
- if (depth > recorded) {
288
- if (!this.dismissOne()) this.pendingPops = 0;
289
- else this.pendingPops -= 1;
290
- } else this.pendingPops = 0;
291
- return;
306
+ if (depth > dismissing.target && performance.now() < dismissing.until) {
307
+ // The latest dismissal landed: the next level. Otherwise the app has not popped yet.
308
+ if (depth < dismissing.from) this.dismissFrom(depth);
309
+ return;
310
+ }
311
+ // Arrived, or the app kept the level (it is then recorded as any other).
312
+ this.dismissing = null;
292
313
  }
293
314
  if (depth > recorded) {
294
315
  // Under our own pending `go`, a push would land relative to wherever the browser is when it runs (and
@@ -326,11 +347,12 @@ export class BrowserHistory {
326
347
  // The browser moved on its own (a swipe, a button): keep painting past its transition.
327
348
  this.renderer.keepRepainting();
328
349
  const depth = this.depth();
350
+ // A move the browser makes replaces any back still in progress.
351
+ this.dismissing = null;
329
352
  if (target.sbDepth < depth) {
330
- // Back: dismiss one level now, the rest as each commit lands.
331
- this.pendingPops = depth - target.sbDepth;
332
- if (!this.dismissOne()) this.pendingPops = 0;
333
- else this.pendingPops -= 1;
353
+ // Back: dismiss one level now, the rest as each pop lands.
354
+ this.dismissing = { target: target.sbDepth, from: depth, until: 0 };
355
+ this.dismissFrom(depth);
334
356
  } else if (target.sbDepth > depth) {
335
357
  // Forward: a screen cannot be re-pushed; return to the entry that matches.
336
358
  this.go(depth - target.sbDepth, depth);