@capgo/capacitor-stream-call 0.0.31 → 0.0.32

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.
@@ -1,4 +1,6 @@
1
1
  import UIKit
2
+ import os.log
3
+ import WebKit
2
4
 
3
5
  class TouchInterceptView: UIView {
4
6
  private weak var webView: UIView?
@@ -11,58 +13,119 @@ class TouchInterceptView: UIView {
11
13
  // Ensure this view is transparent and doesn't interfere with display
12
14
  self.backgroundColor = .clear
13
15
  self.isOpaque = false
14
- // print("TouchInterceptView setup with webView: \(webView) and overlayView: \(overlayView)")
16
+ os_log(.debug, "TouchInterceptView: setupWithWebView - webView: %{public}s, overlayView: %{public}s", String(describing: webView), String(describing: overlayView))
15
17
  }
16
18
 
17
- override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
18
- guard let webView = webView, let overlayView = overlayView, overlayView.isHidden == false else {
19
- // print("hitTest: Conditions not met - webView: \(webView?.description ?? "nil"), overlayView: \(overlayView?.description ?? "nil"), overlayHidden: \(overlayView?.isHidden ?? true)")
20
- return super.hitTest(point, with: event)
19
+ private func isInteractive(_ view: UIView) -> Bool {
20
+ if view is UIControl { return true }
21
+ if let grs = view.gestureRecognizers, !grs.isEmpty { return true }
22
+ return false
23
+ }
24
+
25
+ private func nonGreedyInteractiveHitTest(in view: UIView, point: CGPoint, with event: UIEvent?) -> UIView? {
26
+ // Traverse subviews in reverse order (frontmost first)
27
+ for subview in view.subviews.reversed() where subview.isUserInteractionEnabled && !subview.isHidden && subview.alpha >= 0.01 {
28
+ let converted = view.convert(point, to: subview)
29
+ if subview.point(inside: converted, with: event) {
30
+ if let hit = nonGreedyInteractiveHitTest(in: subview, point: converted, with: event) {
31
+ return hit
32
+ }
33
+ }
21
34
  }
22
-
23
- // First, check if the webview has a specific subview to interact with at this point
24
- let webViewHit = webView.hitTest(point, with: event)
25
- if webViewHit != nil && webViewHit != webView {
26
- // Check if the hit view is a specific interactive element (like a button)
27
- if webViewHit is UIButton || webViewHit?.accessibilityTraits.contains(.button) == true {
28
- // print("hitTest: WebView interactive element hit at point \(point) - returning \(webViewHit?.description ?? "nil")")
29
- return webViewHit
35
+ // If no subview handled, return view itself only if truly interactive
36
+ return isInteractive(view) && view.point(inside: point, with: event) ? view : nil
37
+ }
38
+
39
+ private func forwardClickToWeb(at pointInSelf: CGPoint) {
40
+ guard let wk = webView as? WKWebView else { return }
41
+ let locationInWeb = self.convert(pointInSelf, to: wk)
42
+ let x = Int(locationInWeb.x)
43
+ let y = Int(locationInWeb.y)
44
+ let js = """
45
+ (() => {
46
+ const x = \(x); const y = \(y);
47
+ const el = document.elementFromPoint(x, y);
48
+ if (!el) return 'NO_ELEM';
49
+ const eventInit = { bubbles: true, cancelable: true, clientX: x, clientY: y };
50
+ const touchInit = { bubbles: true, cancelable: true, touches: [{ clientX: x, clientY: y }], targetTouches: [], changedTouches: [], shiftKey: false };
51
+ const seq = [];
52
+ try {
53
+ seq.push(new TouchEvent('touchstart', touchInit));
54
+ } catch(e) { console.log('TouchEvent not supported', e); }
55
+ seq.push(new PointerEvent('pointerdown', { ...eventInit, pointerType: 'touch' }));
56
+ seq.push(new MouseEvent('mousedown', eventInit));
57
+ try {
58
+ seq.push(new TouchEvent('touchend', touchInit));
59
+ } catch(e) { }
60
+ seq.push(new PointerEvent('pointerup', { ...eventInit, pointerType: 'touch' }));
61
+ seq.push(new MouseEvent('mouseup', eventInit));
62
+ seq.push(new MouseEvent('click', eventInit));
63
+ seq.forEach(evt => el.dispatchEvent(evt));
64
+ console.log('SyntheticClick seq on', el);
65
+ return el.tagName;
66
+ })();
67
+ """
68
+ os_log(.debug, "TouchInterceptView: forwardClickToWeb - (%{public}d,%{public}d)", x, y)
69
+ wk.evaluateJavaScript(js) { result, error in
70
+ if let error = error {
71
+ os_log(.error, "TouchInterceptView: JS error %{public}s", String(describing: error))
30
72
  } else {
31
- // print("hitTest: WebView hit at point \(point) but not an interactive element - passing through")
73
+ os_log(.debug, "TouchInterceptView: JS returned %{public}s", String(describing: result))
32
74
  }
33
- } else {
34
- // print("hitTest: No WebView hit at point \(point) - passing through")
35
75
  }
36
-
37
- // If no specific interactive element in webview is hit, pass through to overlay (StreamCall UI)
38
- let overlayHit = overlayView.hitTest(point, with: event)
39
- if overlayHit != nil {
40
- // print("hitTest: Overlay hit at point \(point) - returning \(overlayHit?.description ?? "nil")")
41
- return overlayHit
76
+ }
77
+
78
+ override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
79
+ // Forward to web for debugging every touch point
80
+ os_log(.debug, "TouchInterceptView: hitTest entry at %{public}s. Forwarding click to web.", String(describing: point))
81
+ forwardClickToWeb(at: point)
82
+ // 1. interactive hit on overlay (including root)
83
+ if let overlayView = self.overlayView, !overlayView.isHidden {
84
+ let overlayPoint = self.convert(point, to: overlayView)
85
+ if let overlayHit = nonGreedyInteractiveHitTest(in: overlayView, point: overlayPoint, with: event) {
86
+ os_log(.debug, "TouchInterceptView: hitTest - Overlay view %{public}s at %{public}s", String(describing: overlayHit), String(describing: overlayPoint))
87
+ return overlayHit
88
+ }
42
89
  }
43
-
44
- // If neither webview nor overlay handles the touch, return nil to pass through
45
- // print("hitTest: No hit at point \(point) - returning nil")
90
+ // 2. webView fallback
91
+ if let webView = self.webView {
92
+ let webPoint = self.convert(point, to: webView)
93
+ let result = webView.hitTest(webPoint, with: event)
94
+ os_log(.debug, "TouchInterceptView: hitTest - WebView result %{public}s at %{public}s", String(describing: result), String(describing: webPoint))
95
+ return result
96
+ }
97
+ os_log(.debug, "TouchInterceptView: hitTest - No view found for %{public}s", String(describing: point))
46
98
  return nil
47
99
  }
48
100
 
49
101
  override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
50
- guard let webView = webView, let overlayView = overlayView, overlayView.isHidden == false else {
51
- // print("point(inside:with): Conditions not met - webView: \(webView?.description ?? "nil"), overlayView: \(overlayView?.description ?? "nil"), overlayHidden: \(overlayView?.isHidden ?? true)")
52
- return false
53
- }
54
- // Check if webview has content at this point
55
- if webView.point(inside: point, with: event) {
56
- // If webview claims the point, check for interactive elements in hitTest
57
- // print("point(inside:with): WebView claims point \(point) - will check for interactive elements")
58
- return true
102
+ guard let webView = self.webView else {
103
+ os_log(.debug, "TouchInterceptView: point(inside) - webView is nil for point %{public}s. Checking overlay or deferring to super.", String(describing: point))
104
+ if let overlayView = self.overlayView, !overlayView.isHidden {
105
+ let overlayPoint = self.convert(point, to: overlayView)
106
+ let overlayViewConsidersPointInside = overlayView.point(inside: overlayPoint, with: event)
107
+ os_log(.debug, "TouchInterceptView: point(inside) - webView nil. Overlay (%{public}s) for converted point %{public}s = %s", String(describing: overlayViewConsidersPointInside), String(describing: overlayPoint), String(describing: overlayViewConsidersPointInside))
108
+ return overlayViewConsidersPointInside
109
+ }
110
+ return super.point(inside: point, with: event)
59
111
  }
60
- // Otherwise, allow touches to pass through to the overlay if relevant
61
- if overlayView.point(inside: point, with: event) {
62
- // print("point(inside:with): Overlay claims point \(point)")
63
- return true
112
+
113
+ let webViewPoint = self.convert(point, to: webView)
114
+ let webViewConsidersPointInside = webView.point(inside: webViewPoint, with: event)
115
+
116
+ if let overlayView = self.overlayView, !overlayView.isHidden {
117
+ let overlayPoint = self.convert(point, to: overlayView)
118
+ let overlayViewConsidersPointInside = overlayView.point(inside: overlayPoint, with: event)
119
+ let result = webViewConsidersPointInside || overlayViewConsidersPointInside
120
+ os_log(.debug, "TouchInterceptView: point(inside) - WebView (%{public}s at %{public}s) OR Visible Overlay (%{public}s at %{public}s) for original point %{public}s = %s", String(describing: webViewConsidersPointInside), String(describing: webViewPoint), String(describing: overlayViewConsidersPointInside), String(describing: overlayPoint), String(describing: point), String(describing: result))
121
+ return result
122
+ } else {
123
+ if self.overlayView == nil {
124
+ os_log(.debug, "TouchInterceptView: point(inside) - Overlay nil. WebView (%{public}s at %{public}s) for original point %{public}s = %s", String(describing: webViewConsidersPointInside), String(describing: webViewPoint), String(describing: point), String(describing: webViewConsidersPointInside))
125
+ } else {
126
+ os_log(.debug, "TouchInterceptView: point(inside) - Overlay hidden. WebView (%{public}s at %{public}s) for original point %{public}s = %s", String(describing: webViewConsidersPointInside), String(describing: webViewPoint), String(describing: point), String(describing: webViewConsidersPointInside))
127
+ }
128
+ return webViewConsidersPointInside
64
129
  }
65
- // print("point(inside:with): No view claims point \(point)")
66
- return false
67
130
  }
68
131
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-stream-call",
3
- "version": "0.0.31",
3
+ "version": "0.0.32",
4
4
  "description": "Uses the https://getstream.io/ SDK to implement calling in Capacitor",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",