@capgo/capacitor-inappbrowser 8.12.2-beta.pr634.543.1 → 8.12.2-beta.pr634.550.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.
@@ -250,6 +250,11 @@ enum CustomWebViewFrameSupport {
250
250
  }
251
251
 
252
252
  enum ReloadGestureSupport {
253
+ /// Ordinary page finishes must not clear an armed pull still held by the user.
254
+ static func shouldApplyStopReloadGesture(reloadFromGestureInProgress: Bool) -> Bool {
255
+ reloadFromGestureInProgress
256
+ }
257
+
253
258
  /// UIRefreshControl may fire valueChanged while the finger is still down.
254
259
  /// Match browser UX: commit reload only after touch end.
255
260
  static func shouldDeferReloadUntilTouchEnd(isTracking: Bool, isDragging: Bool) -> Bool {
@@ -273,18 +278,18 @@ enum ReloadGestureSupport {
273
278
  return currentPullDistance >= armedPullDistance * 0.9
274
279
  }
275
280
 
276
- /// Resting offset for the current adjusted top inset (safe-area aware).
277
- /// - Parameter forceToRestingTop: After gesture reload, always snap to resting top (fixes clipped header).
278
- /// Otherwise only un-overscroll after a cancelled pull.
281
+ /// Resting offset after gesture reload.
282
+ /// Plugin safe-area uses CSS variables with `contentInset = .zero`, so forced reset is always `0`.
283
+ /// - Parameter forceToRestingTop: After gesture reload, snap to top. Otherwise only un-overscroll.
279
284
  static func contentOffsetYAfterReloadReset(
280
285
  currentY: CGFloat,
281
286
  adjustedContentInsetTop: CGFloat,
282
287
  forceToRestingTop: Bool = false
283
288
  ) -> CGFloat {
284
- let restingY = -adjustedContentInsetTop
285
289
  if forceToRestingTop {
286
- return restingY
290
+ return 0
287
291
  }
292
+ let restingY = -adjustedContentInsetTop
288
293
  return currentY < restingY ? restingY : currentY
289
294
  }
290
295
  }
@@ -9,6 +9,10 @@ extension WKWebViewController {
9
9
  targetWebView.scrollView.refreshControl = nil
10
10
  }
11
11
 
12
+ // UIRefreshControl can leave sticky insets that clip the page header after reload.
13
+ targetWebView.scrollView.contentInset = .zero
14
+ targetWebView.scrollView.scrollIndicatorInsets = .zero
15
+
12
16
  guard enableReloadGesture, !disableOverscroll else {
13
17
  pendingReloadFromGesture = false
14
18
  reloadGestureArmedPullDistance = 0
@@ -41,7 +45,6 @@ extension WKWebViewController {
41
45
  isDragging: scrollView.isDragging
42
46
  ) {
43
47
  pendingReloadFromGesture = true
44
- // Capture the real activation pull distance from UIRefreshControl.
45
48
  reloadGestureArmedPullDistance = ReloadGestureSupport.pullDistance(
46
49
  contentOffsetY: scrollView.contentOffset.y,
47
50
  adjustedContentInsetTop: scrollView.adjustedContentInset.top
@@ -75,18 +78,16 @@ extension WKWebViewController {
75
78
  } else if !reloadFromGestureInProgress,
76
79
  scrollView.refreshControl?.isRefreshing == true {
77
80
  scrollView.refreshControl?.endRefreshing()
78
- resetReloadGestureScrollState(on: scrollView, forceToRestingTop: false)
81
+ hardResetReloadGestureScroll(on: scrollView)
79
82
  }
80
83
 
81
84
  case .cancelled, .failed:
82
- // System interruption — never commit a reload.
83
85
  pendingReloadFromGesture = false
84
86
  reloadGestureArmedPullDistance = 0
85
- // Don't touch an in-flight gesture reload's spinner / scroll.
86
87
  guard !reloadFromGestureInProgress else { break }
87
88
  if scrollView.refreshControl?.isRefreshing == true {
88
89
  scrollView.refreshControl?.endRefreshing()
89
- resetReloadGestureScrollState(on: scrollView, forceToRestingTop: false)
90
+ hardResetReloadGestureScroll(on: scrollView)
90
91
  }
91
92
 
92
93
  default:
@@ -98,29 +99,43 @@ extension WKWebViewController {
98
99
  pendingReloadFromGesture = false
99
100
  reloadGestureArmedPullDistance = 0
100
101
  reloadFromGestureInProgress = true
101
- // Track this navigation so cancelled prior loads don't clear gesture state early.
102
- reloadGestureNavigation = self.capableWebView?.reload()
102
+
103
+ guard let webView = self.capableWebView else {
104
+ stopReloadGesture()
105
+ return
106
+ }
107
+
108
+ // `reload()` preserves broken scroll/inset state after UIRefreshControl.
109
+ // A fresh URL load matches a normal navigation and resets scroll like the reporter's "proper" reload.
110
+ if let url = webView.url {
111
+ reloadGestureNavigation = webView.load(URLRequest(url: url))
112
+ } else {
113
+ reloadGestureNavigation = webView.reload()
114
+ }
115
+
103
116
  if reloadGestureNavigation == nil {
104
117
  stopReloadGesture()
105
118
  }
106
119
  }
107
120
 
108
- private func resetReloadGestureScrollState(on scrollView: UIScrollView, forceToRestingTop: Bool) {
121
+ private func hardResetReloadGestureScroll(on scrollView: UIScrollView) {
122
+ scrollView.contentInset = .zero
123
+ scrollView.scrollIndicatorInsets = .zero
109
124
  let resetY = ReloadGestureSupport.contentOffsetYAfterReloadReset(
110
125
  currentY: scrollView.contentOffset.y,
111
- adjustedContentInsetTop: scrollView.adjustedContentInset.top,
112
- forceToRestingTop: forceToRestingTop
126
+ adjustedContentInsetTop: 0,
127
+ forceToRestingTop: true
113
128
  )
114
- if resetY != scrollView.contentOffset.y {
115
- scrollView.setContentOffset(CGPoint(x: scrollView.contentOffset.x, y: resetY), animated: false)
129
+ if scrollView.contentOffset.y != resetY || scrollView.contentOffset.x != 0 {
130
+ scrollView.setContentOffset(CGPoint(x: 0, y: resetY), animated: false)
116
131
  }
117
132
  }
118
133
 
119
134
  func stopReloadGesture(for navigation: WKNavigation? = nil) {
120
- // Ordinary page finishes must not clear an armed pull the user is still holding.
121
- guard reloadFromGestureInProgress else { return }
135
+ guard ReloadGestureSupport.shouldApplyStopReloadGesture(
136
+ reloadFromGestureInProgress: reloadFromGestureInProgress
137
+ ) else { return }
122
138
 
123
- // Ignore unrelated navigation callbacks while a gesture reload is in flight.
124
139
  if let expected = reloadGestureNavigation,
125
140
  let navigation,
126
141
  expected !== navigation {
@@ -133,22 +148,23 @@ extension WKWebViewController {
133
148
  reloadGestureArmedPullDistance = 0
134
149
 
135
150
  guard let activeWebView = self.capableWebView else { return }
136
- let scrollView = activeWebView.scrollView
137
- scrollView.refreshControl?.endRefreshing()
151
+ activeWebView.scrollView.refreshControl?.endRefreshing()
138
152
 
139
- // UIRefreshControl + WKWebView.reload() often leave offset at 0 instead of
140
- // -adjustedContentInset.top (clips header ~safe-area) and block the next pull.
153
+ // Reset after the current runloop so WKWebView finishes applying navigation scroll state.
141
154
  DispatchQueue.main.async { [weak self] in
142
155
  guard let self, let activeWebView = self.capableWebView else { return }
143
156
  let scrollView = activeWebView.scrollView
144
157
  scrollView.refreshControl?.endRefreshing()
145
- self.resetReloadGestureScrollState(on: scrollView, forceToRestingTop: true)
158
+ self.hardResetReloadGestureScroll(on: scrollView)
146
159
  self.configureReloadGesture(for: activeWebView)
147
- // Rebind can change adjusted insets; snap again to the new resting top.
148
- self.resetReloadGestureScrollState(
149
- on: activeWebView.scrollView,
150
- forceToRestingTop: true
151
- )
160
+ self.hardResetReloadGestureScroll(on: activeWebView.scrollView)
161
+ activeWebView.evaluateJavaScript("window.scrollTo(0, 0);", completionHandler: nil)
162
+
163
+ // One more pass after layout / JS scroll restoration.
164
+ DispatchQueue.main.async { [weak self] in
165
+ guard let self, let activeWebView = self.capableWebView else { return }
166
+ self.hardResetReloadGestureScroll(on: activeWebView.scrollView)
167
+ }
152
168
  }
153
169
  }
154
170
  }
@@ -3523,10 +3523,11 @@ extension WKWebViewController: WKNavigationDelegate {
3523
3523
  delegate?.webViewController?(self, didFinish: url)
3524
3524
  }
3525
3525
  self.injectJavaScriptInterface()
3526
+ // End refresh + clear sticky insets before safe-area sync zeroes contentInset.
3527
+ stopReloadGesture(for: navigation)
3526
3528
  lastInjectedSafeAreaInsets = nil
3527
3529
  syncWebViewSafeAreaLayout()
3528
3530
  emit("browserPageLoaded")
3529
- stopReloadGesture(for: navigation)
3530
3531
  }
3531
3532
 
3532
3533
  public func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
@@ -3677,10 +3678,16 @@ extension WKWebViewController: WKNavigationDelegate {
3677
3678
 
3678
3679
  public func webView(_ webView: WKWebView, navigationAction: WKNavigationAction, didBecome download: WKDownload) {
3679
3680
  register(download: download, response: nil, sourceURL: navigationAction.request.url?.absoluteString)
3681
+ stopReloadGesture()
3680
3682
  }
3681
3683
 
3682
3684
  public func webView(_ webView: WKWebView, navigationResponse: WKNavigationResponse, didBecome download: WKDownload) {
3683
3685
  register(download: download, response: navigationResponse.response)
3686
+ stopReloadGesture()
3687
+ }
3688
+
3689
+ public func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
3690
+ stopReloadGesture()
3684
3691
  }
3685
3692
 
3686
3693
  // MARK: - Dimension Management
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-inappbrowser",
3
- "version": "8.12.2-beta.pr634.543.1",
3
+ "version": "8.12.2-beta.pr634.550.1",
4
4
  "description": "Capacitor plugin in app browser",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",