@ionic/portals-react-native 0.5.1 → 0.6.0

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 (48) hide show
  1. package/ReactNativePortals.podspec +39 -14
  2. package/android/build.gradle +78 -108
  3. package/android/gradle.properties +5 -3
  4. package/android/src/main/java/io/ionic/portals/reactnative/PortalView.kt +11 -8
  5. package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalManager.kt +37 -16
  6. package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalsModule.kt +2 -3
  7. package/android/src/main/java/io/ionic/portals/reactnative/ReactNativeWebVitalsModule.kt +11 -19
  8. package/ios/IonicPortals+Codable.swift +77 -0
  9. package/ios/LiveUpdateManager+Async.swift +16 -46
  10. package/ios/Podfile +29 -4
  11. package/ios/Podfile.lock +478 -241
  12. package/ios/Portal.swift +50 -54
  13. package/ios/PortalView.swift +26 -8
  14. package/ios/PortalsConfig.swift +1 -88
  15. package/ios/PortalsReactNative-Bridging-Header.h +2 -0
  16. package/ios/PortalsReactNative.swift +41 -36
  17. package/ios/SyncResult+SyncError+Encodable.swift +55 -0
  18. package/ios/WebVitals.swift +19 -26
  19. package/package.json +62 -43
  20. package/src/{PortalView.android.tsx → BasePortalView.android.tsx} +2 -3
  21. package/src/{PortalView.tsx → BasePortalView.tsx} +2 -3
  22. package/src/{index.ts → index.tsx} +77 -39
  23. package/ios/LiveUpdate+Dict.swift +0 -30
  24. package/ios/LiveUpdateManagerError+Dict.swift +0 -19
  25. package/ios/ReactNativePortals.xcodeproj/project.pbxproj +0 -465
  26. package/ios/ReactNativePortals.xcodeproj/xcshareddata/xcschemes/ReactNativePortals.xcscheme +0 -67
  27. package/ios/ReactNativePortals.xcworkspace/contents.xcworkspacedata +0 -10
  28. package/ios/ReactNativePortals.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  29. package/ios/SyncResult+Dict.swift +0 -35
  30. package/lib/commonjs/PortalView.android.js +0 -28
  31. package/lib/commonjs/PortalView.android.js.map +0 -1
  32. package/lib/commonjs/PortalView.js +0 -15
  33. package/lib/commonjs/PortalView.js.map +0 -1
  34. package/lib/commonjs/index.js +0 -200
  35. package/lib/commonjs/index.js.map +0 -1
  36. package/lib/module/PortalView.android.js +0 -20
  37. package/lib/module/PortalView.android.js.map +0 -1
  38. package/lib/module/PortalView.js +0 -8
  39. package/lib/module/PortalView.js.map +0 -1
  40. package/lib/module/index.js +0 -176
  41. package/lib/module/index.js.map +0 -1
  42. package/lib/typescript/PortalView.android.d.ts +0 -4
  43. package/lib/typescript/PortalView.d.ts +0 -4
  44. package/lib/typescript/index.d.ts +0 -181
  45. /package/ios/{PortalManager.m → PortalManager.mm} +0 -0
  46. /package/ios/{PortalView.m → PortalView.mm} +0 -0
  47. /package/ios/{PortalWebVitals.m → PortalWebVitals.mm} +0 -0
  48. /package/ios/{PortalsPubSub.m → PortalsPubSub.mm} +0 -0
@@ -1,21 +1,46 @@
1
- require 'json'
1
+ require "json"
2
2
 
3
- package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+ folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
4
5
 
5
6
  Pod::Spec.new do |s|
6
- s.name = 'ReactNativePortals'
7
- s.version = package['version']
8
- s.summary = package['description']
9
- s.homepage = package['homepage']
10
- s.license = package['license']
11
- s.authors = package['author']
7
+ s.name = "ReactNativePortals"
8
+ s.version = package["version"]
9
+ s.summary = package["description"]
10
+ s.homepage = package["homepage"]
11
+ s.license = package["license"]
12
+ s.authors = package["author"]
12
13
 
13
- s.platforms = { ios: '13.0' }
14
- s.source = { git: 'https://github.com/ionic-team/react-native-ionic-portals.git', tag: "#{s.version}" }
14
+ s.platforms = { :ios => min_ios_version_supported }
15
+ s.source = { :git => "https://github.com/ionic-team/ionic-portals-react-native.git", :tag => "#{s.version}" }
15
16
 
16
- s.source_files = 'ios/**/*.{h,m,mm,swift}'
17
+ s.source_files = "ios/**/*.{h,m,mm,swift}"
17
18
 
18
- s.dependency 'React-Core'
19
- s.dependency 'IonicPortals', '~> 0.8.0'
20
- s.dependency 'IonicLiveUpdates', '~> 0.4.0'
19
+ # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
20
+ # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
21
+ if respond_to?(:install_modules_dependencies, true)
22
+ s.dependency 'IonicPortals', '~> 0.11.0'
23
+ s.dependency 'IonicLiveUpdates', '~> 0.5.2'
24
+ install_modules_dependencies(s)
25
+
26
+ else
27
+ s.dependency "React-Core"
28
+ s.dependency 'IonicPortals', '~> 0.11.0'
29
+ s.dependency 'IonicLiveUpdates', '~> 0.5.2'
30
+
31
+ # Don't install the dependencies when we run `pod install` in the old architecture.
32
+ if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
33
+ s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
34
+ s.pod_target_xcconfig = {
35
+ "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
36
+ "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
37
+ "CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
38
+ }
39
+ s.dependency "React-Codegen"
40
+ s.dependency "RCT-Folly"
41
+ s.dependency "RCTRequired"
42
+ s.dependency "RCTTypeSafety"
43
+ s.dependency "ReactCommon/turbomodule/core"
44
+ end
45
+ end
21
46
  end
@@ -1,131 +1,101 @@
1
1
  buildscript {
2
- // Buildscript is evaluated before everything else so we can't use getExtOrDefault
3
- def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : project.properties['ReactNativePortals_kotlinVersion']
2
+ // Buildscript is evaluated before everything else so we can't use getExtOrDefault
3
+ def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["PortalsReactNative_kotlinVersion"]
4
4
 
5
- repositories {
6
- google()
7
- mavenCentral()
8
- }
5
+ repositories {
6
+ google()
7
+ mavenCentral()
8
+ }
9
9
 
10
- dependencies {
11
- classpath 'com.android.tools.build:gradle:7.1.2'
12
- // noinspection DifferentKotlinGradleVersion
13
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14
- }
10
+ dependencies {
11
+ classpath "com.android.tools.build:gradle:7.2.1"
12
+ // noinspection DifferentKotlinGradleVersion
13
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14
+ }
15
15
  }
16
16
 
17
- apply plugin: 'com.android.library'
18
- apply plugin: 'kotlin-android'
17
+ def reactNativeArchitectures() {
18
+ def value = rootProject.getProperties().get("reactNativeArchitectures")
19
+ return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
20
+ }
19
21
 
20
- def getExtOrIntegerDefault(name) {
21
- return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties['ReactNativePortals_' + name]).toInteger()
22
+ def isNewArchitectureEnabled() {
23
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
22
24
  }
23
25
 
24
- android {
25
- compileSdkVersion getExtOrIntegerDefault('compileSdkVersion')
26
- defaultConfig {
27
- minSdkVersion 16
28
- targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
29
- versionCode 1
30
- versionName "1.0"
31
- }
26
+ apply plugin: "com.android.library"
27
+ apply plugin: "kotlin-android"
32
28
 
33
- buildTypes {
34
- release {
35
- minifyEnabled false
36
- }
37
- }
38
- lintOptions {
39
- disable 'GradleCompatible'
40
- }
29
+ if (isNewArchitectureEnabled()) {
30
+ apply plugin: "com.facebook.react"
41
31
  }
42
32
 
43
- tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
44
- sourceCompatibility = JavaVersion.VERSION_17
45
- targetCompatibility = JavaVersion.VERSION_17
33
+ def getExtOrDefault(name) {
34
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["PortalsReactNative_" + name]
35
+ }
46
36
 
47
- kotlinOptions {
48
- freeCompilerArgs += '-opt-in=kotlin.RequiresOptIn'
49
- }
37
+ def getExtOrIntegerDefault(name) {
38
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["PortalsReactNative_" + name]).toInteger()
50
39
  }
51
40
 
52
- repositories {
53
- mavenCentral()
54
- google()
41
+ def supportsNamespace() {
42
+ def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
43
+ def major = parsed[0].toInteger()
44
+ def minor = parsed[1].toInteger()
55
45
 
56
- def found = false
57
- def defaultDir = null
58
- def androidSourcesName = 'React Native sources'
59
-
60
- if (rootProject.ext.has('reactNativeAndroidRoot')) {
61
- defaultDir = rootProject.ext.get('reactNativeAndroidRoot')
62
- } else {
63
- defaultDir = new File(
64
- projectDir,
65
- '/../../../node_modules/react-native/android'
66
- )
67
- }
46
+ // Namespace support was added in 7.3.0
47
+ return (major == 7 && minor >= 3) || major >= 8
48
+ }
49
+
50
+ android {
51
+ if (supportsNamespace()) {
52
+ namespace "com.ionic.portalsreactnative"
68
53
 
69
- if (defaultDir.exists()) {
70
- maven {
71
- url defaultDir.toString()
72
- name androidSourcesName
73
- }
74
-
75
- logger.info(":${project.name}:reactNativeAndroidRoot ${defaultDir.canonicalPath}")
76
- found = true
77
- } else {
78
- def parentDir = rootProject.projectDir
79
-
80
- 1.upto(5, {
81
- if (found) return true
82
- parentDir = parentDir.parentFile
83
-
84
- def androidSourcesDir = new File(
85
- parentDir,
86
- 'node_modules/react-native'
87
- )
88
-
89
- def androidPrebuiltBinaryDir = new File(
90
- parentDir,
91
- 'node_modules/react-native/android'
92
- )
93
-
94
- if (androidPrebuiltBinaryDir.exists()) {
95
- maven {
96
- url androidPrebuiltBinaryDir.toString()
97
- name androidSourcesName
98
- }
99
-
100
- logger.info(":${project.name}:reactNativeAndroidRoot ${androidPrebuiltBinaryDir.canonicalPath}")
101
- found = true
102
- } else if (androidSourcesDir.exists()) {
103
- maven {
104
- url androidSourcesDir.toString()
105
- name androidSourcesName
106
- }
107
-
108
- logger.info(":${project.name}:reactNativeAndroidRoot ${androidSourcesDir.canonicalPath}")
109
- found = true
110
- }
111
- })
54
+ sourceSets {
55
+ main {
56
+ manifest.srcFile "src/main/AndroidManifestNew.xml"
57
+ }
112
58
  }
59
+ }
60
+
61
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
62
+
63
+ defaultConfig {
64
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
65
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
66
+
67
+ }
113
68
 
114
- if (!found) {
115
- throw new GradleException(
116
- "${project.name}: unable to locate React Native android sources. " +
117
- "Ensure you have you installed React Native as a dependency in your project and try again."
118
- )
69
+ buildTypes {
70
+ release {
71
+ minifyEnabled false
119
72
  }
73
+ }
74
+
75
+ lintOptions {
76
+ disable "GradleCompatible"
77
+ }
78
+
79
+ compileOptions {
80
+ sourceCompatibility JavaVersion.VERSION_1_8
81
+ targetCompatibility JavaVersion.VERSION_1_8
82
+ }
120
83
  }
121
84
 
85
+ repositories {
86
+ mavenCentral()
87
+ google()
88
+ }
89
+
90
+ def kotlin_version = getExtOrDefault("kotlinVersion")
91
+
122
92
  dependencies {
123
- //noinspection GradleDynamicVersion
124
- api 'com.facebook.react:react-native:+'
125
- //noinspection GradleDynamicVersion
126
- api "io.ionic:portals:0.8.+"
127
- //noinspection GradleDynamicVersion
128
- api "io.ionic:liveupdates:0.4.+"
129
-
130
- implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.3"
93
+ // For < 0.71, this will be from the local maven repo
94
+ // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
95
+ //noinspection GradleDynamicVersion
96
+ implementation "com.facebook.react:react-native:+"
97
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
98
+ implementation "io.ionic:portals:0.10.+"
99
+ implementation "io.ionic:liveupdates:0.5.+"
131
100
  }
101
+
@@ -1,4 +1,6 @@
1
- ReactNativePortals_kotlinVersion=1.8.0
2
- ReactNativePortals_compileSdkVersion=33
3
- ReactNativePortals_targetSdkVersion=33
1
+ PortalsReactNative_kotlinVersion=1.8.0
2
+ PortalsReactNative_minSdkVersion=21
3
+ PortalsReactNative_targetSdkVersion=33
4
+ PortalsReactNative_compileSdkVersion=33
5
+ PortalsReactNative_ndkversion=21.4.7075529
4
6
  android.useAndroidX=true
@@ -9,6 +9,7 @@ import androidx.fragment.app.FragmentActivity
9
9
  import com.facebook.react.bridge.ReactApplicationContext
10
10
  import com.facebook.react.bridge.ReadableArray
11
11
  import com.facebook.react.bridge.ReadableMap
12
+ import com.facebook.react.modules.core.DeviceEventManagerModule
12
13
  import com.facebook.react.uimanager.ThemedReactContext
13
14
  import com.facebook.react.uimanager.ViewGroupManager
14
15
  import com.facebook.react.uimanager.annotations.ReactProp
@@ -32,7 +33,7 @@ internal class PortalViewManager(private val context: ReactApplicationContext) :
32
33
  when (fragmentMap[viewGroup.id]) {
33
34
  null -> fragmentMap[viewGroup.id] = PortalViewState(
34
35
  fragment = null,
35
- portal = RNPortalManager.getPortal(name),
36
+ portal = RNPortalManager.getPortal(name) ?: RNPortalManager.createPortal(portal),
36
37
  initialContext = portal.getMap("initialContext")?.toHashMap()
37
38
  )
38
39
  }
@@ -68,13 +69,15 @@ internal class PortalViewManager(private val context: ReactApplicationContext) :
68
69
  setupLayout(parentView)
69
70
 
70
71
  val portal = rnPortal.builder.create()
71
-
72
- if (rnPortal.onFCP != null || rnPortal.onFID != null || rnPortal.onTTFB != null) {
73
- val vitalsPlugin = WebVitals { _, metric, duration ->
74
- when (metric) {
75
- WebVitals.Metric.FCP -> rnPortal.onFCP?.let { it(duration) }
76
- WebVitals.Metric.FID -> rnPortal.onFID?.let { it(duration) }
77
- WebVitals.Metric.TTFB -> rnPortal.onTTFB?.let { it(duration) }
72
+ val vitals: List<String>? = rnPortal.vitals
73
+
74
+ if (vitals != null) {
75
+ val vitalsPlugin = WebVitals { name, metric, duration ->
76
+ val stringMetric = metric.toString().lowercase()
77
+ if (vitals.contains(stringMetric)) {
78
+ context
79
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
80
+ .emit("vitals:$stringMetric", mapOf("duration" to duration, "portalName" to name).toReadableMap())
78
81
  }
79
82
  }
80
83
  portal.addPluginInstance(vitalsPlugin)
@@ -17,9 +17,7 @@ internal data class RNPortal(
17
17
  val builder: PortalBuilder,
18
18
  val index: String?,
19
19
  val plugins: List<PortalPlugin>,
20
- var onFCP: ((Long) -> Unit)? = null,
21
- var onTTFB: ((Long) -> Unit)? = null,
22
- var onFID: ((Long) -> Unit)? = null
20
+ var vitals: MutableList<String>? = null
23
21
  )
24
22
 
25
23
  internal data class PortalPlugin(val androidClassPath: String, val iosClassName: String) {
@@ -43,13 +41,21 @@ internal data class PortalPlugin(val androidClassPath: String, val iosClassName:
43
41
 
44
42
  internal object RNPortalManager {
45
43
  private val manager = PortalManager
44
+ @Deprecated("Will be removed in the next release.")
46
45
  private val portals: ConcurrentHashMap<String, RNPortal> = ConcurrentHashMap()
47
46
  private lateinit var reactApplicationContext: ReactApplicationContext
48
47
  private var usesSecureLiveUpdates = false
49
48
 
50
49
  fun register(key: String) = manager.register(key)
51
50
 
51
+ @Deprecated("Will be removed in the next release.")
52
52
  fun addPortal(map: ReadableMap): RNPortal? {
53
+ val portal = createPortal(map) ?: return null
54
+ portals[portal.builder.name] = portal
55
+ return portal
56
+ }
57
+
58
+ fun createPortal(map: ReadableMap): RNPortal? {
53
59
  val name = map.getString("name") ?: return null
54
60
  val portalBuilder = PortalBuilder(name)
55
61
 
@@ -60,13 +66,16 @@ internal object RNPortalManager {
60
66
  ?.toHashMap()
61
67
  ?.let(portalBuilder::setInitialContext)
62
68
 
69
+ if (map.hasKey("devMode")) {
70
+ map.getBoolean("devMode").let(portalBuilder::setDevMode)
71
+ }
63
72
 
64
73
  val plugins: List<PortalPlugin> = map.getArray("plugins")
65
74
  ?.let { rnArray ->
66
75
  val list = mutableListOf<PortalPlugin>()
67
76
  for (idx in 0 until rnArray.size()) {
68
77
  rnArray.getMap(idx)
69
- ?.let(PortalPlugin.Companion::fromReadableMap)
78
+ .let(PortalPlugin.Companion::fromReadableMap)
70
79
  ?.let(list::add)
71
80
  }
72
81
  return@let list
@@ -84,7 +93,7 @@ internal object RNPortalManager {
84
93
 
85
94
  for (idx in 0 until rnArray.size()) {
86
95
  rnArray.getMap(idx)
87
- ?.let assetMap@{ map ->
96
+ .let assetMap@{ map ->
88
97
  val name = map.getString("name") ?: return@assetMap null
89
98
  AssetMap(
90
99
  name = name,
@@ -105,11 +114,11 @@ internal object RNPortalManager {
105
114
  val appId = readableMap.getString("appId") ?: return@let null
106
115
  val channel = readableMap.getString("channel") ?: return@let null
107
116
  val syncOnAdd = readableMap.getBoolean("syncOnAdd")
108
- Pair(LiveUpdate(appId, channel, RNPortalManager.usesSecureLiveUpdates), syncOnAdd)
117
+ Pair(LiveUpdate(appId, channel, usesSecureLiveUpdates), syncOnAdd)
109
118
  }
110
119
  ?.let { (liveUpdate, updateOnAppLoad) ->
111
120
  portalBuilder.setLiveUpdateConfig(
112
- context = RNPortalManager.reactApplicationContext,
121
+ context = reactApplicationContext,
113
122
  liveUpdateConfig = liveUpdate,
114
123
  updateOnAppLoad = updateOnAppLoad
115
124
  )
@@ -118,18 +127,30 @@ internal object RNPortalManager {
118
127
  portalBuilder
119
128
  .addPlugin(PortalsPlugin::class.java)
120
129
 
121
- val rnPortal = RNPortal(
130
+ val vitals = map.getArray("webVitals")
131
+ val maybeList = if (vitals != null) {
132
+ val size = vitals.size()
133
+ (0 until size).fold(mutableListOf<String>()) { list, next ->
134
+ val vital = vitals.getString(next)
135
+ list.add(vital)
136
+ return@fold list
137
+ }
138
+ } else {
139
+ null
140
+ }
141
+
142
+ return RNPortal(
122
143
  builder = portalBuilder,
123
144
  index = map.getString("index"),
124
- plugins = plugins
145
+ plugins = plugins,
146
+ vitals = maybeList
125
147
  )
126
-
127
- portals[name] = rnPortal
128
- return rnPortal
129
148
  }
130
149
 
131
- fun getPortal(name: String): RNPortal = portals[name]
132
- ?: throw IllegalStateException("Portal with portalId $name not found in RNPortalManager")
150
+ @Deprecated("Will be removed in the next release.")
151
+ fun getPortal(name: String): RNPortal? {
152
+ return portals[name]
153
+ }
133
154
 
134
155
  fun enableSecureLiveUpdates(keyPath: String) {
135
156
  LiveUpdateManager.secureLiveUpdatePEM = keyPath
@@ -182,7 +203,7 @@ internal fun JSONObject.toReactMap(): ReadableMap =
182
203
  is Int -> map.putInt(key, value)
183
204
  is Double -> map.putDouble(key, value)
184
205
  is String -> map.putString(key, value)
185
- null -> map.putNull(key)
206
+ null, JSONObject.NULL -> map.putNull(key)
186
207
  else -> map.putString(key, value.toString())
187
208
  }
188
209
  } catch (_: JSONException) {
@@ -201,7 +222,7 @@ private fun JSONArray.toReactArray(): ReadableArray =
201
222
  is Int -> array.pushInt(value)
202
223
  is Double -> array.pushDouble(value)
203
224
  is String -> array.pushString(value)
204
- null -> array.pushNull()
225
+ null, JSONObject.NULL -> array.pushNull()
205
226
  else -> array.pushString(value.toString())
206
227
  }
207
228
  } catch (_: JSONException) {
@@ -1,7 +1,6 @@
1
1
  package io.ionic.portals.reactnative
2
2
 
3
3
  import com.facebook.react.bridge.*
4
- import io.ionic.portals.Portal
5
4
 
6
5
  internal class PortalManagerModule(reactContext: ReactApplicationContext) :
7
6
  ReactContextBaseJavaModule(reactContext) {
@@ -32,7 +31,7 @@ internal class PortalManagerModule(reactContext: ReactApplicationContext) :
32
31
  val portals = WritableNativeArray()
33
32
 
34
33
  for (i in 0 until array.size()) {
35
- val map = array.getMap(i) ?: continue
34
+ val map = array.getMap(i)
36
35
  val portal = RNPortalManager.addPortal(map) ?: continue
37
36
  portals.pushMap(portal.toReadableMap())
38
37
  }
@@ -44,7 +43,7 @@ internal class PortalManagerModule(reactContext: ReactApplicationContext) :
44
43
  fun getPortal(name: String, promise: Promise) {
45
44
  try {
46
45
  val portal = RNPortalManager.getPortal(name)
47
- promise.resolve(portal.toReadableMap())
46
+ promise.resolve(portal?.toReadableMap())
48
47
  } catch (e: IllegalStateException) {
49
48
  promise.reject(null, "Portal named $name not registered.")
50
49
  }
@@ -4,41 +4,33 @@ import com.facebook.react.bridge.Promise
4
4
  import com.facebook.react.bridge.ReactApplicationContext
5
5
  import com.facebook.react.bridge.ReactContextBaseJavaModule
6
6
  import com.facebook.react.bridge.ReactMethod
7
- import com.facebook.react.modules.core.DeviceEventManagerModule
8
7
 
9
8
  internal class PortalWebVitalsModule(reactContext: ReactApplicationContext): ReactContextBaseJavaModule(reactContext) {
10
9
  override fun getName() = "IONPortalsWebVitals"
11
10
 
12
- @ReactMethod
13
- fun registerOnFirstContentfulPaint(portalName: String, promise: Promise) {
14
- RNPortalManager.getPortal(portalName).onFCP = { duration ->
15
- reactApplicationContext
16
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
17
- .emit("vitals:fcp", mapOf("duration" to duration, "portalName" to portalName).toReadableMap())
11
+ private fun registerVital(portalName: String, vitalName: String) {
12
+ val portal = RNPortalManager.getPortal(portalName) ?: return
13
+ if (portal.vitals == null) {
14
+ portal.vitals = mutableListOf()
18
15
  }
16
+ portal.vitals?.add(vitalName)
17
+ }
19
18
 
19
+ @ReactMethod
20
+ fun registerOnFirstContentfulPaint(portalName: String, promise: Promise) {
21
+ registerVital(portalName,"fcp")
20
22
  promise.resolve(null)
21
23
  }
22
24
 
23
25
  @ReactMethod
24
26
  fun registerOnFirstInputDelay(portalName: String, promise: Promise) {
25
- RNPortalManager.getPortal(portalName).onFID = { duration ->
26
- reactApplicationContext
27
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
28
- .emit("vitals:fid", mapOf("duration" to duration, "portalName" to portalName).toReadableMap())
29
- }
30
-
27
+ registerVital(portalName,"fid")
31
28
  promise.resolve(null)
32
29
  }
33
30
 
34
31
  @ReactMethod
35
32
  fun registerOnTimeToFirstByte(portalName: String, promise: Promise) {
36
- RNPortalManager.getPortal(portalName).onTTFB = { duration ->
37
- reactApplicationContext
38
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
39
- .emit("vitals:ttfb", mapOf("duration" to duration, "portalName" to portalName).toReadableMap())
40
- }
41
-
33
+ registerVital(portalName,"ttfb")
42
34
  promise.resolve(null)
43
35
  }
44
36
 
@@ -0,0 +1,77 @@
1
+ //
2
+ // IonicPortals+Codable.swift
3
+ // ReactNativePortals
4
+ //
5
+ // Created by Trevor Lambert on 6/5/24.
6
+ // Copyright © 2024 Facebook. All rights reserved.
7
+ //
8
+
9
+ import Foundation
10
+ import IonicPortals
11
+
12
+ extension AssetMap: Encodable {
13
+ enum CodingKeys: String, CodingKey {
14
+ case startDir, virtualPath, name
15
+ }
16
+
17
+ public func encode(to encoder: Encoder) throws {
18
+ var container = encoder.container(keyedBy: CodingKeys.self)
19
+ try container.encode(startDir, forKey: .startDir)
20
+ try container.encode(virtualPath, forKey: .virtualPath)
21
+ try container.encode(name, forKey: .name)
22
+ }
23
+ }
24
+
25
+ extension AssetMap: Decodable {
26
+ public init(from decoder: Decoder) throws {
27
+ let container = try decoder.container(keyedBy: CodingKeys.self)
28
+ let name = try container.decode(String.self, forKey: .name)
29
+ let startDir = try container.decodeIfPresent(String.self, forKey: .startDir) ?? ""
30
+ let virtualPath = try container.decodeIfPresent(String.self, forKey: .virtualPath)
31
+ self.init(name: name, virtualPath: virtualPath, startDir: startDir)
32
+ }
33
+ }
34
+
35
+ //export interface Portal {
36
+ // /** The name of the Portal to be referenced. Must be **unique** */
37
+ // name: string;
38
+ // /** Any Capacitor plugins to be made available to the Portal */
39
+ // plugins?: CapacitorPlugin[];
40
+ // /**
41
+ // * The root directory of the web application relative to Bundle.main on iOS
42
+ // * and src/main/assets on Android. If omitted, `name` is used.
43
+ // */
44
+ // startDir?: string;
45
+ // /** The name of the initial file to load. If omitted, 'index.html' is used. */
46
+ // index?: string;
47
+ // /** Any data needed at initial render when a portal is loaded. */
48
+ // initialContext?: {
49
+ // [key: string]: any;
50
+ // };
51
+ // assetMaps?: AssetMap[];
52
+ // liveUpdate?: LiveUpdateConfig;
53
+ //}
54
+
55
+ //export interface LiveUpdate {
56
+ // /** The AppFlow application ID */
57
+ // appId: string;
58
+ // /** The AppFlow distribution channel */
59
+ // channel: string;
60
+ //}
61
+ //
62
+ ///** Data needed to register a live update to be managed */
63
+ //export type LiveUpdateConfig = LiveUpdate & { syncOnAdd: boolean };
64
+
65
+ //export interface AssetMap {
66
+ // /** The name to index the asset map by */
67
+ // name: string;
68
+ // /** Any path to match via the web. If omitted, {@link AssetMap#name} will be used. */
69
+ // virtualPath?: string;
70
+ // /** The root directory of the assets relative to Bundle.main on iOS
71
+ // * and src/main/assets on Android. If omitted, the root of Bundle.main
72
+ // * and src/main/assets will be used.
73
+ // */
74
+ // startDir?: string;
75
+ //}
76
+
77
+