@capgo/capacitor-inappbrowser 8.12.2-beta.pr634.524.1 → 8.12.2-beta.pr634.541.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.
@@ -3868,12 +3868,8 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
3868
3868
  if (_webView != webView) {
3869
3869
  return;
3870
3870
  }
3871
- int targetY = ReloadGestureSupport.webViewScrollYAfterGestureReload(webView.getScrollY());
3872
- if (webView.getScrollY() != targetY) {
3873
- webView.scrollTo(webView.getScrollX(), targetY);
3874
- }
3875
- if (swipeRefreshLayout != null) {
3876
- swipeRefreshLayout.setRefreshing(false);
3871
+ if (webView.getScrollY() != 0) {
3872
+ webView.scrollTo(webView.getScrollX(), 0);
3877
3873
  }
3878
3874
  });
3879
3875
  }
@@ -250,9 +250,6 @@ enum CustomWebViewFrameSupport {
250
250
  }
251
251
 
252
252
  enum ReloadGestureSupport {
253
- /// Approximate UIRefreshControl activation distance in points.
254
- static let defaultPullThreshold: CGFloat = 60
255
-
256
253
  /// UIRefreshControl may fire valueChanged while the finger is still down.
257
254
  /// Match browser UX: commit reload only after touch end.
258
255
  static func shouldDeferReloadUntilTouchEnd(isTracking: Bool, isDragging: Bool) -> Bool {
@@ -264,25 +261,30 @@ enum ReloadGestureSupport {
264
261
  max(0, -(contentOffsetY + adjustedContentInsetTop))
265
262
  }
266
263
 
267
- static func isPullPastRefreshThreshold(
268
- contentOffsetY: CGFloat,
269
- adjustedContentInsetTop: CGFloat,
270
- threshold: CGFloat = defaultPullThreshold
264
+ /// After intentional touch end, reload only if still near the activation pull distance
265
+ /// captured when UIRefreshControl fired `.valueChanged` (not a fixed constant).
266
+ static func shouldReloadOnTouchEnd(
267
+ pendingReload: Bool,
268
+ currentPullDistance: CGFloat,
269
+ armedPullDistance: CGFloat
271
270
  ) -> Bool {
272
- pullDistance(contentOffsetY: contentOffsetY, adjustedContentInsetTop: adjustedContentInsetTop) >= threshold
273
- }
274
-
275
- /// After intentional touch end, reload only if pull is still past the threshold.
276
- static func shouldReloadOnTouchEnd(pendingReload: Bool, isPullPastThreshold: Bool) -> Bool {
277
- pendingReload && isPullPastThreshold
271
+ guard pendingReload, armedPullDistance > 0 else { return false }
272
+ // Allow slight finger jitter below the exact activation point.
273
+ return currentPullDistance >= armedPullDistance * 0.9
278
274
  }
279
275
 
280
276
  /// Resting offset for the current adjusted top inset (safe-area aware).
277
+ /// - forceToRestingTop: after gesture reload, always snap to resting top (fixes clipped header).
278
+ /// Otherwise only un-overscroll after a cancelled pull.
281
279
  static func contentOffsetYAfterReloadReset(
282
280
  currentY: CGFloat,
283
- adjustedContentInsetTop: CGFloat
281
+ adjustedContentInsetTop: CGFloat,
282
+ forceToRestingTop: Bool = false
284
283
  ) -> CGFloat {
285
284
  let restingY = -adjustedContentInsetTop
285
+ if forceToRestingTop {
286
+ return restingY
287
+ }
286
288
  return currentY < restingY ? restingY : currentY
287
289
  }
288
290
  }
@@ -11,6 +11,7 @@ extension WKWebViewController {
11
11
 
12
12
  guard enableReloadGesture, !disableOverscroll else {
13
13
  pendingReloadFromGesture = false
14
+ reloadGestureArmedPullDistance = 0
14
15
  return
15
16
  }
16
17
 
@@ -40,6 +41,11 @@ extension WKWebViewController {
40
41
  isDragging: scrollView.isDragging
41
42
  ) {
42
43
  pendingReloadFromGesture = true
44
+ // Capture the real activation pull distance from UIRefreshControl.
45
+ reloadGestureArmedPullDistance = ReloadGestureSupport.pullDistance(
46
+ contentOffsetY: scrollView.contentOffset.y,
47
+ adjustedContentInsetTop: scrollView.adjustedContentInset.top
48
+ )
43
49
  return
44
50
  }
45
51
 
@@ -52,30 +58,35 @@ extension WKWebViewController {
52
58
 
53
59
  switch gesture.state {
54
60
  case .ended:
55
- let pastThreshold = ReloadGestureSupport.isPullPastRefreshThreshold(
61
+ let pullDistance = ReloadGestureSupport.pullDistance(
56
62
  contentOffsetY: scrollView.contentOffset.y,
57
63
  adjustedContentInsetTop: scrollView.adjustedContentInset.top
58
64
  )
59
65
  let shouldReload = ReloadGestureSupport.shouldReloadOnTouchEnd(
60
66
  pendingReload: pendingReloadFromGesture,
61
- isPullPastThreshold: pastThreshold
67
+ currentPullDistance: pullDistance,
68
+ armedPullDistance: reloadGestureArmedPullDistance
62
69
  )
63
70
  pendingReloadFromGesture = false
71
+ reloadGestureArmedPullDistance = 0
64
72
 
65
73
  if shouldReload {
66
74
  performReloadFromGesture()
67
75
  } else if !reloadFromGestureInProgress,
68
76
  scrollView.refreshControl?.isRefreshing == true {
69
77
  scrollView.refreshControl?.endRefreshing()
70
- resetReloadGestureScrollState(on: scrollView)
78
+ resetReloadGestureScrollState(on: scrollView, forceToRestingTop: false)
71
79
  }
72
80
 
73
81
  case .cancelled, .failed:
74
82
  // System interruption — never commit a reload.
75
83
  pendingReloadFromGesture = false
84
+ reloadGestureArmedPullDistance = 0
85
+ // Don't touch an in-flight gesture reload's spinner / scroll.
86
+ guard !reloadFromGestureInProgress else { break }
76
87
  if scrollView.refreshControl?.isRefreshing == true {
77
88
  scrollView.refreshControl?.endRefreshing()
78
- resetReloadGestureScrollState(on: scrollView)
89
+ resetReloadGestureScrollState(on: scrollView, forceToRestingTop: false)
79
90
  }
80
91
 
81
92
  default:
@@ -85,25 +96,41 @@ extension WKWebViewController {
85
96
 
86
97
  private func performReloadFromGesture() {
87
98
  pendingReloadFromGesture = false
99
+ reloadGestureArmedPullDistance = 0
88
100
  reloadFromGestureInProgress = true
89
- reload()
101
+ // Track this navigation so cancelled prior loads don't clear gesture state early.
102
+ reloadGestureNavigation = self.capableWebView?.reload()
103
+ if reloadGestureNavigation == nil {
104
+ stopReloadGesture()
105
+ }
90
106
  }
91
107
 
92
- private func resetReloadGestureScrollState(on scrollView: UIScrollView) {
93
- // Keep baseline/safe-area insets; only clamp sticky refresh overscroll.
108
+ private func resetReloadGestureScrollState(on scrollView: UIScrollView, forceToRestingTop: Bool) {
94
109
  let resetY = ReloadGestureSupport.contentOffsetYAfterReloadReset(
95
110
  currentY: scrollView.contentOffset.y,
96
- adjustedContentInsetTop: scrollView.adjustedContentInset.top
111
+ adjustedContentInsetTop: scrollView.adjustedContentInset.top,
112
+ forceToRestingTop: forceToRestingTop
97
113
  )
98
114
  if resetY != scrollView.contentOffset.y {
99
115
  scrollView.setContentOffset(CGPoint(x: scrollView.contentOffset.x, y: resetY), animated: false)
100
116
  }
101
117
  }
102
118
 
103
- func stopReloadGesture() {
119
+ func stopReloadGesture(for navigation: WKNavigation? = nil) {
104
120
  pendingReloadFromGesture = false
121
+
122
+ // Ignore unrelated navigation callbacks while a gesture reload is in flight.
123
+ if reloadFromGestureInProgress,
124
+ let expected = reloadGestureNavigation,
125
+ let navigation,
126
+ expected !== navigation {
127
+ return
128
+ }
129
+
105
130
  let shouldResetScroll = reloadFromGestureInProgress
106
131
  reloadFromGestureInProgress = false
132
+ reloadGestureNavigation = nil
133
+ reloadGestureArmedPullDistance = 0
107
134
 
108
135
  guard let activeWebView = self.capableWebView else { return }
109
136
  let scrollView = activeWebView.scrollView
@@ -111,14 +138,19 @@ extension WKWebViewController {
111
138
 
112
139
  guard shouldResetScroll else { return }
113
140
 
114
- // UIRefreshControl + WKWebView.reload() often leave a top gap and block the next pull
115
- // until a full document navigation recreates scroll state.
141
+ // UIRefreshControl + WKWebView.reload() often leave offset at 0 instead of
142
+ // -adjustedContentInset.top (clips header ~safe-area) and block the next pull.
116
143
  DispatchQueue.main.async { [weak self] in
117
144
  guard let self, let activeWebView = self.capableWebView else { return }
118
145
  let scrollView = activeWebView.scrollView
119
146
  scrollView.refreshControl?.endRefreshing()
120
- self.resetReloadGestureScrollState(on: scrollView)
147
+ self.resetReloadGestureScrollState(on: scrollView, forceToRestingTop: true)
121
148
  self.configureReloadGesture(for: activeWebView)
149
+ // Rebind can change adjusted insets; snap again to the new resting top.
150
+ self.resetReloadGestureScrollState(
151
+ on: activeWebView.scrollView,
152
+ forceToRestingTop: true
153
+ )
122
154
  }
123
155
  }
124
156
  }
@@ -474,6 +474,8 @@ open class WKWebViewController: UIViewController, WKScriptMessageHandler {
474
474
  var enableReloadGesture: Bool = false
475
475
  var pendingReloadFromGesture = false
476
476
  var reloadFromGestureInProgress = false
477
+ var reloadGestureNavigation: WKNavigation?
478
+ var reloadGestureArmedPullDistance: CGFloat = 0
477
479
  var reloadPanObserverInstalled = false
478
480
  var disableOverscroll: Bool = false
479
481
  var proxyRequests: Bool = false
@@ -2576,6 +2578,8 @@ public extension WKWebViewController {
2576
2578
  }
2577
2579
  pendingReloadFromGesture = false
2578
2580
  reloadFromGestureInProgress = false
2581
+ reloadGestureNavigation = nil
2582
+ reloadGestureArmedPullDistance = 0
2579
2583
 
2580
2584
  // Remove KVO observers FIRST, before any operation that could trigger them
2581
2585
  webView.removeObserver(self, forKeyPath: estimatedProgressKeyPath)
@@ -3522,7 +3526,7 @@ extension WKWebViewController: WKNavigationDelegate {
3522
3526
  lastInjectedSafeAreaInsets = nil
3523
3527
  syncWebViewSafeAreaLayout()
3524
3528
  emit("browserPageLoaded")
3525
- stopReloadGesture()
3529
+ stopReloadGesture(for: navigation)
3526
3530
  }
3527
3531
 
3528
3532
  public func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
@@ -3532,7 +3536,7 @@ extension WKWebViewController: WKNavigationDelegate {
3532
3536
  self.url = url
3533
3537
  delegate?.webViewController?(self, didFail: url, withError: error)
3534
3538
  }
3535
- stopReloadGesture()
3539
+ stopReloadGesture(for: navigation)
3536
3540
  emit("pageLoadError")
3537
3541
  }
3538
3542
 
@@ -3543,7 +3547,7 @@ extension WKWebViewController: WKNavigationDelegate {
3543
3547
  self.url = url
3544
3548
  delegate?.webViewController?(self, didFail: url, withError: error)
3545
3549
  }
3546
- stopReloadGesture()
3550
+ stopReloadGesture(for: navigation)
3547
3551
  emit("pageLoadError")
3548
3552
  }
3549
3553
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-inappbrowser",
3
- "version": "8.12.2-beta.pr634.524.1",
3
+ "version": "8.12.2-beta.pr634.541.1",
4
4
  "description": "Capacitor plugin in app browser",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",
@@ -1,16 +0,0 @@
1
- package ee.forgr.capacitor_inappbrowser;
2
-
3
- /**
4
- * Pure helpers for pull-to-refresh reset after a gesture reload.
5
- * Sticky scrollY greater than 0 makes SwipeRefreshLayout think the child can scroll up,
6
- * which disables the next pull until a full document navigation.
7
- */
8
- final class ReloadGestureSupport {
9
-
10
- private ReloadGestureSupport() {}
11
-
12
- /** Pin vertical scroll to top after a gesture reload so the next pull can start. */
13
- static int webViewScrollYAfterGestureReload(int currentScrollY) {
14
- return 0;
15
- }
16
- }