@signalk/app-dock 1.3.0 → 1.3.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.
Files changed (3) hide show
  1. package/README.md +11 -1
  2. package/package.json +1 -1
  3. package/public/dock.js +36 -20
package/README.md CHANGED
@@ -48,13 +48,23 @@ if (window.self !== window.top) {
48
48
  document.addEventListener(
49
49
  'pointerdown',
50
50
  (e) => {
51
- window.top.postMessage({ type: 'signalk-app-dock:pointerdown', x: e.clientX, y: e.clientY }, '*')
51
+ window.top.postMessage(
52
+ {
53
+ type: 'signalk-app-dock:pointerdown',
54
+ x: e.clientX,
55
+ y: e.clientY,
56
+ t: performance.timeOrigin + e.timeStamp
57
+ },
58
+ '*'
59
+ )
52
60
  },
53
61
  { passive: true, capture: true }
54
62
  )
55
63
  }
56
64
  ```
57
65
 
66
+ `t` is when the tap happened. It is optional, but a page that is busy, rendering a radar display for example, can post its messages late, and without `t` the dock can only go by when they arrive, which can split a double-tap in two.
67
+
58
68
  The dock accepts these messages only from a page of the app on screen that it cannot listen to itself, or a frame inside that page, and all they do is count towards a double-tap. The tab goes away once every such page has sent one. A page that loads again starts over: the tab is back until its first tap.
59
69
 
60
70
  ## Install as an app
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signalk/app-dock",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "macOS-style configurable app dock for switching between SignalK webapps on touch screens",
5
5
  "repository": {
6
6
  "url": "https://github.com/SignalK/app-dock"
package/public/dock.js CHANGED
@@ -674,6 +674,19 @@
674
674
  // two taps pair up only within the same one.
675
675
  let lastTapSpace = null
676
676
 
677
+ // Taps are timed by when they happened, not when their handler ran: a page
678
+ // busy rendering can hold a pointerdown back long enough to split a
679
+ // double-tap. Times are on the clock every document in the browser shares,
680
+ // performance.timeOrigin plus the page's own high-resolution time.
681
+ function nowTime() {
682
+ return performance.timeOrigin + performance.now()
683
+ }
684
+
685
+ function eventTime(doc, e) {
686
+ const perf = doc.defaultView && doc.defaultView.performance
687
+ return perf ? perf.timeOrigin + e.timeStamp : nowTime()
688
+ }
689
+
677
690
  function triggerDock() {
678
691
  if (DEBUG) console.log('[Dock] triggerDock called, dockVisible=', dockVisible)
679
692
  if (dockVisible) return
@@ -687,15 +700,14 @@
687
700
 
688
701
  const DEBUG = new URLSearchParams(location.search).has('debugDock')
689
702
 
690
- function handleTap(clientX, clientY, space = null) {
703
+ function handleTap(clientX, clientY, space = null, time = nowTime()) {
691
704
  if (dockVisible) return
692
- const now = Date.now()
693
705
  const dx = clientX - lastTapX
694
706
  const dy = clientY - lastTapY
695
707
  const dist = Math.round(Math.sqrt(dx * dx + dy * dy))
696
708
  const close = dist <= DOUBLE_TAP_SLOP && space === lastTapSpace
697
- const gap = now - lastTapTime
698
- const match = gap < DOUBLE_TAP_MS && close
709
+ const gap = time - lastTapTime
710
+ const match = gap >= 0 && gap < DOUBLE_TAP_MS && close
699
711
  if (DEBUG) {
700
712
  console.log('[Dock] tap', { x: Math.round(clientX), y: Math.round(clientY), gap, dist, close, match })
701
713
  }
@@ -703,7 +715,7 @@
703
715
  lastTapTime = 0
704
716
  triggerDock()
705
717
  } else {
706
- lastTapTime = now
718
+ lastTapTime = time
707
719
  lastTapX = clientX
708
720
  lastTapY = clientY
709
721
  lastTapSpace = space
@@ -722,22 +734,23 @@
722
734
 
723
735
  // Dedupe: touchstart and pointerdown often both fire for the same gesture.
724
736
  // We accept whichever arrives first and ignore the other if it's within
725
- // 50 ms (the browser's synthesized-event coalescence window).
726
- let lastEventAt = 0
737
+ // 50 ms (the browser's synthesized-event coalescence window). The window is
738
+ // measured in event time: after the page stalls, two real taps can be
739
+ // handled back to back, and neither may be taken for the other's twin.
740
+ let lastEventAt = -Infinity
727
741
  const COALESCE_MS = 50
728
742
 
729
- const onDown = (x, y) => {
730
- const now = Date.now()
731
- if (now - lastEventAt < COALESCE_MS) return
732
- lastEventAt = now
733
- handleTap(x, y)
743
+ const onDown = (x, y, time) => {
744
+ if (Math.abs(time - lastEventAt) < COALESCE_MS) return
745
+ lastEventAt = time
746
+ handleTap(x, y, null, time)
734
747
  }
735
748
 
736
749
  doc.addEventListener(
737
750
  'pointerdown',
738
751
  (e) => {
739
752
  const o = offset()
740
- onDown(e.clientX + o.x, e.clientY + o.y)
753
+ onDown(e.clientX + o.x, e.clientY + o.y, eventTime(doc, e))
741
754
  },
742
755
  { passive: true, capture: true }
743
756
  )
@@ -752,7 +765,7 @@
752
765
  if (!e.touches || e.touches.length === 0) return
753
766
  const t = e.touches[0]
754
767
  const o = offset()
755
- onDown(t.clientX + o.x, t.clientY + o.y)
768
+ onDown(t.clientX + o.x, t.clientY + o.y, eventTime(doc, e))
756
769
  },
757
770
  { passive: true, capture: true }
758
771
  )
@@ -823,7 +836,7 @@
823
836
  // loaded.
824
837
  const unreachableFrames = new WeakMap()
825
838
  const forwardingFrames = new WeakSet()
826
- let lastForwardedAt = 0
839
+ let lastForwardedAt = -Infinity
827
840
 
828
841
  // The app frame a frame belongs to: the frame itself, or the one holding
829
842
  // the document it sits in, found through same-origin parents.
@@ -913,16 +926,19 @@
913
926
  forwardingFrames.add(from)
914
927
  updateHandle()
915
928
  }
916
- const now = Date.now()
917
- if (now - lastForwardedAt < FORWARD_COALESCE_MS) return
918
- lastForwardedAt = now
929
+ // The page can say when the tap happened. Without that, or with a time
930
+ // that has not come yet, the message's arrival stands in for it.
931
+ const received = nowTime()
932
+ const time = Number.isFinite(data.t) && data.t <= received ? data.t : received
933
+ if (Math.abs(time - lastForwardedAt) < FORWARD_COALESCE_MS) return
934
+ lastForwardedAt = time
919
935
  // A frame inside the page sits somewhere the dock cannot see, so its
920
936
  // taps keep their own coordinates and pair up only with each other.
921
937
  if (e.source === from.contentWindow) {
922
938
  const o = offsetInDock(from)
923
- handleTap(data.x + o.x, data.y + o.y)
939
+ handleTap(data.x + o.x, data.y + o.y, null, time)
924
940
  } else {
925
- handleTap(data.x, data.y, e.source)
941
+ handleTap(data.x, data.y, e.source, time)
926
942
  }
927
943
  })
928
944