@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.
- package/compose/build.gradle.kts +1 -1
- package/compose/compose.podspec +1 -1
- package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +1 -3
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -7
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +3 -3
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +63 -70
- package/gradle.properties +1 -1
- package/package.json +1 -1
package/compose/build.gradle.kts
CHANGED
package/compose/compose.podspec
CHANGED
|
@@ -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(
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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(
|
|
134
|
-
|
|
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
|
-
|
|
142
|
+
StatusBarSwizzleManager.install()
|
|
150
143
|
}
|
|
151
144
|
|
|
152
145
|
internal actual fun onNavigatorExited(navigator: Navigator) {
|
|
153
|
-
|
|
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.
|
|
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
|