@ionic/portals-react-native 0.0.1 → 0.0.4
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/README.md +14 -34
- package/ReactNativePortals.podspec +2 -2
- package/android/build.gradle +15 -5
- package/android/src/main/java/io/ionic/portals/reactnative/ReactNativeLiveUpdatesModule.kt +187 -0
- package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalsModule.kt +96 -36
- package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalsPackage.kt +2 -1
- package/ios/LiveUpdatesManager.m +16 -0
- package/ios/Podfile +1 -1
- package/ios/Podfile.lock +12 -16
- package/ios/PortalsPubSub.m +2 -2
- package/ios/ReactNativePortals.swift +166 -17
- package/ios/ReactNativePortals.xcodeproj/project.pbxproj +4 -0
- package/lib/commonjs/PortalView.android.js +1 -1
- package/lib/commonjs/PortalView.android.js.map +1 -1
- package/lib/commonjs/PortalView.js.map +1 -1
- package/lib/commonjs/index.js +39 -10
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/PortalView.android.js +1 -1
- package/lib/module/PortalView.android.js.map +1 -1
- package/lib/module/PortalView.js.map +1 -1
- package/lib/module/index.js +29 -9
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/index.d.ts +24 -1
- package/package.json +1 -1
- package/src/index.ts +64 -11
package/README.md
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
<br />
|
|
9
9
|
<p align="center">
|
|
10
10
|
<a href="https://github.com/ionic-team/react-native-ionic-portals/actions?query=workflow%3ACI"><img src="https://img.shields.io/github/workflow/status/ionic-team/ionic-portals/CI?style=flat-square" /></a>
|
|
11
|
-
<a href="https://www.npmjs.com/package/@ionic/react-native
|
|
12
|
-
<a href="https://www.npmjs.com/package/@ionic/react-native
|
|
11
|
+
<a href="https://www.npmjs.com/package/@ionic/portals-react-native"><img src="https://img.shields.io/npm/l/@ionic/portals-react-native?style=flat-square" /></a>
|
|
12
|
+
<a href="https://www.npmjs.com/package/@ionic/portals-react-native"><img src="https://img.shields.io/npm/v/@ionic/portals-react-native?style=flat-square" /></a>
|
|
13
13
|
</p>
|
|
14
14
|
<p align="center">
|
|
15
15
|
<a href="https://ionic.io/docs/portals"><img src="https://img.shields.io/static/v1?label=docs&message=ionic.io/portals&color=blue&style=flat-square" /></a>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
-
Ionic Portals is a supercharged native Web View component for iOS and Android that lets you add web-based experiences to
|
|
21
|
+
Ionic Portals is a supercharged native Web View component for iOS and Android that lets you add web-based experiences to React Native mobile apps. It enables React Native and web teams to better collaborate and bring new and existing web experiences to mobile in a safe, controlled way.
|
|
22
22
|
|
|
23
23
|
## Getting Started
|
|
24
24
|
|
|
@@ -132,44 +132,24 @@ unsubscribe('channel:topic', subscriptionReference)
|
|
|
132
132
|
To see an example of Portals Pub/Sub in action that manages the lifecycle of a subscription with the lifecycle of a React Native component, refer to the [`PubSubLabel`](https://github.com/ionic-team/react-native-ionic-portals/blob/af19df0d66059d85ab8dde493504368c3bf39127/example/App.tsx#L53) implementation in the example project.
|
|
133
133
|
|
|
134
134
|
### Using Capacitor Plugins
|
|
135
|
-
If you need to use any Capacitor plugins,
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
PortalManager.register("YOUR_PORTAL_KEY_HERE");
|
|
145
|
-
PortalManager.newPortal("hello")
|
|
146
|
-
.addPlugin(MyCapacitorPlugin.class) // Plugin registration
|
|
147
|
-
.setInitialContext(Map.of("greeting", "Hello, world!"))
|
|
148
|
-
.setStartDir("portals/hello")
|
|
149
|
-
.create();
|
|
135
|
+
If you need to use any Capacitor plugins, the classpath of the Android plugins will have to be provided to the `Portal` `androidPlugins` property.
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
const helloPortal = {
|
|
139
|
+
name: 'hello',
|
|
140
|
+
startDir: 'portals/hello',
|
|
141
|
+
androidPlugins: ['com.capacitorjs.plugins.camera.CameraPlugin'],
|
|
142
|
+
initialContext: {
|
|
143
|
+
greeting: 'Hello, world!'
|
|
150
144
|
}
|
|
151
|
-
}
|
|
145
|
+
};
|
|
152
146
|
```
|
|
153
147
|
|
|
154
|
-
|
|
155
|
-
```objective-c
|
|
156
|
-
@implementation RNAppDelegate
|
|
157
|
-
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDicationary *)launchOptions {
|
|
158
|
-
// React Native boilerplate
|
|
159
|
-
[PortalManager register:@"YOUR_PORTAL_KEY_HERE"];
|
|
160
|
-
PortalBuilder *builder = [[PortalBuilder alloc] init:@"hello"];
|
|
161
|
-
[builder setStartDir:@"portals/hello"];
|
|
162
|
-
[builder setInitialContext: @{ @"greeting": @"Hello, world!" }]
|
|
163
|
-
Portal *portal = [builder create];
|
|
164
|
-
[PortalManager addPortal:portal];
|
|
165
|
-
}
|
|
166
|
-
@end
|
|
167
|
-
```
|
|
148
|
+
No configuration for iOS is needed since plugins are automatically registered when the Capacitor bridge initializes on iOS.
|
|
168
149
|
|
|
169
150
|
### Bundling Your Web Apps
|
|
170
151
|
Currently there is no tooling for bundling your web apps directly as part of @ionic/portals-react-native. Please follow the [native guides](https://ionic.io/docs/portals/how-to/pull-in-web-bundle#setup-the-web-asset-directory) to manage this as part of the native build process.
|
|
171
152
|
|
|
172
|
-
|
|
173
153
|
## Registration
|
|
174
154
|
|
|
175
155
|
Ionic Portals for React Native requires a key to use. Once you have integrated Portals into your project, login to your ionic account to get a key. See our doc on [how to register for free and get your Portals license key](https://ionic.io/docs/portals/how-to/get-a-product-key) and refer to the [usage](#Usage) section on how to add your key to your React Native application.
|
|
@@ -10,11 +10,11 @@ Pod::Spec.new do |s|
|
|
|
10
10
|
s.license = package["license"]
|
|
11
11
|
s.authors = package["author"]
|
|
12
12
|
|
|
13
|
-
s.platforms = { :ios => "
|
|
13
|
+
s.platforms = { :ios => "13.0" }
|
|
14
14
|
s.source = { :git => "https://github.com/ionic-team/react-native-ionic-portals.git", :tag => "#{s.version}" }
|
|
15
15
|
|
|
16
16
|
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
17
17
|
|
|
18
18
|
s.dependency "React-Core"
|
|
19
|
-
s.dependency "IonicPortals", "~> 0.
|
|
19
|
+
s.dependency "IonicPortals", "~> 0.6.1"
|
|
20
20
|
end
|
package/android/build.gradle
CHANGED
|
@@ -38,9 +38,14 @@ android {
|
|
|
38
38
|
lintOptions {
|
|
39
39
|
disable 'GradleCompatible'
|
|
40
40
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
|
|
44
|
+
sourceCompatibility = JavaVersion.VERSION_1_8
|
|
45
|
+
targetCompatibility = JavaVersion.VERSION_1_8
|
|
46
|
+
|
|
47
|
+
kotlinOptions {
|
|
48
|
+
freeCompilerArgs += '-Xopt-in=kotlin.RequiresOptIn'
|
|
44
49
|
}
|
|
45
50
|
}
|
|
46
51
|
|
|
@@ -116,7 +121,12 @@ repositories {
|
|
|
116
121
|
}
|
|
117
122
|
|
|
118
123
|
dependencies {
|
|
119
|
-
//
|
|
124
|
+
//noinspection GradleDynamicVersion
|
|
120
125
|
api 'com.facebook.react:react-native:+'
|
|
121
|
-
|
|
126
|
+
//noinspection GradleDynamicVersion
|
|
127
|
+
api "io.ionic:portals:0.6.+"
|
|
128
|
+
//noinspection GradleDynamicVersion
|
|
129
|
+
api "io.ionic:liveupdates:0.0.+"
|
|
130
|
+
|
|
131
|
+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0"
|
|
122
132
|
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
package io.ionic.portals.reactnative
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.*
|
|
4
|
+
import io.ionic.liveupdates.LiveUpdate
|
|
5
|
+
import io.ionic.liveupdates.LiveUpdateManager
|
|
6
|
+
import io.ionic.liveupdates.network.FailStep
|
|
7
|
+
import io.ionic.liveupdates.network.SyncCallback
|
|
8
|
+
import kotlinx.coroutines.*
|
|
9
|
+
import kotlinx.coroutines.channels.awaitClose
|
|
10
|
+
import kotlinx.coroutines.flow.callbackFlow
|
|
11
|
+
import kotlinx.coroutines.flow.fold
|
|
12
|
+
import java.util.concurrent.Executors
|
|
13
|
+
|
|
14
|
+
internal class LiveUpdatesModule(reactContext: ReactApplicationContext) :
|
|
15
|
+
ReactContextBaseJavaModule(reactContext) {
|
|
16
|
+
|
|
17
|
+
override fun getName() = "IONLiveUpdatesManager"
|
|
18
|
+
|
|
19
|
+
private val liveUpdateScope = CoroutineScope(
|
|
20
|
+
Executors.newFixedThreadPool(4)
|
|
21
|
+
.asCoroutineDispatcher()
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
private fun callbackToMap(
|
|
25
|
+
liveUpdate: LiveUpdate,
|
|
26
|
+
failStep: FailStep?,
|
|
27
|
+
failMsg: String?
|
|
28
|
+
): ReadableMap =
|
|
29
|
+
if (failStep != null) {
|
|
30
|
+
val map = WritableNativeMap()
|
|
31
|
+
map.putString("appId", liveUpdate.appId)
|
|
32
|
+
map.putString("failStep", failStep.name)
|
|
33
|
+
map.putString("message", failMsg ?: "Sync failed for unknown reason")
|
|
34
|
+
map
|
|
35
|
+
} else {
|
|
36
|
+
liveUpdate.toReadableMap()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private fun callbackToResult(
|
|
40
|
+
liveUpdate: LiveUpdate,
|
|
41
|
+
failStep: FailStep?,
|
|
42
|
+
failMsg: String?
|
|
43
|
+
): LiveUpdateResult =
|
|
44
|
+
if (failStep != null) {
|
|
45
|
+
LiveUpdateError(
|
|
46
|
+
appId = liveUpdate.appId,
|
|
47
|
+
failStep = failStep.name,
|
|
48
|
+
failMsg = failMsg ?: "Sync failed for unknown reason"
|
|
49
|
+
)
|
|
50
|
+
} else {
|
|
51
|
+
LiveUpdateSuccess(liveUpdate)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@ReactMethod
|
|
56
|
+
fun addLiveUpdate(map: ReadableMap) {
|
|
57
|
+
val appId = map.getString("appId") ?: return
|
|
58
|
+
val channel = map.getString("channel") ?: return
|
|
59
|
+
|
|
60
|
+
LiveUpdateManager.addLiveUpdateInstance(
|
|
61
|
+
context = reactApplicationContext,
|
|
62
|
+
liveUpdate = LiveUpdate(appId, channel)
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@ReactMethod
|
|
67
|
+
fun syncOne(appId: String, promise: Promise) {
|
|
68
|
+
LiveUpdateManager.sync(
|
|
69
|
+
context = reactApplicationContext,
|
|
70
|
+
appId = appId,
|
|
71
|
+
callback = object : SyncCallback {
|
|
72
|
+
override fun onAppComplete(
|
|
73
|
+
liveUpdate: LiveUpdate,
|
|
74
|
+
failStep: FailStep?,
|
|
75
|
+
failMsg: String?
|
|
76
|
+
) {
|
|
77
|
+
val map = callbackToMap(liveUpdate, failStep, failMsg)
|
|
78
|
+
promise.resolve(map)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
override fun onSyncComplete() {
|
|
82
|
+
// do nothing
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@ReactMethod
|
|
89
|
+
fun syncSome(appIds: ReadableArray, promise: Promise) {
|
|
90
|
+
@Suppress("NAME_SHADOWING")
|
|
91
|
+
val appIds = (0 until appIds.size())
|
|
92
|
+
.mapNotNull(appIds::getString)
|
|
93
|
+
.toTypedArray()
|
|
94
|
+
|
|
95
|
+
sync(appIds, promise)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@ReactMethod
|
|
99
|
+
fun syncAll(promise: Promise) {
|
|
100
|
+
sync(emptyArray(), promise)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@OptIn(ExperimentalCoroutinesApi::class)
|
|
104
|
+
private fun sync(appIds: Array<String>, promise: Promise) {
|
|
105
|
+
liveUpdateScope.launch {
|
|
106
|
+
val results = callbackFlow {
|
|
107
|
+
LiveUpdateManager.sync(
|
|
108
|
+
context = reactApplicationContext,
|
|
109
|
+
appIds = appIds,
|
|
110
|
+
callback = object : SyncCallback {
|
|
111
|
+
override fun onAppComplete(
|
|
112
|
+
liveUpdate: LiveUpdate,
|
|
113
|
+
failStep: FailStep?,
|
|
114
|
+
failMsg: String?
|
|
115
|
+
) {
|
|
116
|
+
trySend(callbackToResult(liveUpdate, failStep, failMsg))
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
override fun onSyncComplete() {
|
|
120
|
+
close()
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
awaitClose { cancel() }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
val syncResults = results.fold(SyncResults.empty()) { syncResults, result ->
|
|
129
|
+
when (result) {
|
|
130
|
+
is LiveUpdateSuccess -> syncResults.liveUpdates.add(result.liveUpdate)
|
|
131
|
+
is LiveUpdateError -> syncResults.errors.add(result)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return@fold syncResults
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
promise.resolve(syncResults.asReadableMap)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
fun LiveUpdate.toReadableMap(): ReadableMap {
|
|
143
|
+
val map = WritableNativeMap()
|
|
144
|
+
map.putString("appId", appId)
|
|
145
|
+
map.putString("channel", channelName)
|
|
146
|
+
return map
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private sealed class LiveUpdateResult
|
|
150
|
+
|
|
151
|
+
private data class LiveUpdateError(val appId: String, val failStep: String, val failMsg: String) :
|
|
152
|
+
LiveUpdateResult() {
|
|
153
|
+
val asReadableMap: ReadableMap
|
|
154
|
+
get() {
|
|
155
|
+
val map = WritableNativeMap()
|
|
156
|
+
map.putString("appId", appId)
|
|
157
|
+
map.putString("failStep", failStep)
|
|
158
|
+
map.putString("message", failMsg)
|
|
159
|
+
return map
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private data class LiveUpdateSuccess(val liveUpdate: LiveUpdate) : LiveUpdateResult()
|
|
164
|
+
|
|
165
|
+
private data class SyncResults(
|
|
166
|
+
val liveUpdates: MutableList<LiveUpdate>,
|
|
167
|
+
val errors: MutableList<LiveUpdateError>
|
|
168
|
+
) {
|
|
169
|
+
val asReadableMap: ReadableMap
|
|
170
|
+
get() {
|
|
171
|
+
val map = WritableNativeMap()
|
|
172
|
+
|
|
173
|
+
val liveUpdatesArray = WritableNativeArray()
|
|
174
|
+
liveUpdates.forEach { liveUpdatesArray.pushMap(it.toReadableMap()) }
|
|
175
|
+
map.putArray("liveUpdates", liveUpdatesArray)
|
|
176
|
+
|
|
177
|
+
val errorsArray = WritableNativeArray()
|
|
178
|
+
errors.forEach { errorsArray.pushMap(it.asReadableMap) }
|
|
179
|
+
map.putArray("errors", errorsArray)
|
|
180
|
+
|
|
181
|
+
return map
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
companion object {
|
|
185
|
+
fun empty() = SyncResults(mutableListOf(), mutableListOf())
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package io.ionic.portals.reactnative
|
|
2
2
|
|
|
3
|
+
import android.util.Log
|
|
3
4
|
import android.view.Choreographer
|
|
4
5
|
import android.view.View
|
|
5
6
|
import android.view.ViewGroup
|
|
@@ -11,16 +12,16 @@ import com.facebook.react.uimanager.ThemedReactContext
|
|
|
11
12
|
import com.facebook.react.uimanager.ViewGroupManager
|
|
12
13
|
import com.facebook.react.uimanager.annotations.ReactProp
|
|
13
14
|
import com.getcapacitor.JSObject
|
|
15
|
+
import com.getcapacitor.Plugin
|
|
16
|
+
import io.ionic.liveupdates.LiveUpdate
|
|
14
17
|
import io.ionic.portals.*
|
|
15
18
|
import org.json.JSONArray
|
|
16
19
|
import org.json.JSONException
|
|
17
20
|
import org.json.JSONObject
|
|
18
21
|
|
|
19
|
-
class PortalManagerModule(reactContext: ReactApplicationContext) :
|
|
22
|
+
internal class PortalManagerModule(reactContext: ReactApplicationContext) :
|
|
20
23
|
ReactContextBaseJavaModule(reactContext) {
|
|
21
|
-
override fun getName()
|
|
22
|
-
return "IONPortalManager"
|
|
23
|
-
}
|
|
24
|
+
override fun getName() = "IONPortalManager"
|
|
24
25
|
|
|
25
26
|
@ReactMethod
|
|
26
27
|
fun register(key: String) {
|
|
@@ -29,35 +30,51 @@ class PortalManagerModule(reactContext: ReactApplicationContext) :
|
|
|
29
30
|
|
|
30
31
|
@ReactMethod
|
|
31
32
|
fun addPortal(map: ReadableMap) {
|
|
32
|
-
map.getString("name")
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
val name = map.getString("name") ?: return
|
|
34
|
+
val portalBuilder = PortalBuilder(name)
|
|
35
|
+
|
|
36
|
+
map.getString("startDir")
|
|
37
|
+
?.let(portalBuilder::setStartDir)
|
|
38
|
+
|
|
39
|
+
map.getMap("initialContext")
|
|
40
|
+
?.toHashMap()
|
|
41
|
+
?.let(portalBuilder::setInitialContext)
|
|
42
|
+
|
|
43
|
+
map.getArray("androidPlugins")
|
|
44
|
+
?.toArrayList()
|
|
45
|
+
?.mapNotNull { it as? String }
|
|
46
|
+
?.map {
|
|
47
|
+
Class.forName(it)
|
|
48
|
+
.asSubclass(Plugin::class.java)
|
|
40
49
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
?.forEach(portalBuilder::addPlugin)
|
|
51
|
+
|
|
52
|
+
map.getMap("liveUpdate")
|
|
53
|
+
?.let { readableMap ->
|
|
54
|
+
val appId = readableMap.getString("appId") ?: return@let null
|
|
55
|
+
val channel = readableMap.getString("channel") ?: return@let null
|
|
56
|
+
val syncOnAdd = readableMap.getBoolean("syncOnAdd")
|
|
57
|
+
Pair(LiveUpdate(appId, channel), syncOnAdd)
|
|
58
|
+
}
|
|
59
|
+
?.let { pair ->
|
|
60
|
+
portalBuilder.setLiveUpdateConfig(
|
|
61
|
+
context = reactApplicationContext,
|
|
62
|
+
liveUpdateConfig = pair.first,
|
|
63
|
+
updateOnAppLoad = pair.second
|
|
64
|
+
)
|
|
44
65
|
}
|
|
45
66
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
.create()
|
|
67
|
+
val portal = portalBuilder
|
|
68
|
+
.addPlugin(PortalsPlugin::class.java)
|
|
69
|
+
.create()
|
|
50
70
|
|
|
51
|
-
|
|
52
|
-
}
|
|
71
|
+
PortalManager.addPortal(portal)
|
|
53
72
|
}
|
|
54
73
|
}
|
|
55
74
|
|
|
56
|
-
class PortalsPubSubModule(reactContext: ReactApplicationContext) :
|
|
75
|
+
internal class PortalsPubSubModule(reactContext: ReactApplicationContext) :
|
|
57
76
|
ReactContextBaseJavaModule(reactContext) {
|
|
58
|
-
override fun getName()
|
|
59
|
-
return "IONPortalsPubSub"
|
|
60
|
-
}
|
|
77
|
+
override fun getName() = "IONPortalPubSub"
|
|
61
78
|
|
|
62
79
|
@ReactMethod
|
|
63
80
|
fun subscribe(topic: String, promise: Promise) {
|
|
@@ -91,7 +108,7 @@ class PortalsPubSubModule(reactContext: ReactApplicationContext) :
|
|
|
91
108
|
}
|
|
92
109
|
}
|
|
93
110
|
|
|
94
|
-
fun JSONObject.toReactMap(): ReadableMap =
|
|
111
|
+
private fun JSONObject.toReactMap(): ReadableMap =
|
|
95
112
|
keys().asSequence().fold(WritableNativeMap()) { map, key ->
|
|
96
113
|
try {
|
|
97
114
|
when (val value = get(key)) {
|
|
@@ -110,7 +127,7 @@ fun JSONObject.toReactMap(): ReadableMap =
|
|
|
110
127
|
return@fold map
|
|
111
128
|
}
|
|
112
129
|
|
|
113
|
-
fun JSONArray.toReactArray(): ReadableArray =
|
|
130
|
+
private fun JSONArray.toReactArray(): ReadableArray =
|
|
114
131
|
(0 until length()).fold(WritableNativeArray()) { array, index ->
|
|
115
132
|
try {
|
|
116
133
|
when (val value = get(index)) {
|
|
@@ -129,26 +146,44 @@ fun JSONArray.toReactArray(): ReadableArray =
|
|
|
129
146
|
return@fold array
|
|
130
147
|
}
|
|
131
148
|
|
|
132
|
-
fun ReadableMap.toJSObject(): JSObject = JSObject.fromJSONObject(JSONObject(toHashMap()))
|
|
149
|
+
private fun ReadableMap.toJSObject(): JSObject = JSObject.fromJSONObject(JSONObject(toHashMap()))
|
|
150
|
+
|
|
151
|
+
private data class PortalViewState(
|
|
152
|
+
var fragment: PortalFragment?,
|
|
153
|
+
var portal: Portal?,
|
|
154
|
+
var initialContext: HashMap<String, Any>?
|
|
155
|
+
)
|
|
133
156
|
|
|
134
|
-
class PortalViewManager(private val context: ReactApplicationContext) :
|
|
157
|
+
internal class PortalViewManager(private val context: ReactApplicationContext) :
|
|
135
158
|
ViewGroupManager<FrameLayout>() {
|
|
136
159
|
private val createId = 1
|
|
137
|
-
private
|
|
160
|
+
private val fragmentMap = mutableMapOf<Int, PortalViewState>()
|
|
138
161
|
|
|
139
162
|
@ReactProp(name = "name")
|
|
140
163
|
fun setPortal(viewGroup: ViewGroup, portalName: String) {
|
|
141
|
-
|
|
164
|
+
when (val viewState = fragmentMap[viewGroup.id]) {
|
|
165
|
+
null -> fragmentMap[viewGroup.id] = PortalViewState(
|
|
166
|
+
fragment = null,
|
|
167
|
+
portal = PortalManager.getPortal(portalName),
|
|
168
|
+
initialContext = null
|
|
169
|
+
)
|
|
170
|
+
else -> viewState.portal = PortalManager.getPortal(portalName)
|
|
171
|
+
}
|
|
142
172
|
}
|
|
143
173
|
|
|
144
174
|
@ReactProp(name = "initialContext")
|
|
145
175
|
fun setInitialContext(viewGroup: ViewGroup, initialContext: ReadableMap) {
|
|
146
|
-
|
|
176
|
+
when (val viewState = fragmentMap[viewGroup.id]) {
|
|
177
|
+
null -> fragmentMap[viewGroup.id] = PortalViewState(
|
|
178
|
+
fragment = null,
|
|
179
|
+
portal = null,
|
|
180
|
+
initialContext = initialContext.toHashMap()
|
|
181
|
+
)
|
|
182
|
+
else -> viewState.initialContext = initialContext.toHashMap()
|
|
183
|
+
}
|
|
147
184
|
}
|
|
148
185
|
|
|
149
|
-
override fun getName()
|
|
150
|
-
return "AndroidPortalView"
|
|
151
|
-
}
|
|
186
|
+
override fun getName() = "AndroidPortalView"
|
|
152
187
|
|
|
153
188
|
override fun createViewInstance(reactContext: ThemedReactContext): FrameLayout {
|
|
154
189
|
return FrameLayout(reactContext)
|
|
@@ -161,6 +196,8 @@ class PortalViewManager(private val context: ReactApplicationContext) :
|
|
|
161
196
|
override fun receiveCommand(root: FrameLayout, commandId: String?, args: ReadableArray?) {
|
|
162
197
|
super.receiveCommand(root, commandId, args)
|
|
163
198
|
val viewId = args?.getInt(0) ?: return
|
|
199
|
+
|
|
200
|
+
@Suppress("NAME_SHADOWING")
|
|
164
201
|
val commandId = commandId?.toIntOrNull() ?: return
|
|
165
202
|
|
|
166
203
|
when (commandId) {
|
|
@@ -169,11 +206,17 @@ class PortalViewManager(private val context: ReactApplicationContext) :
|
|
|
169
206
|
}
|
|
170
207
|
|
|
171
208
|
private fun createFragment(root: FrameLayout, viewId: Int) {
|
|
172
|
-
val
|
|
209
|
+
val viewState = fragmentMap[viewId] ?: return
|
|
210
|
+
val portal = viewState.portal ?: return
|
|
211
|
+
|
|
173
212
|
val parentView = root.findViewById<ViewGroup>(viewId)
|
|
174
213
|
setupLayout(parentView)
|
|
175
214
|
|
|
176
215
|
val portalFragment = PortalFragment(portal)
|
|
216
|
+
viewState.initialContext?.let(portalFragment::setInitialContext)
|
|
217
|
+
|
|
218
|
+
viewState.fragment = portalFragment
|
|
219
|
+
|
|
177
220
|
val fragmentActivity = context.currentActivity as? FragmentActivity ?: return
|
|
178
221
|
fragmentActivity.supportFragmentManager
|
|
179
222
|
.beginTransaction()
|
|
@@ -181,6 +224,23 @@ class PortalViewManager(private val context: ReactApplicationContext) :
|
|
|
181
224
|
.commit()
|
|
182
225
|
}
|
|
183
226
|
|
|
227
|
+
override fun onDropViewInstance(view: FrameLayout) {
|
|
228
|
+
super.onDropViewInstance(view)
|
|
229
|
+
val viewState = fragmentMap[view.id] ?: return
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
viewState.fragment
|
|
233
|
+
?.parentFragmentManager
|
|
234
|
+
?.beginTransaction()
|
|
235
|
+
?.remove(viewState.fragment!!)
|
|
236
|
+
?.commit()
|
|
237
|
+
} catch (e: IllegalStateException) {
|
|
238
|
+
Log.i("io.ionic.portals.rn", "Parent fragment manager not available")
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
fragmentMap.remove(view.id)
|
|
242
|
+
}
|
|
243
|
+
|
|
184
244
|
private fun setupLayout(view: ViewGroup) {
|
|
185
245
|
Choreographer.getInstance().postFrameCallback {
|
|
186
246
|
layoutChildren(view)
|
|
@@ -10,7 +10,8 @@ class ReactNativePortalsPackage : ReactPackage {
|
|
|
10
10
|
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
11
11
|
return listOf(
|
|
12
12
|
PortalManagerModule(reactContext),
|
|
13
|
-
PortalsPubSubModule(reactContext)
|
|
13
|
+
PortalsPubSubModule(reactContext),
|
|
14
|
+
LiveUpdatesModule(reactContext)
|
|
14
15
|
)
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//
|
|
2
|
+
// LiveUpdatesManager.m
|
|
3
|
+
// ReactNativePortals
|
|
4
|
+
//
|
|
5
|
+
// Created by Steven Sherry on 6/21/22.
|
|
6
|
+
// Copyright © 2022 Facebook. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
#import <React/RCTBridgeModule.h>
|
|
10
|
+
|
|
11
|
+
@interface RCT_EXTERN_MODULE(IONLiveUpdatesManager, NSObject)
|
|
12
|
+
RCT_EXTERN_METHOD(addLiveUpdate: (NSDictionary) liveUpdate)
|
|
13
|
+
RCT_EXTERN_METHOD(syncOne: (NSString *) appId resolver: (RCTPromiseResolveBlock) resolver rejector: (RCTPromiseRejectBlock) rejector)
|
|
14
|
+
RCT_EXTERN_METHOD(syncSome: (NSArray) appIds resolver: (RCTPromiseResolveBlock) resolver rejector: (RCTPromiseRejectBlock) rejector)
|
|
15
|
+
RCT_EXTERN_METHOD(syncAll: (RCTPromiseResolveBlock) resolver rejector: (RCTPromiseRejectBlock) rejector)
|
|
16
|
+
@end
|
package/ios/Podfile
CHANGED
package/ios/Podfile.lock
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
PODS:
|
|
2
2
|
- boost-for-react-native (1.63.0)
|
|
3
|
-
- Capacitor (3.
|
|
3
|
+
- Capacitor (3.6.0):
|
|
4
4
|
- CapacitorCordova
|
|
5
|
-
- CapacitorCordova (3.
|
|
5
|
+
- CapacitorCordova (3.6.0)
|
|
6
6
|
- DoubleConversion (1.1.6)
|
|
7
7
|
- FBLazyVector (0.63.4)
|
|
8
8
|
- FBReactNativeSpec (0.63.4):
|
|
@@ -22,11 +22,10 @@ PODS:
|
|
|
22
22
|
- DoubleConversion
|
|
23
23
|
- glog
|
|
24
24
|
- glog (0.3.5)
|
|
25
|
-
- IonicLiveUpdates (0.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
- IonicLiveUpdates
|
|
25
|
+
- IonicLiveUpdates (0.1.1)
|
|
26
|
+
- IonicPortals (0.6.1):
|
|
27
|
+
- Capacitor (~> 3.5)
|
|
28
|
+
- IonicLiveUpdates (~> 0.1.0)
|
|
30
29
|
- RCTRequired (0.63.4)
|
|
31
30
|
- RCTTypeSafety (0.63.4):
|
|
32
31
|
- FBLazyVector (= 0.63.4)
|
|
@@ -254,7 +253,6 @@ PODS:
|
|
|
254
253
|
- React-cxxreact (= 0.63.4)
|
|
255
254
|
- React-jsi (= 0.63.4)
|
|
256
255
|
- Yoga (1.14.0)
|
|
257
|
-
- ZIPFoundation (0.9.13)
|
|
258
256
|
|
|
259
257
|
DEPENDENCIES:
|
|
260
258
|
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
|
@@ -262,7 +260,7 @@ DEPENDENCIES:
|
|
|
262
260
|
- FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`)
|
|
263
261
|
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
|
|
264
262
|
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
|
265
|
-
- IonicPortals (~> 0.
|
|
263
|
+
- IonicPortals (~> 0.6.1)
|
|
266
264
|
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
|
|
267
265
|
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
|
|
268
266
|
- React (from `../node_modules/react-native/`)
|
|
@@ -294,7 +292,6 @@ SPEC REPOS:
|
|
|
294
292
|
- CapacitorCordova
|
|
295
293
|
- IonicLiveUpdates
|
|
296
294
|
- IonicPortals
|
|
297
|
-
- ZIPFoundation
|
|
298
295
|
|
|
299
296
|
EXTERNAL SOURCES:
|
|
300
297
|
DoubleConversion:
|
|
@@ -352,15 +349,15 @@ EXTERNAL SOURCES:
|
|
|
352
349
|
|
|
353
350
|
SPEC CHECKSUMS:
|
|
354
351
|
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
|
|
355
|
-
Capacitor:
|
|
356
|
-
CapacitorCordova:
|
|
352
|
+
Capacitor: 28d6f7c3b02858b0696c98d27527ffb1ac8aba8f
|
|
353
|
+
CapacitorCordova: 23b4def1cd6a8c463074916376f511d7446d683e
|
|
357
354
|
DoubleConversion: cde416483dac037923206447da6e1454df403714
|
|
358
355
|
FBLazyVector: 3bb422f41b18121b71783a905c10e58606f7dc3e
|
|
359
356
|
FBReactNativeSpec: f2c97f2529dd79c083355182cc158c9f98f4bd6e
|
|
360
357
|
Folly: b73c3869541e86821df3c387eb0af5f65addfab4
|
|
361
358
|
glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3
|
|
362
|
-
IonicLiveUpdates:
|
|
363
|
-
IonicPortals:
|
|
359
|
+
IonicLiveUpdates: 5fb89c6faed266d083691e64f33710211edb3839
|
|
360
|
+
IonicPortals: 17b9ee9ae513fc3906b062bdd0b7492db94e38ce
|
|
364
361
|
RCTRequired: 082f10cd3f905d6c124597fd1c14f6f2655ff65e
|
|
365
362
|
RCTTypeSafety: 8c9c544ecbf20337d069e4ae7fd9a377aadf504b
|
|
366
363
|
React: b0a957a2c44da4113b0c4c9853d8387f8e64e615
|
|
@@ -382,8 +379,7 @@ SPEC CHECKSUMS:
|
|
|
382
379
|
React-RCTVibration: ae4f914cfe8de7d4de95ae1ea6cc8f6315d73d9d
|
|
383
380
|
ReactCommon: 73d79c7039f473b76db6ff7c6b159c478acbbb3b
|
|
384
381
|
Yoga: 4bd86afe9883422a7c4028c00e34790f560923d6
|
|
385
|
-
ZIPFoundation: ae5b4b813d216d3bf0a148773267fff14bd51d37
|
|
386
382
|
|
|
387
|
-
PODFILE CHECKSUM:
|
|
383
|
+
PODFILE CHECKSUM: 67d6b2cc79c25d2c4dcf32d6e3c6d6e6defffdb1
|
|
388
384
|
|
|
389
385
|
COCOAPODS: 1.11.2
|
package/ios/PortalsPubSub.m
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
#import <React/RCTBridgeModule.h>
|
|
10
10
|
#import <React/RCTEventEmitter.h>
|
|
11
11
|
|
|
12
|
-
@interface RCT_EXTERN_MODULE(
|
|
12
|
+
@interface RCT_EXTERN_MODULE(IONPortalPubSub, RCTEventEmitter)
|
|
13
13
|
RCT_EXTERN_METHOD(subscribe: (NSString *) topic resolver: (RCTPromiseResolveBlock) resolver rejector: (RCTPromiseRejectBlock) rejector)
|
|
14
|
-
RCT_EXTERN_METHOD(unsubscribe: (NSString *) topic subscriptionRef: (NSNumber) subscriptionRef)
|
|
14
|
+
RCT_EXTERN_METHOD(unsubscribe: (NSString *) topic subscriptionRef: (NSNumber _Nonnull) subscriptionRef)
|
|
15
15
|
RCT_EXTERN_METHOD(publish: (NSString *) topic data: (id) data)
|
|
16
16
|
@end
|