@momo-kits/native-kits 0.160.1-scrolltotop.20-debug → 0.160.1-scrolltotop.21-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.20-debug"
43
+ version = "0.160.1-scrolltotop.21-debug"
44
44
  summary = "IOS Shared module"
45
45
  homepage = "https://momo.vn"
46
46
  ios.deploymentTarget = "15.0"
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |spec|
2
2
  spec.name = 'compose'
3
- spec.version = '0.160.1-scrolltotop.18'
3
+ spec.version = '0.160.1-scrolltotop.20'
4
4
  spec.homepage = 'https://momo.vn'
5
5
  spec.source = { :http=> ''}
6
6
  spec.authors = ''
@@ -3,9 +3,7 @@ package vn.momo.kits.navigation
3
3
  import androidx.compose.runtime.Composable
4
4
 
5
5
  @Composable
6
- internal actual fun RegisterScrollToTop(navigator: Navigation, callback: (() -> Unit)?) {
7
- // No-op on Android: status bar tap doesn't trigger scroll-to-top on this platform.
8
- }
6
+ internal actual fun RegisterScrollToTop(callback: (() -> Unit)?) = Unit
9
7
 
10
8
  internal actual fun onNavigatorEntered(navigator: Navigator) = Unit
11
9
  internal actual fun onNavigatorExited(navigator: Navigator) = Unit
@@ -40,12 +40,6 @@ fun NavigationContainer(
40
40
 
41
41
  val theme = remember { mutableStateOf(initialTheme) }
42
42
 
43
-
44
- LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
45
- // Code to run when the screen is resumed
46
- onNavigatorEntered(navigator)
47
- }
48
-
49
43
  LaunchedEffect(Unit) {
50
44
  val headerBar = config?.headerBar
51
45
  if (headerBar != null && theme.value.assets.headerBackground == null) {
@@ -140,7 +134,6 @@ fun NavigationContainer(
140
134
 
141
135
  DisposableEffect(Unit) {
142
136
  onDispose {
143
- onNavigatorExited(navigator)
144
137
  navigator.dispose()
145
138
  }
146
139
  }
@@ -3,7 +3,7 @@ package vn.momo.kits.navigation
3
3
  import androidx.compose.runtime.Composable
4
4
 
5
5
  @Composable
6
- internal expect fun RegisterScrollToTop(navigator: Navigation, callback: (() -> Unit)?)
6
+ internal expect fun RegisterScrollToTop(callback: (() -> Unit)?)
7
7
 
8
8
 
9
9
  internal expect fun onNavigatorEntered(navigator: Navigator)
@@ -150,9 +150,9 @@ internal fun StackScreen(
150
150
  val footerHeightPx = remember { mutableIntStateOf(0) }
151
151
  val headerRightWidthPx = remember { mutableIntStateOf(0) }
152
152
 
153
-
154
- RegisterScrollToTop(navigation, navigation.options?.scrollData?.scrollToTopCallback)
155
-
153
+ if (navigation.options.scrollData.scrollToTopCallback != null) {
154
+ RegisterScrollToTop(navigation.options.scrollData.scrollToTopCallback)
155
+ }
156
156
 
157
157
  BackHandler(true) { navigator.onBackSafe() }
158
158
 
@@ -2,27 +2,12 @@ package vn.momo.kits.navigation
2
2
 
3
3
  import androidx.compose.runtime.Composable
4
4
  import androidx.compose.runtime.DisposableEffect
5
- import androidx.compose.runtime.remember
6
- import kotlinx.cinterop.BetaInteropApi
7
- import kotlinx.cinterop.CFunction
8
- import kotlinx.cinterop.COpaquePointer
9
- import kotlinx.cinterop.CPointer
10
- import kotlinx.cinterop.ExperimentalForeignApi
11
- import kotlinx.cinterop.ObjCClass
12
- import kotlinx.cinterop.reinterpret
13
- import kotlinx.cinterop.staticCFunction
14
- import kotlinx.cinterop.toKString
5
+ import kotlinx.cinterop.*
15
6
  import platform.Foundation.NSNotification
16
7
  import platform.Foundation.NSNotificationCenter
17
8
  import platform.Foundation.NSOperationQueue
18
9
  import platform.darwin.NSObjectProtocol
19
- import platform.objc.class_getInstanceMethod
20
- import platform.objc.class_replaceMethod
21
- import platform.objc.method_getImplementation
22
- import platform.objc.method_getTypeEncoding
23
- import platform.objc.method_setImplementation
24
- import platform.objc.objc_getClass
25
- import platform.objc.sel_registerName
10
+ import platform.objc.*
26
11
  import kotlin.time.Clock
27
12
  import kotlin.time.ExperimentalTime
28
13
 
@@ -31,58 +16,58 @@ private val replacementHandleTap = staticCFunction<COpaquePointer, COpaquePointe
31
16
  NSNotificationCenter.defaultCenter.postNotificationName("statusBarSelected", null)
32
17
  }
33
18
 
34
- @OptIn(ExperimentalForeignApi::class)
35
- private var originalHandleTapImp: CPointer<CFunction<() -> Unit>>? = null
36
-
37
- private var swizzleInstalled = false
38
-
39
19
  @OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)
40
- private fun installStatusBarSwizzle() {
41
- try {
42
- if (swizzleInstalled) return
43
- // objc_getClass returns Any? in the binding, but the underlying value is
44
- // the ObjC Class metaobject — safe to cast to ObjCClass for the other
45
- // runtime calls that require it.
46
- val cls = objc_getClass("UIStatusBarManager") as? ObjCClass ?: run {
47
- log("swizzle skipped: UIStatusBarManager class not found")
48
- return
49
- }
50
- val selector = sel_registerName("handleTapAction:") ?: run {
51
- log("swizzle skipped: handleTapAction: selector unavailable")
52
- return
53
- }
54
- val method = class_getInstanceMethod(cls, selector) ?: run {
55
- log("swizzle skipped: handleTapAction: method not found")
56
- return
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}")
57
51
  }
58
- // Capture original IMP so we can restore it on uninstall.
59
- originalHandleTapImp = method_getImplementation(method)
60
- val typeEncoding = method_getTypeEncoding(method)?.toKString()
61
- class_replaceMethod(cls, selector, replacementHandleTap.reinterpret(), typeEncoding)
62
- swizzleInstalled = true
63
- log("UIStatusBarManager handleTapAction: replaced (originalImp captured)")
64
- } catch (e: Throwable) {
65
- log("install error: ${e.message}")
66
52
  }
67
- }
68
53
 
69
- @OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)
70
- private fun uninstallStatusBarSwizzle() {
71
- try {
72
- if (!swizzleInstalled) return
73
- val original = originalHandleTapImp ?: run {
74
- log("uninstall skipped: no original IMP captured")
75
- return
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}")
76
70
  }
77
- val cls = objc_getClass("UIStatusBarManager") as? ObjCClass ?: return
78
- val selector = sel_registerName("handleTapAction:") ?: return
79
- val method = class_getInstanceMethod(cls, selector) ?: return
80
- method_setImplementation(method, original)
81
- originalHandleTapImp = null
82
- swizzleInstalled = false
83
- log("UIStatusBarManager handleTapAction: original IMP restored")
84
- } catch (e: Throwable) {
85
- log("uninstall error: ${e.message}")
86
71
  }
87
72
  }
88
73
 
@@ -93,6 +78,9 @@ private class StatusBarObserver {
93
78
  if (token != null) {
94
79
  return
95
80
  }
81
+ if (StatusBarSwizzleManager.observerCount == 0) {
82
+ StatusBarSwizzleManager.install()
83
+ }
96
84
  token = NSNotificationCenter.defaultCenter.addObserverForName(
97
85
  name = "statusBarSelected",
98
86
  `object` = null,
@@ -101,6 +89,8 @@ private class StatusBarObserver {
101
89
  log("statusBar tap notification → ScrollToTopRegistry.trigger()")
102
90
  callback?.invoke()
103
91
  }
92
+ StatusBarSwizzleManager.observerCount++
93
+ log("observerCount acquire ${StatusBarSwizzleManager.observerCount}")
104
94
 
105
95
  }
106
96
 
@@ -108,6 +98,11 @@ private class StatusBarObserver {
108
98
  if (token == null) return
109
99
  token?.let { NSNotificationCenter.defaultCenter.removeObserver(it) }
110
100
  token = null
101
+ StatusBarSwizzleManager.observerCount--
102
+ if (StatusBarSwizzleManager.observerCount == 0) {
103
+ StatusBarSwizzleManager.uninstall()
104
+ }
105
+ log("observerCount release ${StatusBarSwizzleManager.observerCount}")
111
106
  }
112
107
  }
113
108
 
@@ -130,10 +125,8 @@ private fun log(msg: String) = println("${ts()} $TAG $msg")
130
125
 
131
126
 
132
127
  @Composable
133
- internal actual fun RegisterScrollToTop(navigator: Navigation, callback: (() -> Unit)?) {
134
- val token = remember { Any() }
135
-
136
- DisposableEffect(navigator, callback, token) {
128
+ internal actual fun RegisterScrollToTop(callback: (() -> Unit)?) {
129
+ DisposableEffect(callback) {
137
130
  val acquired = callback != null
138
131
  val statusBarObserver = StatusBarObserver()
139
132
  if (acquired) {
@@ -146,9 +139,9 @@ internal actual fun RegisterScrollToTop(navigator: Navigation, callback: (() ->
146
139
  }
147
140
 
148
141
  internal actual fun onNavigatorEntered(navigator: Navigator) {
149
- installStatusBarSwizzle()
142
+ StatusBarSwizzleManager.install()
150
143
  }
151
144
 
152
145
  internal actual fun onNavigatorExited(navigator: Navigator) {
153
- uninstallStatusBarSwizzle()
146
+ StatusBarSwizzleManager.uninstall()
154
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.20
21
+ version=0.160.1-scrolltotop.21
22
22
 
23
23
  repo=GitLab
24
24
  url=https://gitlab.mservice.com.vn/api/v4/projects/5400/packages/maven
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/native-kits",
3
- "version": "0.160.1-scrolltotop.20-debug",
3
+ "version": "0.160.1-scrolltotop.21-debug",
4
4
  "private": false,
5
5
  "dependencies": {},
6
6
  "devDependencies": {},