@momo-kits/native-kits 0.160.1-scrolltotop.22-debug → 0.160.1-scrolltotop.23-debug

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.
@@ -40,7 +40,7 @@ kotlin {
40
40
  }
41
41
 
42
42
  cocoapods {
43
- version = "0.160.1-scrolltotop.22-debug"
43
+ version = "0.160.1-scrolltotop.23-debug"
44
44
  summary = "IOS Shared module"
45
45
  homepage = "https://momo.vn"
46
46
  ios.deploymentTarget = "15.0"
@@ -3,7 +3,4 @@ package vn.momo.kits.navigation
3
3
  import androidx.compose.runtime.Composable
4
4
 
5
5
  @Composable
6
- internal actual fun RegisterScrollToTop(callback: (() -> Unit)?) = Unit
7
-
8
- internal actual fun onNavigatorEntered(navigator: Navigator) = Unit
9
- internal actual fun onNavigatorExited(navigator: Navigator) = Unit
6
+ internal actual fun RegisterScrollToTop(callback: (() -> Unit)?) = Unit
@@ -6,6 +6,3 @@ import androidx.compose.runtime.Composable
6
6
  internal expect fun RegisterScrollToTop(callback: (() -> Unit)?)
7
7
 
8
8
 
9
- internal expect fun onNavigatorEntered(navigator: Navigator)
10
-
11
- internal expect fun onNavigatorExited(navigator: Navigator)
@@ -2,146 +2,32 @@ package vn.momo.kits.navigation
2
2
 
3
3
  import androidx.compose.runtime.Composable
4
4
  import androidx.compose.runtime.DisposableEffect
5
- import kotlinx.cinterop.*
6
5
  import platform.Foundation.NSNotification
7
6
  import platform.Foundation.NSNotificationCenter
8
7
  import platform.Foundation.NSOperationQueue
9
8
  import platform.darwin.NSObjectProtocol
10
- import platform.objc.*
11
- import kotlin.time.Clock
12
- import kotlin.time.ExperimentalTime
13
-
14
- @OptIn(ExperimentalForeignApi::class)
15
- private val replacementHandleTap = staticCFunction<COpaquePointer, COpaquePointer, COpaquePointer?, Unit> { _, _, _ ->
16
- NSNotificationCenter.defaultCenter.postNotificationName("statusBarSelected", null)
17
- }
18
-
19
- @OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)
20
- private object StatusBarSwizzleManager {
21
- var originalHandleTapImp: CPointer<CFunction<() -> Unit>>? = null
22
- var swizzleInstalled = false
23
- var observerCount = 0
24
-
25
- fun install() {
26
- try {
27
- if (swizzleInstalled) return
28
- // objc_getClass returns Any? in the binding, but the underlying value is
29
- // the ObjC Class metaobject — safe to cast to ObjCClass for the other
30
- // runtime calls that require it.
31
- val cls = objc_getClass("UIStatusBarManager") as? ObjCClass ?: run {
32
- log("swizzle skipped: UIStatusBarManager class not found")
33
- return
34
- }
35
- val selector = sel_registerName("handleTapAction:") ?: run {
36
- log("swizzle skipped: handleTapAction: selector unavailable")
37
- return
38
- }
39
- val method = class_getInstanceMethod(cls, selector) ?: run {
40
- log("swizzle skipped: handleTapAction: method not found")
41
- return
42
- }
43
- // Capture original IMP so we can restore it on uninstall.
44
- originalHandleTapImp = method_getImplementation(method)
45
- val typeEncoding = method_getTypeEncoding(method)?.toKString()
46
- class_replaceMethod(cls, selector, replacementHandleTap.reinterpret(), typeEncoding)
47
- swizzleInstalled = true
48
- log("UIStatusBarManager handleTapAction: replaced (originalImp captured)")
49
- } catch (e: Throwable) {
50
- log("install error: ${e.message}")
51
- }
52
- }
53
-
54
- fun uninstall() {
55
- try {
56
- if (!swizzleInstalled) return
57
- val original = originalHandleTapImp ?: run {
58
- log("uninstall skipped: no original IMP captured")
59
- return
60
- }
61
- val cls = objc_getClass("UIStatusBarManager") as? ObjCClass ?: return
62
- val selector = sel_registerName("handleTapAction:") ?: return
63
- val method = class_getInstanceMethod(cls, selector) ?: return
64
- method_setImplementation(method, original)
65
- originalHandleTapImp = null
66
- swizzleInstalled = false
67
- log("UIStatusBarManager handleTapAction: original IMP restored")
68
- } catch (e: Throwable) {
69
- log("uninstall error: ${e.message}")
70
- }
71
- }
72
- }
73
-
74
-
75
- private class StatusBarObserver {
76
- private var token: NSObjectProtocol? = null
77
- fun acquire(callback: (() -> Unit)?) {
78
- if (token != null) {
79
- return
80
- }
81
- if (StatusBarSwizzleManager.observerCount == 0) {
82
- StatusBarSwizzleManager.install()
83
- }
84
- token = NSNotificationCenter.defaultCenter.addObserverForName(
85
- name = "statusBarSelected",
86
- `object` = null,
87
- queue = NSOperationQueue.mainQueue,
88
- ) { _: NSNotification? ->
89
- log("statusBar tap notification → ScrollToTopRegistry.trigger()")
90
- callback?.invoke()
91
- }
92
- StatusBarSwizzleManager.observerCount++
93
- log("observerCount acquire ${StatusBarSwizzleManager.observerCount}")
94
-
95
- }
96
-
97
- fun release() {
98
- if (token == null) return
99
- token?.let { NSNotificationCenter.defaultCenter.removeObserver(it) }
100
- token = null
101
- StatusBarSwizzleManager.observerCount--
102
- if (StatusBarSwizzleManager.observerCount == 0) {
103
- StatusBarSwizzleManager.uninstall()
104
- }
105
- log("observerCount release ${StatusBarSwizzleManager.observerCount}")
106
- }
107
- }
108
-
109
- private const val TAG = "[ScrollToTop]"
110
-
111
- private fun Any.tag(): String = "${this::class.simpleName ?: "Any"}@${hashCode().toString(16)}"
112
-
113
- @OptIn(ExperimentalTime::class)
114
- private fun ts(): String {
115
- val ms = Clock.System.now().toEpochMilliseconds()
116
- val s = ms / 1000
117
- val hh = ((s / 3600) % 24).toString().padStart(2, '0')
118
- val mm = ((s / 60) % 60).toString().padStart(2, '0')
119
- val ss = (s % 60).toString().padStart(2, '0')
120
- val mmm = (ms % 1000).toString().padStart(3, '0')
121
- return "$hh:$mm:$ss.$mmm"
122
- }
123
-
124
- private fun log(msg: String) = println("${ts()} $TAG $msg")
125
9
 
10
+ private const val STATUS_BAR_TAPPED_NOTIFICATION = "statusBarSelected"
126
11
 
127
12
  @Composable
128
13
  internal actual fun RegisterScrollToTop(callback: (() -> Unit)?) {
14
+ val maxApi = LocalMaxApi.current
129
15
  DisposableEffect(callback) {
130
- val acquired = callback != null
131
- val statusBarObserver = StatusBarObserver()
132
- if (acquired) {
133
- statusBarObserver.acquire { callback?.invoke() }
134
- }
16
+ val cb = callback
17
+ val token: NSObjectProtocol? = if (cb != null) {
18
+ NSNotificationCenter.defaultCenter.addObserverForName(
19
+ name = STATUS_BAR_TAPPED_NOTIFICATION,
20
+ `object` = null,
21
+ queue = NSOperationQueue.mainQueue,
22
+ ) { _: NSNotification? ->
23
+ runCatching { cb() }.onFailure {
24
+ maxApi?.logFile("[ScrollToTop] callback threw: ${it.message}") {}
25
+
26
+ }
27
+ }
28
+ } else null
135
29
  onDispose {
136
- if (acquired) statusBarObserver.release()
30
+ token?.let { NSNotificationCenter.defaultCenter.removeObserver(it) }
137
31
  }
138
32
  }
139
33
  }
140
-
141
- internal actual fun onNavigatorEntered(navigator: Navigator) {
142
- StatusBarSwizzleManager.install()
143
- }
144
-
145
- internal actual fun onNavigatorExited(navigator: Navigator) {
146
- StatusBarSwizzleManager.uninstall()
147
- }
package/gradle.properties CHANGED
@@ -18,7 +18,7 @@ kotlin.apple.xcodeCompatibility.nowarn=true
18
18
  name="ComposeKits"
19
19
  group=vn.momo.kits
20
20
  artifact.id=kits
21
- version=0.160.1-scrolltotop.22
21
+ version=0.160.1-scrolltotop.23
22
22
 
23
23
  repo=GitLab
24
24
  url=https://gitlab.mservice.com.vn/api/v4/projects/5400/packages/maven
@@ -0,0 +1,42 @@
1
+ import UIKit
2
+ import ObjectiveC.runtime
3
+
4
+ extension UIStatusBarManager {
5
+ public static var statusBarTappedNotification: Notification.Name = {
6
+ let name = Notification.Name("statusBarSelected")
7
+ guard let original = class_getInstanceMethod(UIStatusBarManager.self, Selector(("handleTapAction:"))) else {
8
+ NSLog("[MoMoStatusBarTap] original handleTapAction: not found — skip")
9
+ return name
10
+ }
11
+ // Verify the method takes (self, _cmd, id) and returns void.
12
+ // Encoding may include offsets (e.g. "v32@0:8@16") so strip digits before comparing.
13
+ if let encPtr = method_getTypeEncoding(original) {
14
+ let enc = String(cString: encPtr).filter { !$0.isNumber }
15
+ guard enc == "v@:@" else {
16
+ NSLog("[MoMoStatusBarTap] unexpected encoding \(enc) — skip")
17
+ return name
18
+ }
19
+ }
20
+ guard let swizzled = class_getInstanceMethod(UIStatusBarManager.self, #selector(_mm_handleTapAction)) else {
21
+ NSLog("[MoMoStatusBarTap] replacement _mm_handleTapAction not found — skip")
22
+ return name
23
+ }
24
+ method_exchangeImplementations(original, swizzled)
25
+ NSLog("[MoMoStatusBarTap] swizzle installed")
26
+ return name
27
+ }()
28
+
29
+ @objc dynamic private func _mm_handleTapAction(_ action: Any?) {
30
+ _mm_handleTapAction(action)
31
+ NotificationCenter.default.post(name: UIStatusBarManager.statusBarTappedNotification, object: nil)
32
+ }
33
+ }
34
+
35
+ @objc(MoMoStatusBarTap)
36
+ public final class MoMoStatusBarTap: NSObject {
37
+ @objc public static let notificationName: String = "statusBarSelected"
38
+
39
+ @objc public static func install() {
40
+ _ = UIStatusBarManager.statusBarTappedNotification
41
+ }
42
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/native-kits",
3
- "version": "0.160.1-scrolltotop.22-debug",
3
+ "version": "0.160.1-scrolltotop.23-debug",
4
4
  "private": false,
5
5
  "dependencies": {},
6
6
  "devDependencies": {},