@ionic/portals-react-native 0.1.0 → 0.2.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 (40) hide show
  1. package/README.md +11 -159
  2. package/ReactNativePortals.podspec +2 -1
  3. package/android/build.gradle +1 -1
  4. package/android/src/main/java/io/ionic/portals/reactnative/PortalView.kt +132 -0
  5. package/android/src/main/java/io/ionic/portals/reactnative/ReactNativeLiveUpdatesModule.kt +10 -28
  6. package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalManager.kt +149 -0
  7. package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalsModule.kt +73 -232
  8. package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalsPackage.kt +0 -1
  9. package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalsPubSub.kt +45 -0
  10. package/ios/LiveUpdate+Dict.swift +30 -0
  11. package/ios/LiveUpdateManager+Async.swift +69 -0
  12. package/ios/LiveUpdateManagerError+Dict.swift +19 -0
  13. package/ios/Podfile +2 -1
  14. package/ios/Podfile.lock +8 -7
  15. package/ios/Portal+Dict.swift +35 -0
  16. package/ios/PortalManager.m +10 -4
  17. package/ios/PortalView.m +1 -1
  18. package/ios/PortalView.swift +67 -0
  19. package/ios/PortalsConfig.swift +92 -0
  20. package/ios/PortalsPubSub.m +3 -3
  21. package/ios/PortalsPubSub.swift +47 -0
  22. package/ios/PortalsReactNative.swift +104 -0
  23. package/ios/ReactNativePortals.xcodeproj/project.pbxproj +32 -8
  24. package/lib/commonjs/PortalView.android.js +2 -10
  25. package/lib/commonjs/PortalView.android.js.map +1 -1
  26. package/lib/commonjs/PortalView.js +0 -6
  27. package/lib/commonjs/PortalView.js.map +1 -1
  28. package/lib/commonjs/index.js +54 -38
  29. package/lib/commonjs/index.js.map +1 -1
  30. package/lib/module/PortalView.android.js +2 -5
  31. package/lib/module/PortalView.android.js.map +1 -1
  32. package/lib/module/PortalView.js +0 -2
  33. package/lib/module/PortalView.js.map +1 -1
  34. package/lib/module/index.js +52 -19
  35. package/lib/module/index.js.map +1 -1
  36. package/lib/typescript/index.d.ts +27 -2
  37. package/package.json +3 -1
  38. package/src/index.ts +47 -12
  39. package/ios/LiveUpdatesManager.m +0 -16
  40. package/ios/ReactNativePortals.swift +0 -262
package/src/index.ts CHANGED
@@ -5,8 +5,7 @@ import {
5
5
  ViewProps,
6
6
  } from 'react-native';
7
7
 
8
- const { IONPortalPubSub, IONPortalManager, IONLiveUpdatesManager } =
9
- NativeModules;
8
+ const { IONPortalPubSub, IONPortalsReactNative } = NativeModules;
10
9
 
11
10
  export { default as PortalView } from './PortalView';
12
11
 
@@ -82,9 +81,10 @@ export const publish = (topic: string, data: any) => {
82
81
  /**
83
82
  * Validates that a valid registration key has been procured from http://ionic.io/register-portals
84
83
  * @param key The registration key
84
+ * @returns Promise<void>
85
85
  */
86
- export const register = (key: string) => {
87
- IONPortalManager.register(key);
86
+ export const register = async (key: string): Promise<void> => {
87
+ return IONPortalsReactNative.register(key);
88
88
  };
89
89
 
90
90
  /**
@@ -126,9 +126,30 @@ export type PortalProps = PortalProp & ViewProps;
126
126
  * Adds a Portal to an internal registry. Must be called before attempting to render a {@link PortalView}.
127
127
  *
128
128
  * @param portal The portal to add to the internal registry.
129
+ * @returns Promise containing the Portal that was added to the registry.
129
130
  */
130
- export const addPortal = (portal: Portal) => {
131
- IONPortalManager.addPortal(portal);
131
+ export const addPortal = async (portal: Portal): Promise<Portal> => {
132
+ return IONPortalsReactNative.addPortal(portal);
133
+ };
134
+
135
+ /**
136
+ * Adds all portals to an internal registry. This or {@link addPortal} must be called before attempting to render a {@link PortalView}
137
+ *
138
+ * @param portals The portals to add to the internal registry.
139
+ * @returns Promise containing the Portals that were added to the registry.
140
+ */
141
+ export const addPortals = async (portals: Portal[]): Promise<Portal[]> => {
142
+ return IONPortalsReactNative.addPortals(portals);
143
+ };
144
+
145
+ /**
146
+ * Gets a {@link Portal} previously registered via {@link addPortal} or {@link addPortals}.
147
+ *
148
+ * @param name The portal name to retrieve from the internal registry.
149
+ * @returns Promise containing the registered {@link Portal}. If the {@link Portal} was not registered, the Promise will fail.
150
+ */
151
+ export const getPortal = async (name: string): Promise<Portal> => {
152
+ return IONPortalsReactNative.getPortal(name);
132
153
  };
133
154
 
134
155
  export interface LiveUpdate {
@@ -156,14 +177,28 @@ export interface SyncResults {
156
177
  errors: LiveUpdateError[];
157
178
  }
158
179
 
180
+ /**
181
+ * Configures LiveUpdates to cyrptographically verify the contents of the downloaded bundles.
182
+ * This method must be called before any LiveUpdates are registered otherwise they will no longer be tracked.
183
+ *
184
+ * @param pathToKey The *relative* path to the public key for verification.
185
+ * This path should be the same relatibe to the main application bundle on iOS and the assets directory on Android.
186
+ * @returns Promise<void>
187
+ */
188
+ export const enableSecureLiveUpdates = async (
189
+ pathToKey: string
190
+ ): Promise<void> => {
191
+ return IONPortalsReactNative.enableSecureLiveUpdates(pathToKey);
192
+ };
193
+
159
194
  /**
160
195
  * Syncs a single live update.
161
196
  *
162
197
  * @param appId The AppFlow application ID to sync.
163
198
  * @returns A Promise<LiveUpdate>. A failure should result in a {@link LiveUpdateError}.
164
199
  */
165
- export const syncOne = (appId: string): Promise<LiveUpdate> => {
166
- return IONLiveUpdatesManager.syncOne(appId);
200
+ export const syncOne = async (appId: string): Promise<LiveUpdate> => {
201
+ return IONPortalsReactNative.syncOne(appId);
167
202
  };
168
203
 
169
204
  /**
@@ -172,14 +207,14 @@ export const syncOne = (appId: string): Promise<LiveUpdate> => {
172
207
  * @param appIds The AppFlow application IDs to sync.
173
208
  * @returns Promise<SyncResults>
174
209
  */
175
- export const syncSome = (appIds: string[]): Promise<SyncResults> => {
176
- return IONLiveUpdatesManager.syncSome(appIds);
210
+ export const syncSome = async (appIds: string[]): Promise<SyncResults> => {
211
+ return IONPortalsReactNative.syncSome(appIds);
177
212
  };
178
213
 
179
214
  /**
180
215
  * Syncs all registered LiveUpdates
181
216
  * @returns Promise<SyncResults>
182
217
  */
183
- export const syncAll = (): Promise<SyncResults> => {
184
- return IONLiveUpdatesManager.syncAll();
218
+ export const syncAll = async (): Promise<SyncResults> => {
219
+ return IONPortalsReactNative.syncAll();
185
220
  };
@@ -1,16 +0,0 @@
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
@@ -1,262 +0,0 @@
1
- import Foundation
2
- import IonicPortals
3
- import IonicLiveUpdates
4
- import React
5
- import UIKit
6
- import Capacitor
7
-
8
- @objc(IONPortalManager)
9
- public class PortalManager: NSObject {
10
- private static var portals: [String: Portal] = [:]
11
-
12
- public static func register(_ key: String) {
13
- PortalsRegistrationManager.shared.register(key: key)
14
- }
15
-
16
- public static func add(_ portal: Portal) {
17
- portals[portal.name] = portal
18
- }
19
-
20
- static func getPortal(named name: String) -> Portal? { portals[name] }
21
-
22
- @objc func register(_ key: String) {
23
- Self.register(key)
24
- }
25
-
26
- @objc func addPortal(_ portalDict: [String: Any]) {
27
- guard let portal = Portal(portalDict) else { return }
28
- Self.add(portal)
29
- }
30
-
31
- @objc static func requiresMainQueueSetup() -> Bool { true }
32
- }
33
-
34
- @objc(IONPortalPubSub)
35
- class PortalsPubSub: RCTEventEmitter {
36
- private let eventName = "PortalsSubscription"
37
-
38
- override func supportedEvents() -> [String]! {
39
- [eventName]
40
- }
41
-
42
- @objc func subscribe(_ topic: String, resolver: RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
43
- let subRef = IonicPortals.PortalsPubSub.subscribe(topic) { [weak self] result in
44
- guard let self = self else { return }
45
- self.sendEvent(
46
- withName: self.eventName,
47
- body: [
48
- "subscriptionRef": result.subscriptionRef,
49
- "topic": result.topic,
50
- "data": result.data
51
- ]
52
- )
53
- }
54
-
55
- resolver(subRef)
56
- }
57
-
58
- @objc func unsubscribe(_ topic: String, subscriptionRef: NSNumber) {
59
- IonicPortals.PortalsPubSub.unsubscribe(from: topic, subscriptionRef: subscriptionRef.intValue)
60
- }
61
-
62
- @objc func publish(_ topic: String, data: Any) {
63
- IONPortalsPubSub.publish(message: data, topic: topic)
64
- }
65
-
66
- override class func requiresMainQueueSetup() -> Bool { true }
67
- }
68
-
69
- @objc(IONPortalViewManager)
70
- class PortalViewManager: RCTViewManager {
71
- override class func requiresMainQueueSetup() -> Bool { true }
72
- override func view() -> UIView! { PortalView() }
73
- }
74
-
75
- class PortalView: UIView {
76
- private var webView: PortalUIView?
77
-
78
- @objc var portal: [String: Any]? {
79
- get {
80
- guard let _portal = _portal else { return nil }
81
- return [
82
- "name": _portal.name,
83
- "initialContext": _portal.initialContext
84
- ]
85
- }
86
-
87
- set {
88
- guard let portalDict = newValue,
89
- let name = portalDict["name"] as? String
90
- else { return }
91
-
92
- var portal = PortalManager.getPortal(named: name)
93
- if let initialContext = portalDict["initialContext"] as? [String: Any] {
94
- portal?.initialContext = JSTypes.coerceDictionaryToJSObject(initialContext) ?? [:]
95
- }
96
-
97
- _portal = portal
98
- }
99
- }
100
-
101
- private var _portal: Portal? {
102
- didSet {
103
- guard let portal = _portal else { return }
104
-
105
- DispatchQueue.main.async { [weak self] in
106
- guard let self = self else { return }
107
- self.webView?.removeFromSuperview()
108
- let webView = PortalUIView(portal: portal)
109
- webView.translatesAutoresizingMaskIntoConstraints = false
110
- self.addSubview(webView)
111
- NSLayoutConstraint.activate([
112
- webView.topAnchor.constraint(equalTo: self.topAnchor),
113
- webView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
114
- webView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
115
- webView.trailingAnchor.constraint(equalTo: self.trailingAnchor)
116
- ])
117
- self.webView = webView
118
- }
119
- }
120
- }
121
- }
122
-
123
- extension Portal {
124
- init?(_ dict: [String: Any]) {
125
- guard let name = dict["name"] as? String else { return nil }
126
- self.init(
127
- name: name,
128
- startDir: dict["startDir"] as? String,
129
- index: dict["index"] as? String ?? "index.html",
130
- initialContext: JSTypes.coerceDictionaryToJSObject(dict["initialContext"] as? [String: Any]) ?? [:],
131
- liveUpdateConfig: (dict["liveUpdate"] as? [String: Any]).flatMap(LiveUpdate.init)
132
- )
133
- }
134
- }
135
-
136
- extension LiveUpdate {
137
- init?(_ dict: [String: Any]) {
138
- guard let appId = dict["appId"] as? String,
139
- let channel = dict["channel"] as? String,
140
- let syncOnAdd = dict["syncOnAdd"] as? Bool
141
- else { return nil }
142
-
143
- self.init(appId: appId, channel: channel, syncOnAdd: syncOnAdd)
144
- }
145
- }
146
-
147
- extension LiveUpdate {
148
- var dict: [String: Any] {
149
- return [
150
- "appId": appId,
151
- "channel": channel,
152
- "syncOnAdd": syncOnAdd
153
- ]
154
- }
155
- }
156
-
157
- extension LiveUpdateManager.Error {
158
- var dict: [String: Any] {
159
- return [
160
- "appId": appId,
161
- "failStep": failStep.rawValue.uppercased(),
162
- "message": localizedDescription
163
- ]
164
- }
165
- }
166
-
167
- private struct SyncResults {
168
- var liveUpdates: [LiveUpdate]
169
- var errors: [LiveUpdateManager.Error]
170
- }
171
-
172
- extension SyncResults {
173
- var dict: [String: Any] {
174
- return [
175
- "liveUpdates": liveUpdates.map(\.dict),
176
- "errors": errors.map(\.dict)
177
- ]
178
- }
179
- }
180
-
181
- @objc(IONLiveUpdatesManager)
182
- public class LiveUpdatesManager: NSObject {
183
- private var lum = LiveUpdateManager.shared
184
-
185
- @objc func addLiveUpdate(_ dict: [String: Any]) {
186
- guard let liveUpdate = LiveUpdate(dict) else { return }
187
- try? lum.add(liveUpdate)
188
- }
189
-
190
- @objc func syncOne(_ appId: String, resolver: @escaping RCTPromiseResolveBlock, rejector: @escaping RCTPromiseRejectBlock) {
191
- lum.sync(appId: appId, isParallel: true) { result in
192
- switch result {
193
- case .success(let update):
194
- resolver(update.dict)
195
- case .failure(let error):
196
- rejector(nil, nil, error)
197
- }
198
- }
199
- }
200
-
201
- @objc func syncSome(_ appIds: [String], resolver: @escaping RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
202
- Task {
203
- let syncResult = await lum.syncSome(appIds)
204
- resolver(syncResult.dict)
205
- }
206
- }
207
-
208
- @objc func syncAll(_ resolver: @escaping RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
209
- Task {
210
- let syncResult = await lum.syncAll()
211
- resolver(syncResult.dict)
212
- }
213
- }
214
-
215
- @objc static func requiresMainQueueSetup() -> Bool { true }
216
- }
217
-
218
- extension LiveUpdateManager {
219
- fileprivate func syncSome(_ appIds: [String]) async -> SyncResults {
220
- await _syncSome(appIds).syncResults
221
- }
222
-
223
- private func _syncSome(_ appIds: [String]) -> AsyncStream<Result<LiveUpdate, LiveUpdateManager.Error>> {
224
- AsyncStream { continuation in
225
- sync(appIds: appIds, isParallel: true) {
226
- continuation.finish()
227
- } appComplete: { result in
228
- continuation.yield(result)
229
- }
230
- }
231
- }
232
-
233
- fileprivate func syncAll() async -> SyncResults {
234
- await _syncAll().syncResults
235
- }
236
-
237
-
238
- private func _syncAll() -> AsyncStream<Result<LiveUpdate, LiveUpdateManager.Error>> {
239
- AsyncStream { continuation in
240
- sync(isParallel: true) {
241
- continuation.finish()
242
- } appComplete: { result in
243
- continuation.yield(result)
244
- }
245
- }
246
- }
247
- }
248
-
249
- extension AsyncStream where Element == Result<LiveUpdate, LiveUpdateManager.Error> {
250
- fileprivate var syncResults: SyncResults {
251
- get async {
252
- await reduce(into: SyncResults(liveUpdates: [], errors: [])) { acc, next in
253
- switch next {
254
- case .success(let liveUpdate):
255
- acc.liveUpdates.append(liveUpdate)
256
- case .failure(let error):
257
- acc.errors.append(error)
258
- }
259
- }
260
- }
261
- }
262
- }