@nebula-rn/host 0.0.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.
Files changed (37) hide show
  1. package/NebulaHost.podspec +23 -0
  2. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  3. package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
  4. package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
  5. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  6. package/android/.gradle/8.9/gc.properties +0 -0
  7. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  8. package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
  9. package/android/.gradle/vcs-1/gc.properties +0 -0
  10. package/android/build.gradle +27 -0
  11. package/android/consumer-rules.pro +1 -0
  12. package/android/src/main/AndroidManifest.xml +1 -0
  13. package/android/src/main/java/com/hectorzhuang/nebula/NebulaActivity.kt +290 -0
  14. package/android/src/main/java/com/hectorzhuang/nebula/NebulaAppManager.kt +134 -0
  15. package/android/src/main/java/com/hectorzhuang/nebula/NebulaConfig.kt +324 -0
  16. package/android/src/main/java/com/hectorzhuang/nebula/NebulaEventHub.kt +49 -0
  17. package/android/src/main/java/com/hectorzhuang/nebula/NebulaHost.kt +145 -0
  18. package/android/src/main/java/com/hectorzhuang/nebula/NebulaHostModalActivity.kt +178 -0
  19. package/android/src/main/java/com/hectorzhuang/nebula/NebulaManifestManager.kt +130 -0
  20. package/android/src/main/java/com/hectorzhuang/nebula/NebulaNativeModule.kt +604 -0
  21. package/android/src/main/java/com/hectorzhuang/nebula/NebulaPackage.kt +16 -0
  22. package/android/src/main/java/com/hectorzhuang/nebula/NebulaRouter.kt +300 -0
  23. package/ios/Nebula/NebulaAppManager.swift +355 -0
  24. package/ios/Nebula/NebulaConfig.swift +549 -0
  25. package/ios/Nebula/NebulaContainerController.swift +580 -0
  26. package/ios/Nebula/NebulaDevLoading.swift +333 -0
  27. package/ios/Nebula/NebulaHost.swift +611 -0
  28. package/ios/Nebula/NebulaManifest.swift +214 -0
  29. package/ios/Nebula/NebulaNativeModule.swift +682 -0
  30. package/ios/Nebula/NebulaNativeModuleBridge.m +364 -0
  31. package/ios/Nebula/NebulaPerformanceMonitor.swift +46 -0
  32. package/ios/Nebula/NebulaRouter.swift +594 -0
  33. package/ios/Nebula/NebulaRouterBridge.m +19 -0
  34. package/ios/Nebula/RNInstanceViewController.swift +52 -0
  35. package/package.json +41 -0
  36. package/react-native.config.js +14 -0
  37. package/src/index.ts +9 -0
@@ -0,0 +1,604 @@
1
+ package com.hectorzhuang.nebula
2
+
3
+ import android.os.Build
4
+ import android.widget.Toast
5
+ import android.content.Intent
6
+ import com.facebook.react.bridge.Arguments
7
+ import com.facebook.react.bridge.Promise
8
+ import com.facebook.react.bridge.ReactApplicationContext
9
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
10
+ import com.facebook.react.bridge.ReactMethod
11
+ import com.facebook.react.bridge.ReadableMap
12
+ import com.facebook.react.modules.core.DeviceEventManagerModule
13
+
14
+ class NebulaNativeModule(
15
+ reactContext: ReactApplicationContext,
16
+ ) : ReactContextBaseJavaModule(reactContext) {
17
+ companion object {
18
+ private const val HOST_MESSAGE_EVENT = "NebulaHostMessage"
19
+ private const val MINI_APP_MESSAGE_EVENT = "NebulaMiniAppMessage"
20
+ private const val PAGE_LIFECYCLE_EVENT = "NebulaPageLifecycle"
21
+ }
22
+
23
+ private val emitterId = NebulaEventHub.nextEmitterId()
24
+ private var currentRuntimeAppId: String? = null
25
+ private var listenerCount = 0
26
+
27
+ init {
28
+ NebulaEventHub.register(this)
29
+ }
30
+
31
+ override fun getName(): String = "NebulaNativeModule"
32
+
33
+ override fun invalidate() {
34
+ NebulaEventHub.unregister(this)
35
+ super.invalidate()
36
+ }
37
+
38
+ @ReactMethod
39
+ fun addListener(eventName: String) {
40
+ listenerCount += 1
41
+ }
42
+
43
+ @ReactMethod
44
+ fun removeListeners(count: Double) {
45
+ listenerCount = (listenerCount - count.toInt()).coerceAtLeast(0)
46
+ }
47
+
48
+ @ReactMethod
49
+ fun openMiniApp(appId: String, initialProps: ReadableMap?, promise: Promise) {
50
+ val activity = reactApplicationContext.getCurrentActivity() ?: NebulaHost.currentActivity()
51
+ if (activity == null) {
52
+ promise.reject("NO_ACTIVITY", "Cannot find an active Activity")
53
+ return
54
+ }
55
+ activity.runOnUiThread {
56
+ runCatching {
57
+ NebulaHost.openApp(activity, appId, initialProps?.toHashMap() ?: emptyMap())
58
+ }.onSuccess {
59
+ promise.resolve(Arguments.makeNativeMap(mapOf("success" to true, "appId" to appId)))
60
+ }.onFailure { error ->
61
+ promise.reject("OPEN_ERROR", error.message, error)
62
+ }
63
+ }
64
+ }
65
+
66
+ @ReactMethod
67
+ fun preloadMiniApp(appId: String, promise: Promise) {
68
+ runCatching {
69
+ NebulaHost.preloadApp(appId)
70
+ promise.resolve(Arguments.makeNativeMap(mapOf("success" to true, "appId" to appId)))
71
+ }.onFailure { error ->
72
+ promise.reject("PRELOAD_ERROR", error.message, error)
73
+ }
74
+ }
75
+
76
+ @ReactMethod
77
+ fun openMiniAppWithBundleURL(
78
+ appId: String,
79
+ bundleURL: String,
80
+ connectToURLMetroServer: Boolean,
81
+ initialProps: ReadableMap?,
82
+ promise: Promise,
83
+ ) {
84
+ runCatching {
85
+ NebulaHost.installApp(
86
+ appId,
87
+ bundleURL,
88
+ connectToURLMetroServer,
89
+ )
90
+ val activity =
91
+ reactApplicationContext.currentActivity ?: NebulaHost.currentActivity()
92
+ ?: error("Cannot find an active Activity")
93
+ activity.runOnUiThread {
94
+ NebulaHost.openApp(activity, appId, initialProps?.toHashMap() ?: emptyMap())
95
+ promise.resolve(
96
+ Arguments.makeNativeMap(
97
+ mapOf("success" to true, "appId" to appId),
98
+ ),
99
+ )
100
+ }
101
+ }.onFailure { error ->
102
+ promise.reject("OPEN_ERROR", error.message, error)
103
+ }
104
+ }
105
+
106
+ @ReactMethod
107
+ fun preloadMiniAppWithBundleURL(
108
+ appId: String,
109
+ bundleURL: String,
110
+ connectToURLMetroServer: Boolean,
111
+ promise: Promise,
112
+ ) {
113
+ runCatching {
114
+ NebulaHost.installApp(
115
+ appId,
116
+ bundleURL,
117
+ connectToURLMetroServer,
118
+ )
119
+ NebulaHost.preloadApp(appId)
120
+ }.onSuccess {
121
+ promise.resolve(
122
+ Arguments.makeNativeMap(
123
+ mapOf("success" to true, "appId" to appId),
124
+ ),
125
+ )
126
+ }.onFailure { error ->
127
+ promise.reject("PRELOAD_ERROR", error.message, error)
128
+ }
129
+ }
130
+
131
+ @ReactMethod
132
+ fun installMiniApp(appId: String, bundleURL: String, promise: Promise) {
133
+ installMiniAppWithBundleURL(appId, bundleURL, false, promise)
134
+ }
135
+
136
+ @ReactMethod
137
+ fun installMiniAppWithBundleURL(
138
+ appId: String,
139
+ bundleURL: String,
140
+ connectToURLMetroServer: Boolean,
141
+ promise: Promise,
142
+ ) {
143
+ runCatching {
144
+ NebulaHost.installApp(
145
+ appId,
146
+ bundleURL,
147
+ connectToURLMetroServer,
148
+ )
149
+ }.onSuccess {
150
+ promise.resolve(
151
+ Arguments.makeNativeMap(
152
+ mapOf(
153
+ "success" to true,
154
+ "appId" to appId,
155
+ )
156
+ ),
157
+ )
158
+ }.onFailure { error ->
159
+ promise.reject("INSTALL_ERROR", error.message, error)
160
+ }
161
+ }
162
+
163
+ @ReactMethod
164
+ fun installMiniAppFromURLs(
165
+ appId: String,
166
+ bundleURL: String,
167
+ manifestURL: String,
168
+ assetsURL: String,
169
+ connectToURLMetroServer: Boolean,
170
+ promise: Promise,
171
+ ) {
172
+ runCatching {
173
+ NebulaConfig.installApp(
174
+ appId,
175
+ bundleURL,
176
+ manifestURL,
177
+ assetsURL,
178
+ connectToURLMetroServer,
179
+ )
180
+ NebulaAppManager.invalidate(appId)
181
+ }.onSuccess {
182
+ promise.resolve(
183
+ Arguments.makeNativeMap(
184
+ mapOf("success" to true, "appId" to appId),
185
+ ),
186
+ )
187
+ }.onFailure { error ->
188
+ promise.reject("INSTALL_ERROR", error.message, error)
189
+ }
190
+ }
191
+
192
+ @ReactMethod
193
+ fun closeMiniApp(appId: String, promise: Promise) {
194
+ val activity = reactApplicationContext.currentActivity ?: NebulaHost.currentActivity()
195
+ if (activity == null) {
196
+ promise.reject("NO_ACTIVITY", "Cannot find an active Activity")
197
+ return
198
+ }
199
+
200
+ activity.runOnUiThread {
201
+ runCatching {
202
+ NebulaHost.closeApp(appId)
203
+ }.onSuccess {
204
+ promise.resolve(
205
+ Arguments.makeNativeMap(
206
+ mapOf("success" to true, "appId" to appId),
207
+ ),
208
+ )
209
+ }.onFailure { error ->
210
+ promise.reject("CLOSE_ERROR", error.message, error)
211
+ }
212
+ }
213
+ }
214
+
215
+ @ReactMethod
216
+ fun uninstallMiniApp(appId: String, promise: Promise) {
217
+ runCatching {
218
+ NebulaHost.uninstallApp(appId)
219
+ }.onSuccess {
220
+ promise.resolve(
221
+ Arguments.makeNativeMap(
222
+ mapOf("success" to true, "appId" to appId),
223
+ ),
224
+ )
225
+ }.onFailure { error ->
226
+ promise.reject("UNINSTALL_ERROR", error.message, error)
227
+ }
228
+ }
229
+
230
+ @ReactMethod
231
+ fun getInstalledMiniApps(promise: Promise) {
232
+ promise.resolve(Arguments.makeNativeMap(mapOf("apps" to NebulaHost.installedApps())))
233
+ }
234
+
235
+ @ReactMethod
236
+ fun getInstalledMiniAppInfo(appId: String, promise: Promise) {
237
+ val installedApp = NebulaConfig.getInstalledApp(appId)
238
+ if (installedApp == null) {
239
+ promise.resolve(
240
+ Arguments.makeNativeMap(
241
+ mapOf(
242
+ "installed" to false,
243
+ "app" to null,
244
+ ),
245
+ ),
246
+ )
247
+ return
248
+ }
249
+
250
+ promise.resolve(
251
+ Arguments.makeNativeMap(
252
+ mapOf(
253
+ "installed" to true,
254
+ "app" to mapOf(
255
+ "appId" to installedApp.appId,
256
+ "mode" to installedApp.mode,
257
+ "bundlePath" to installedApp.bundlePath,
258
+ "sourceUrl" to installedApp.sourceUrl,
259
+ "version" to installedApp.version,
260
+ "updateStrategy" to installedApp.updateStrategy,
261
+ ),
262
+ ),
263
+ ),
264
+ )
265
+ }
266
+
267
+ @ReactMethod
268
+ fun getServerBaseURL(promise: Promise) {
269
+ promise.resolve(
270
+ Arguments.makeNativeMap(
271
+ mapOf("serverBaseURL" to NebulaConfig.serverBaseURL),
272
+ ),
273
+ )
274
+ }
275
+
276
+ @ReactMethod
277
+ fun setServerBaseURL(serverBaseURL: String?, promise: Promise) {
278
+ val trimmedValue = serverBaseURL?.trim().orEmpty()
279
+ if (trimmedValue.isNotEmpty() && NebulaConfig.normalizeServerBaseURL(trimmedValue) == null) {
280
+ promise.reject("INVALID_SERVER_URL", "serverBaseURL must be a valid http(s) URL")
281
+ return
282
+ }
283
+ NebulaConfig.serverBaseURL = trimmedValue.ifEmpty { null }
284
+ promise.resolve(
285
+ Arguments.makeNativeMap(
286
+ mapOf("serverBaseURL" to NebulaConfig.serverBaseURL),
287
+ ),
288
+ )
289
+ }
290
+
291
+ @ReactMethod
292
+ fun setMiniappLoadingDelay(delayMs: Double?, promise: Promise) {
293
+ val normalizedDelayMs =
294
+ delayMs
295
+ ?.takeIf { it.isFinite() }
296
+ ?.toLong()
297
+ ?.coerceAtLeast(0L)
298
+ ?: 0L
299
+ NebulaRouter.setMiniappLoadingDelay(normalizedDelayMs)
300
+ promise.resolve(
301
+ Arguments.makeNativeMap(
302
+ mapOf("delayMs" to normalizedDelayMs.toDouble()),
303
+ ),
304
+ )
305
+ }
306
+
307
+ @ReactMethod
308
+ fun setMiniappLoadingEnterContentDelay(delayMs: Double?, promise: Promise) {
309
+ val normalizedDelayMs =
310
+ delayMs
311
+ ?.takeIf { it.isFinite() }
312
+ ?.toLong()
313
+ ?.coerceAtLeast(0L)
314
+ ?: 0L
315
+ NebulaRouter.setMiniappLoadingEnterContentDelay(normalizedDelayMs)
316
+ promise.resolve(
317
+ Arguments.makeNativeMap(
318
+ mapOf("delayMs" to normalizedDelayMs.toDouble()),
319
+ ),
320
+ )
321
+ }
322
+
323
+ @ReactMethod
324
+ fun registerRoutes(appId: String, routes: ReadableMap, promise: Promise) {
325
+ val pages =
326
+ routes.toHashMap().mapValues { it.value?.toString().orEmpty() }
327
+ .filterValues { it.isNotBlank() }
328
+ currentRuntimeAppId = appId
329
+ NebulaManifestManager.registerRoutes(appId, pages)
330
+ promise.resolve(
331
+ Arguments.makeNativeMap(
332
+ mapOf(
333
+ "success" to true,
334
+ "appId" to appId,
335
+ "count" to pages.size
336
+ )
337
+ )
338
+ )
339
+ }
340
+
341
+ @ReactMethod
342
+ fun registerManifest(appId: String, manifest: ReadableMap, promise: Promise) {
343
+ runCatching {
344
+ val map = manifest.toHashMap()
345
+ val pages =
346
+ (map["pages"] as? Map<*, *>)
347
+ ?.entries
348
+ ?.mapNotNull { entry ->
349
+ val route = entry.key?.toString() ?: return@mapNotNull null
350
+ val componentName = entry.value?.toString() ?: return@mapNotNull null
351
+ route to componentName
352
+ }
353
+ ?.toMap()
354
+ ?: emptyMap<String, String>()
355
+ val pageConfigs =
356
+ (map["pageConfigs"] as? Map<*, *>)
357
+ ?.entries
358
+ ?.mapNotNull { entry ->
359
+ val route = entry.key?.toString() ?: return@mapNotNull null
360
+ @Suppress("UNCHECKED_CAST")
361
+ route to ((entry.value as? Map<String, Any?>) ?: emptyMap<String, Any?>())
362
+ }
363
+ ?.toMap()
364
+ ?: emptyMap<String, Map<String, Any?>>()
365
+ val window = (map["window"] as? Map<String, Any?>) ?: emptyMap<String, Any?>()
366
+ val parsed =
367
+ NebulaManifest(
368
+ appId = appId,
369
+ entryPagePath = map["entryPagePath"]?.toString(),
370
+ pages = pages,
371
+ pageConfigs = pageConfigs,
372
+ updateStrategy = map["updateStrategy"]?.toString() ?: "manual",
373
+ version = map["version"]?.toString(),
374
+ window = window,
375
+ )
376
+ currentRuntimeAppId = appId
377
+ NebulaManifestManager.registerManifest(appId, parsed)
378
+ NebulaRouter.updatePageStyle(
379
+ appId,
380
+ NebulaManifestManager.getPageConfig(appId, parsed.entryPagePath ?: "/")
381
+ )
382
+ promise.resolve(
383
+ Arguments.makeNativeMap(
384
+ mapOf(
385
+ "success" to true,
386
+ "appId" to appId,
387
+ "count" to pages.size
388
+ )
389
+ ),
390
+ )
391
+ }.onFailure { error ->
392
+ promise.reject("INVALID_MANIFEST", error.message, error)
393
+ }
394
+ }
395
+
396
+ @ReactMethod
397
+ fun postMessageToHost(appId: String, message: ReadableMap, promise: Promise) {
398
+ currentRuntimeAppId = appId
399
+ NebulaEventHub.publishBridgeMessage(
400
+ NebulaBridgeMessage(
401
+ direction = "toHost",
402
+ appId = appId,
403
+ message = message.toHashMap(),
404
+ sourceEmitterId = emitterId,
405
+ timestamp = System.currentTimeMillis() / 1000.0,
406
+ ),
407
+ )
408
+ promise.resolve(Arguments.makeNativeMap(mapOf("errMsg" to "postMessageToHost:ok")))
409
+ }
410
+
411
+ @ReactMethod
412
+ fun postMessageToMiniApp(appId: String, message: ReadableMap, promise: Promise) {
413
+ NebulaEventHub.publishBridgeMessage(
414
+ NebulaBridgeMessage(
415
+ direction = "toMiniApp",
416
+ appId = appId,
417
+ message = message.toHashMap(),
418
+ sourceEmitterId = emitterId,
419
+ timestamp = System.currentTimeMillis() / 1000.0,
420
+ ),
421
+ )
422
+ promise.resolve(Arguments.makeNativeMap(mapOf("errMsg" to "postMessageToMiniApp:ok")))
423
+ }
424
+
425
+ @ReactMethod
426
+ fun presentHostModal(moduleName: String, props: ReadableMap?, promise: Promise) {
427
+ val activity = reactApplicationContext.getCurrentActivity() ?: NebulaHost.currentActivity()
428
+ if (activity == null) {
429
+ promise.resolve(Arguments.makeNativeMap(mapOf("errMsg" to "presentHostModal:fail no_activity")))
430
+ return
431
+ }
432
+
433
+ activity.runOnUiThread {
434
+ runCatching {
435
+ val intent =
436
+ NebulaHostModalActivity.createIntent(
437
+ activity,
438
+ moduleName,
439
+ props?.toHashMap() ?: emptyMap(),
440
+ ).apply {
441
+ addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
442
+ }
443
+ activity.startActivity(intent)
444
+ activity.overridePendingTransition(0, 0)
445
+ }.onSuccess {
446
+ promise.resolve(Arguments.makeNativeMap(mapOf("errMsg" to "presentHostModal:ok")))
447
+ }.onFailure { error ->
448
+ promise.reject("PRESENT_HOST_MODAL_ERROR", error.message, error)
449
+ }
450
+ }
451
+ }
452
+
453
+ @ReactMethod
454
+ fun dismissHostModal(promise: Promise) {
455
+ val activity = NebulaHost.presentedHostModalActivity()
456
+ if (activity == null) {
457
+ promise.resolve(Arguments.makeNativeMap(mapOf("errMsg" to "dismissHostModal:ok")))
458
+ return
459
+ }
460
+
461
+ activity.runOnUiThread {
462
+ runCatching {
463
+ activity.finish()
464
+ activity.overridePendingTransition(0, 0)
465
+ }.onSuccess {
466
+ promise.resolve(Arguments.makeNativeMap(mapOf("errMsg" to "dismissHostModal:ok")))
467
+ }.onFailure { error ->
468
+ promise.reject("DISMISS_HOST_MODAL_ERROR", error.message, error)
469
+ }
470
+ }
471
+ }
472
+
473
+ @ReactMethod
474
+ fun navigateTo(appId: String, url: String, promise: Promise) {
475
+ resolveNavigation("navigateTo", promise) { NebulaRouter.navigateToURL(url, appId) }
476
+ }
477
+
478
+ @ReactMethod
479
+ fun redirectTo(appId: String, url: String, promise: Promise) {
480
+ resolveNavigation("redirectTo", promise) { NebulaRouter.redirectToURL(url, appId) }
481
+ }
482
+
483
+ @ReactMethod
484
+ fun reLaunch(appId: String, url: String, promise: Promise) {
485
+ resolveNavigation("reLaunch", promise) { NebulaRouter.reLaunchURL(url, appId) }
486
+ }
487
+
488
+ @ReactMethod
489
+ fun navigateBack(appId: String, delta: Double, promise: Promise) {
490
+ resolveNavigation("navigateBack", promise) {
491
+ NebulaRouter.navigateBack(
492
+ appId,
493
+ delta.toInt()
494
+ )
495
+ }
496
+ }
497
+
498
+ @ReactMethod
499
+ fun setPageStyle(appId: String, style: ReadableMap, promise: Promise) {
500
+ resolveNavigation("setPageStyle", promise) {
501
+ NebulaRouter.updatePageStyle(appId, style.toHashMap())
502
+ }
503
+ }
504
+
505
+ @ReactMethod
506
+ fun showToast(title: String, promise: Promise) {
507
+ val activity = reactApplicationContext.getCurrentActivity() ?: NebulaHost.currentActivity()
508
+ if (activity == null) {
509
+ promise.resolve(Arguments.makeNativeMap(mapOf("errMsg" to "showToast:fail no_activity")))
510
+ return
511
+ }
512
+ activity.runOnUiThread {
513
+ Toast.makeText(activity, title, Toast.LENGTH_SHORT).show()
514
+ promise.resolve(Arguments.makeNativeMap(mapOf("errMsg" to "showToast:ok")))
515
+ }
516
+ }
517
+
518
+ @ReactMethod(isBlockingSynchronousMethod = true)
519
+ fun getDeviceInfo(): Map<String, Any?> {
520
+ return mapOf(
521
+ "platform" to "Android",
522
+ "systemVersion" to Build.VERSION.RELEASE,
523
+ "model" to Build.MODEL,
524
+ )
525
+ }
526
+
527
+ internal fun handleBridgeMessage(message: NebulaBridgeMessage) {
528
+ if (listenerCount <= 0 || message.sourceEmitterId == emitterId) {
529
+ return
530
+ }
531
+ when (message.direction) {
532
+ "toHost" -> {
533
+ if (currentRuntimeAppId != null) {
534
+ return
535
+ }
536
+ emitEvent(
537
+ HOST_MESSAGE_EVENT,
538
+ mapOf(
539
+ "appId" to message.appId,
540
+ "message" to message.message,
541
+ "timestamp" to message.timestamp,
542
+ ),
543
+ )
544
+ }
545
+
546
+ "toMiniApp" -> {
547
+ if (currentRuntimeAppId != message.appId) {
548
+ return
549
+ }
550
+ emitEvent(
551
+ MINI_APP_MESSAGE_EVENT,
552
+ mapOf(
553
+ "appId" to message.appId,
554
+ "message" to message.message,
555
+ "timestamp" to message.timestamp,
556
+ ),
557
+ )
558
+ }
559
+ }
560
+ }
561
+
562
+ internal fun handlePageLifecycle(event: NebulaPageLifecycleEvent) {
563
+ if (listenerCount <= 0) {
564
+ return
565
+ }
566
+ if (currentRuntimeAppId != null && currentRuntimeAppId != event.appId) {
567
+ return
568
+ }
569
+ emitEvent(
570
+ PAGE_LIFECYCLE_EVENT,
571
+ mapOf(
572
+ "appId" to event.appId,
573
+ "instanceId" to event.instanceId,
574
+ "routePath" to event.routePath,
575
+ "type" to event.type,
576
+ ),
577
+ )
578
+ }
579
+
580
+ private fun emitEvent(eventName: String, payload: Map<String, Any?>) {
581
+ reactApplicationContext
582
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
583
+ .emit(eventName, Arguments.makeNativeMap(payload))
584
+ }
585
+
586
+ private fun resolveNavigation(
587
+ method: String,
588
+ promise: Promise,
589
+ action: () -> Result<Unit>,
590
+ ) {
591
+ val activity = reactApplicationContext.getCurrentActivity() ?: NebulaHost.currentActivity()
592
+ if (activity == null) {
593
+ promise.reject("no_activity", "$method:fail no activity")
594
+ return
595
+ }
596
+ activity.runOnUiThread {
597
+ action().onSuccess {
598
+ promise.resolve(Arguments.makeNativeMap(mapOf("errMsg" to "$method:ok")))
599
+ }.onFailure { error ->
600
+ promise.reject(method, "$method:fail ${error.message ?: "unknown"}")
601
+ }
602
+ }
603
+ }
604
+ }
@@ -0,0 +1,16 @@
1
+ package com.hectorzhuang.nebula
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+ class NebulaPackage : ReactPackage {
9
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
10
+ return listOf(NebulaNativeModule(reactContext))
11
+ }
12
+
13
+ override fun createViewManagers(
14
+ reactContext: ReactApplicationContext,
15
+ ): List<ViewManager<*, *>> = emptyList()
16
+ }