@ionic/portals-react-native 0.2.0 → 0.4.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 (37) hide show
  1. package/README.md +2 -2
  2. package/ReactNativePortals.podspec +14 -14
  3. package/android/build.gradle +4 -5
  4. package/android/gradle.properties +1 -1
  5. package/android/src/main/java/io/ionic/portals/reactnative/PortalView.kt +14 -4
  6. package/android/src/main/java/io/ionic/portals/reactnative/ReactNativeLiveUpdatesModule.kt +57 -74
  7. package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalManager.kt +84 -21
  8. package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalsModule.kt +19 -22
  9. package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalsPackage.kt +1 -0
  10. package/android/src/main/java/io/ionic/portals/reactnative/ReactNativeWebVitalsModule.kt +52 -0
  11. package/ios/AssetMap+Dict.swift +28 -0
  12. package/ios/ConcurrentDictionary.swift +45 -0
  13. package/ios/LiveUpdateManager+Async.swift +7 -7
  14. package/ios/Podfile +3 -4
  15. package/ios/Podfile.lock +13 -13
  16. package/ios/Portal.swift +108 -0
  17. package/ios/PortalManager.m +1 -1
  18. package/ios/PortalView.swift +1 -1
  19. package/ios/PortalWebVitals.m +15 -0
  20. package/ios/PortalsConfig.swift +18 -7
  21. package/ios/PortalsPubSub.swift +1 -0
  22. package/ios/PortalsReactNative.swift +1 -1
  23. package/ios/ReactNativePortals.xcodeproj/project.pbxproj +24 -4
  24. package/ios/SyncResult+Dict.swift +35 -0
  25. package/ios/WebVitals.swift +35 -0
  26. package/lib/commonjs/PortalView.android.js.map +1 -1
  27. package/lib/commonjs/PortalView.js.map +1 -1
  28. package/lib/commonjs/index.js +63 -3
  29. package/lib/commonjs/index.js.map +1 -1
  30. package/lib/module/PortalView.android.js.map +1 -1
  31. package/lib/module/PortalView.js.map +1 -1
  32. package/lib/module/index.js +55 -2
  33. package/lib/module/index.js.map +1 -1
  34. package/lib/typescript/index.d.ts +51 -7
  35. package/package.json +1 -1
  36. package/src/index.ts +128 -5
  37. package/ios/Portal+Dict.swift +0 -35
@@ -1 +1 @@
1
- {"version":3,"names":["IONPortalPubSub","IONPortalsReactNative","NativeModules","PortalsPubSub","NativeEventEmitter","subscriptionMap","Map","subscribe","topic","onMessageReceived","subscriptionRef","subscriber","addListener","message","set","unsubscribe","subRef","subscription","get","undefined","remove","delete","publish","data","msg","register","key","addPortal","portal","addPortals","portals","getPortal","name","enableSecureLiveUpdates","pathToKey","syncOne","appId","syncSome","appIds","syncAll"],"sources":["index.ts"],"sourcesContent":["import {\n EmitterSubscription,\n NativeEventEmitter,\n NativeModules,\n ViewProps,\n} from 'react-native';\n\nconst { IONPortalPubSub, IONPortalsReactNative } = NativeModules;\n\nexport { default as PortalView } from './PortalView';\n\n/**\n * The data that is received from a subscription event.\n */\nexport interface Message {\n /** The unique subscription reference received from {@link subscribe}*/\n subscriptionRef: number;\n data: any;\n /** The topic the message was sent from */\n topic: string;\n}\n\nconst PortalsPubSub = new NativeEventEmitter(IONPortalPubSub);\n\nconst subscriptionMap = new Map<number, EmitterSubscription>();\n\n/**\n * Subscribes to messages for a topic\n *\n * @param topic The topic to subscribe to\n * @param onMessageReceived The callback to invoke when a message is received\n * @returns A Promise<number> containing the unique subscription reference. This will need to be stored for calling {@link unsubscribe}.\n */\nexport const subscribe = async (\n topic: string,\n onMessageReceived: (message: Message) => void\n): Promise<number> => {\n const subscriptionRef = await IONPortalPubSub.subscribe(topic);\n\n const subscriber = PortalsPubSub.addListener(\n 'PortalsSubscription',\n (message: Message) => {\n if (message.subscriptionRef === subscriptionRef) {\n onMessageReceived(message);\n }\n }\n );\n\n subscriptionMap.set(subscriptionRef, subscriber);\n\n return subscriptionRef;\n};\n\n/**\n * Unsubscribes from events for the provided topic and subscription reference\n *\n * @param topic The topic to unsubscribe from\n * @param subRef The unique subscription reference received when initially calling {@link subscribe}\n */\nexport const unsubscribe = (topic: string, subRef: number) => {\n IONPortalPubSub.unsubscribe(topic, subRef);\n\n const subscription = subscriptionMap.get(subRef);\n if (subscription !== undefined) {\n subscription.remove();\n subscriptionMap.delete(subRef);\n }\n};\n\n/**\n * Publishes a message to the provided topic\n *\n * @param topic The topic to publish the message to\n * @param data The data to publish to subscribers\n */\nexport const publish = (topic: string, data: any) => {\n const msg = { message: data };\n IONPortalPubSub.publish(topic, msg);\n};\n\n/**\n * Validates that a valid registration key has been procured from http://ionic.io/register-portals\n * @param key The registration key\n * @returns Promise<void>\n */\nexport const register = async (key: string): Promise<void> => {\n return IONPortalsReactNative.register(key);\n};\n\n/**\n * The configuration of a web application to be embedded in a React Native application.\n */\nexport interface Portal {\n /** The name of the Portal to be referenced. Must be **unique** */\n name: string;\n /** The classpath of all Capacitor plugins used in Android. (e.g. com.capacitorjs.plugins.camera.CameraPlugin) */\n androidPlugins?: string[];\n /**\n * The root directory of the web application relative to Bundle.main on iOS\n * and src/main/assets on Android. If omitted, `name` is used.\n */\n startDir?: string;\n /** The name of the initial file to load. If omitted, 'index.html' is used. */\n index?: string;\n /** Any data needed at initial render when a portal is loaded. */\n initialContext?: {\n [key: string]: any;\n };\n liveUpdate?: LiveUpdateConfig;\n}\n\n/**\n * A subset of {@link Portal} properties needed for rendering a Portal. `initialContext` can be used to override\n * any initialContext defined in the original {@link Portal} definition.\n */\nexport type PortalProp = {\n portal: Pick<Portal, 'name' | 'initialContext'>;\n};\n\n/**\n * Props needed for rendering a {@link Portal}\n */\nexport type PortalProps = PortalProp & ViewProps;\n\n/**\n * Adds a Portal to an internal registry. Must be called before attempting to render a {@link PortalView}.\n *\n * @param portal The portal to add to the internal registry.\n * @returns Promise containing the Portal that was added to the registry.\n */\nexport const addPortal = async (portal: Portal): Promise<Portal> => {\n return IONPortalsReactNative.addPortal(portal);\n};\n\n/**\n * Adds all portals to an internal registry. This or {@link addPortal} must be called before attempting to render a {@link PortalView}\n *\n * @param portals The portals to add to the internal registry.\n * @returns Promise containing the Portals that were added to the registry.\n */\nexport const addPortals = async (portals: Portal[]): Promise<Portal[]> => {\n return IONPortalsReactNative.addPortals(portals);\n};\n\n/**\n * Gets a {@link Portal} previously registered via {@link addPortal} or {@link addPortals}.\n *\n * @param name The portal name to retrieve from the internal registry.\n * @returns Promise containing the registered {@link Portal}. If the {@link Portal} was not registered, the Promise will fail.\n */\nexport const getPortal = async (name: string): Promise<Portal> => {\n return IONPortalsReactNative.getPortal(name);\n};\n\nexport interface LiveUpdate {\n /** The AppFlow application ID */\n appId: string;\n /** The AppFlow distribution channel */\n channel: string;\n}\n\n/** Data needed to register a live update to be managed */\nexport type LiveUpdateConfig = LiveUpdate & { syncOnAdd: boolean };\n\nexport interface LiveUpdateError {\n /** The AppFlow application ID relating to the failure */\n appId: string;\n /** The step in the sync process the LiveUpdate failed on. (e.g. CHECK, UNPACK)*/\n failStep: string;\n /** A human readable error message */\n message: string;\n}\n\n/** Used for communicating sync results of multiple live updates */\nexport interface SyncResults {\n liveUpdates: LiveUpdate[];\n errors: LiveUpdateError[];\n}\n\n/**\n * Configures LiveUpdates to cyrptographically verify the contents of the downloaded bundles.\n * This method must be called before any LiveUpdates are registered otherwise they will no longer be tracked.\n *\n * @param pathToKey The *relative* path to the public key for verification.\n * This path should be the same relatibe to the main application bundle on iOS and the assets directory on Android.\n * @returns Promise<void>\n */\nexport const enableSecureLiveUpdates = async (\n pathToKey: string\n): Promise<void> => {\n return IONPortalsReactNative.enableSecureLiveUpdates(pathToKey);\n};\n\n/**\n * Syncs a single live update.\n *\n * @param appId The AppFlow application ID to sync.\n * @returns A Promise<LiveUpdate>. A failure should result in a {@link LiveUpdateError}.\n */\nexport const syncOne = async (appId: string): Promise<LiveUpdate> => {\n return IONPortalsReactNative.syncOne(appId);\n};\n\n/**\n * Syncs many live updates.\n *\n * @param appIds The AppFlow application IDs to sync.\n * @returns Promise<SyncResults>\n */\nexport const syncSome = async (appIds: string[]): Promise<SyncResults> => {\n return IONPortalsReactNative.syncSome(appIds);\n};\n\n/**\n * Syncs all registered LiveUpdates\n * @returns Promise<SyncResults>\n */\nexport const syncAll = async (): Promise<SyncResults> => {\n return IONPortalsReactNative.syncAll();\n};\n"],"mappings":"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"}
1
+ {"version":3,"names":["_reactNative","require","_PortalView","_interopRequireDefault","obj","__esModule","default","IONPortalPubSub","IONPortalsReactNative","IONPortalsWebVitals","NativeModules","PortalsPubSub","NativeEventEmitter","subscriptionMap","Map","subscribe","topic","onMessageReceived","subscriptionRef","subscriber","addListener","message","set","exports","webVitalsMap","WebVitals","onFirstContentfulPaint","portalName","callback","listener","event","duration","registerOnFirstContentfulPaint","onFirstInputDelay","Platform","OS","registerOnFirstInputDelay","onTimeToFirstByte","registerOnTimeToFirstByte","registerWebVitals","firstContentfulPaint","firstInputDelay","timeToFirstByte","unsubscribe","subRef","subscription","get","undefined","remove","delete","publish","data","msg","register","key","addPortal","portal","addPortals","portals","getPortal","name","enableSecureLiveUpdates","pathToKey","syncOne","appId","syncSome","appIds","syncAll"],"sources":["index.ts"],"sourcesContent":["import {\n EmitterSubscription,\n NativeEventEmitter,\n NativeModules,\n Platform,\n ViewProps,\n} from 'react-native';\n\nconst { IONPortalPubSub, IONPortalsReactNative, IONPortalsWebVitals } =\n NativeModules;\n\nexport { default as PortalView } from './PortalView';\n\n/**\n * The data that is received from a subscription event.\n */\nexport interface Message {\n /** The unique subscription reference received from {@link subscribe}*/\n subscriptionRef: number;\n data: any;\n /** The topic the message was sent from */\n topic: string;\n}\n\nconst PortalsPubSub = new NativeEventEmitter(IONPortalPubSub);\n\nconst subscriptionMap = new Map<number, EmitterSubscription>();\n\n/**\n * Subscribes to messages for a topic\n *\n * @param topic The topic to subscribe to\n * @param onMessageReceived The callback to invoke when a message is received\n * @returns A Promise<number> containing the unique subscription reference. This will need to be stored for calling {@link unsubscribe}.\n */\nexport const subscribe = async (\n topic: string,\n onMessageReceived: (message: Message) => void\n): Promise<number> => {\n const subscriptionRef = await IONPortalPubSub.subscribe(topic);\n\n const subscriber = PortalsPubSub.addListener(\n 'PortalsSubscription',\n (message: Message) => {\n if (message.subscriptionRef === subscriptionRef) {\n onMessageReceived(message);\n }\n }\n );\n\n subscriptionMap.set(subscriptionRef, subscriber);\n\n return subscriptionRef;\n};\n\nconst webVitalsMap = new Map<string, EmitterSubscription>();\nconst WebVitals = new NativeEventEmitter(IONPortalsWebVitals);\n\ninterface WebVitalsEvent {\n portalName: string;\n duration: number;\n}\n\nexport const onFirstContentfulPaint = async (\n portalName: string,\n callback: (duration: number) => void\n): Promise<void> => {\n const listener = WebVitals.addListener(\n 'vitals:fcp',\n (event: WebVitalsEvent) => {\n if (event.portalName === portalName) {\n callback(event.duration);\n }\n }\n );\n\n await IONPortalsWebVitals.registerOnFirstContentfulPaint(portalName);\n\n webVitalsMap.set(`${portalName}-vitals:fcp`, listener);\n};\n\nexport const onFirstInputDelay = async (\n portalName: string,\n callback: (duration: number) => void\n) => {\n if (Platform.OS === 'android') {\n const listener = WebVitals.addListener(\n 'vitals:fid',\n (event: WebVitalsEvent) => {\n if (event.portalName === portalName) {\n callback(event.duration);\n }\n }\n );\n\n await IONPortalsWebVitals.registerOnFirstInputDelay(portalName);\n\n webVitalsMap.set(`${portalName}-vitals:fcp`, listener);\n }\n};\n\nexport const onTimeToFirstByte = async (\n portalName: string,\n callback: (duration: number) => void\n) => {\n if (Platform.OS === 'android') {\n const listener = WebVitals.addListener(\n 'vitals:ttfb',\n (event: WebVitalsEvent) => {\n if (event.portalName === portalName) {\n callback(event.duration);\n }\n }\n );\n\n await IONPortalsWebVitals.registerOnTimeToFirstByte(portalName);\n\n webVitalsMap.set(`${portalName}-vitals:ttfb`, listener);\n }\n};\n\nexport const registerWebVitals = async (\n portalName: string,\n firstContentfulPaint: (duration: number) => void,\n firstInputDelay: (duration: number) => void,\n timeToFirstByte: (duration: number) => void\n) => {\n onFirstContentfulPaint(portalName, firstContentfulPaint);\n onFirstInputDelay(portalName, firstInputDelay);\n onTimeToFirstByte(portalName, timeToFirstByte);\n};\n\n/**\n * Unsubscribes from events for the provided topic and subscription reference\n *\n * @param topic The topic to unsubscribe from\n * @param subRef The unique subscription reference received when initially calling {@link subscribe}\n */\nexport const unsubscribe = (topic: string, subRef: number) => {\n IONPortalPubSub.unsubscribe(topic, subRef);\n\n const subscription = subscriptionMap.get(subRef);\n if (subscription !== undefined) {\n subscription.remove();\n subscriptionMap.delete(subRef);\n }\n};\n\n/**\n * Publishes a message to the provided topic\n *\n * @param topic The topic to publish the message to\n * @param data The data to publish to subscribers\n */\nexport const publish = (topic: string, data: any) => {\n const msg = { message: data };\n IONPortalPubSub.publish(topic, msg);\n};\n\n/**\n * Validates that a valid registration key has been procured from http://ionic.io/register-portals\n * @param key The registration key\n * @returns Promise<void>\n */\nexport const register = async (key: string): Promise<void> => {\n return IONPortalsReactNative.register(key);\n};\n\n/**\n * The configuration of a web application to be embedded in a React Native application.\n */\nexport interface Portal {\n /** The name of the Portal to be referenced. Must be **unique** */\n name: string;\n /** Any Capacitor plugins to be made available to the Portal */\n plugins?: CapacitorPlugin[];\n /**\n * The root directory of the web application relative to Bundle.main on iOS\n * and src/main/assets on Android. If omitted, `name` is used.\n */\n startDir?: string;\n /** The name of the initial file to load. If omitted, 'index.html' is used. */\n index?: string;\n /** Any data needed at initial render when a portal is loaded. */\n initialContext?: {\n [key: string]: any;\n };\n assetMaps?: AssetMap[];\n liveUpdate?: LiveUpdateConfig;\n}\n\nexport interface CapacitorPlugin {\n /** The classpath of the plugin to be used in Android. (e.g. com.capacitorjs.plugins.camera.CameraPlugin) */\n androidClassPath: string;\n /** The class name of the plugin to be used in iOS.\n * This must be the name as it is exposed to the Objective-C runtime.\n * For example, The CameraPlugin swift class is exposed to Objective-C as CAPCameraPlugin.\n */\n iosClassName: string;\n}\n\nexport interface AssetMap {\n /** The name to index the asset map by */\n name: string;\n /** Any path to match via the web. If omitted, {@link AssetMap#name} will be used. */\n virtualPath?: string;\n /** The root directory of the assets relative to Bundle.main on iOS\n * and src/main/assets on Android. If omitted, the root of Bundle.main\n * and src/main/assets will be used.\n */\n startDir?: string;\n}\n\n/**\n * A subset of {@link Portal} properties needed for rendering a Portal. `initialContext` can be used to override\n * any initialContext defined in the original {@link Portal} definition.\n */\nexport type PortalProp = {\n portal: Pick<Portal, 'name' | 'initialContext'>;\n};\n\n/**\n * Props needed for rendering a {@link Portal}\n */\nexport type PortalProps = PortalProp & ViewProps;\n\n/**\n * Adds a Portal to an internal registry. Must be called before attempting to render a {@link PortalView}.\n *\n * @param portal The portal to add to the internal registry.\n * @returns Promise containing the Portal that was added to the registry.\n */\nexport const addPortal = async (portal: Portal): Promise<Portal> => {\n return IONPortalsReactNative.addPortal(portal);\n};\n\n/**\n * Adds all portals to an internal registry. This or {@link addPortal} must be called before attempting to render a {@link PortalView}\n *\n * @param portals The portals to add to the internal registry.\n * @returns Promise containing the Portals that were added to the registry.\n */\nexport const addPortals = async (portals: Portal[]): Promise<Portal[]> => {\n return IONPortalsReactNative.addPortals(portals);\n};\n\n/**\n * Gets a {@link Portal} previously registered via {@link addPortal} or {@link addPortals}.\n *\n * @param name The portal name to retrieve from the internal registry.\n * @returns Promise containing the registered {@link Portal}. If the {@link Portal} was not registered, the Promise will fail.\n */\nexport const getPortal = async (name: string): Promise<Portal> => {\n return IONPortalsReactNative.getPortal(name);\n};\n\nexport interface LiveUpdate {\n /** The AppFlow application ID */\n appId: string;\n /** The AppFlow distribution channel */\n channel: string;\n}\n\n/** Data needed to register a live update to be managed */\nexport type LiveUpdateConfig = LiveUpdate & { syncOnAdd: boolean };\n\nexport interface LiveUpdateError {\n /** The AppFlow application ID relating to the failure */\n appId: string;\n /** The step in the sync process the LiveUpdate failed on. (e.g. CHECK, UNPACK)*/\n failStep: string;\n /** A human readable error message */\n message: string;\n}\n\nexport interface Snapshot {\n /** The snapshot id as found in AppFlow */\n id: string;\n /** The AppFlow build id that produced the snapshot */\n buildId: string;\n}\n\nexport interface SyncResult {\n /** The {@link LiveUpdate} associated with the result */\n liveUpdate: LiveUpdate;\n /** The {@link Snapshot} that was sync'd */\n snapshot: Snapshot | null;\n /** Whether the snapshot was downloaded or already on disk */\n source: 'download' | 'cache';\n /** If the active application path was changed. A `false` value would indicate\n * the application already has the latest code for the associated {@link LiveUpdate}\n * configuration.\n */\n activeApplicationPathChanged: boolean;\n}\n\n/** Used for communicating sync results of multiple live updates */\nexport interface SyncResults {\n results: SyncResult[];\n errors: LiveUpdateError[];\n}\n\n/**\n * Configures LiveUpdates to cyrptographically verify the contents of the downloaded bundles.\n * This method must be called before any LiveUpdates are registered otherwise they will no longer be tracked.\n *\n * @param pathToKey The *relative* path to the public key for verification.\n * This path should be the same relatibe to the main application bundle on iOS and the assets directory on Android.\n * @returns Promise<void>\n */\nexport const enableSecureLiveUpdates = async (\n pathToKey: string\n): Promise<void> => {\n return IONPortalsReactNative.enableSecureLiveUpdates(pathToKey);\n};\n\n/**\n * Syncs a single live update.\n *\n * @param appId The AppFlow application ID to sync.\n * @returns A Promise<LiveUpdate>. A failure should result in a {@link LiveUpdateError}.\n */\nexport const syncOne = async (appId: string): Promise<SyncResult> => {\n return IONPortalsReactNative.syncOne(appId);\n};\n\n/**\n * Syncs many live updates.\n *\n * @param appIds The AppFlow application IDs to sync.\n * @returns Promise<SyncResults>\n */\nexport const syncSome = async (appIds: string[]): Promise<SyncResults> => {\n return IONPortalsReactNative.syncSome(appIds);\n};\n\n/**\n * Syncs all registered LiveUpdates\n * @returns Promise<SyncResults>\n */\nexport const syncAll = async (): Promise<SyncResults> => {\n return IONPortalsReactNative.syncAll();\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"names":["React","useEffect","useRef","findNodeHandle","requireNativeComponent","UIManager","PortalViewManager","createFragment","viewId","dispatchViewManagerCommand","AndroidPortalView","Commands","create","toString","PortalView","props","ref","current"],"sources":["PortalView.android.tsx"],"sourcesContent":["import React, { useEffect, useRef } from 'react';\nimport {\n findNodeHandle,\n requireNativeComponent,\n UIManager,\n} from 'react-native';\nimport type { PortalProps } from '.';\n\nconst PortalViewManager = requireNativeComponent('AndroidPortalView');\n\nconst createFragment = (viewId: number | null) =>\n UIManager.dispatchViewManagerCommand(\n viewId,\n // we are calling the 'create' command\n // @ts-expect-error\n UIManager.AndroidPortalView.Commands.create.toString(),\n [viewId]\n );\n\nconst PortalView = (props: PortalProps) => {\n const ref = useRef(null);\n\n useEffect(() => {\n const viewId = findNodeHandle(ref.current);\n createFragment(viewId);\n }, []);\n\n return <PortalViewManager {...props} ref={ref} />;\n};\n\nexport default PortalView;\n"],"mappings":";AAAA,OAAOA,KAAK,IAAIC,SAAS,EAAEC,MAAM,QAAQ,OAAO;AAChD,SACEC,cAAc,EACdC,sBAAsB,EACtBC,SAAS,QACJ,cAAc;AAGrB,MAAMC,iBAAiB,GAAGF,sBAAsB,CAAC,mBAAmB,CAAC;AAErE,MAAMG,cAAc,GAAIC,MAAqB,IAC3CH,SAAS,CAACI,0BAA0B,CAClCD,MAAM;AACN;AACA;AACAH,SAAS,CAACK,iBAAiB,CAACC,QAAQ,CAACC,MAAM,CAACC,QAAQ,EAAE,EACtD,CAACL,MAAM,CAAC,CACT;AAEH,MAAMM,UAAU,GAAIC,KAAkB,IAAK;EACzC,MAAMC,GAAG,GAAGd,MAAM,CAAC,IAAI,CAAC;EAExBD,SAAS,CAAC,MAAM;IACd,MAAMO,MAAM,GAAGL,cAAc,CAACa,GAAG,CAACC,OAAO,CAAC;IAC1CV,cAAc,CAACC,MAAM,CAAC;EACxB,CAAC,EAAE,EAAE,CAAC;EAEN,oBAAO,oBAAC,iBAAiB,eAAKO,KAAK;IAAE,GAAG,EAAEC;EAAI,GAAG;AACnD,CAAC;AAED,eAAeF,UAAU"}
1
+ {"version":3,"names":["React","useEffect","useRef","findNodeHandle","requireNativeComponent","UIManager","PortalViewManager","createFragment","viewId","dispatchViewManagerCommand","AndroidPortalView","Commands","create","toString","PortalView","props","ref","current","createElement","_extends"],"sources":["PortalView.android.tsx"],"sourcesContent":["import React, { useEffect, useRef } from 'react';\nimport {\n findNodeHandle,\n requireNativeComponent,\n UIManager,\n} from 'react-native';\nimport type { PortalProps } from '.';\n\nconst PortalViewManager = requireNativeComponent('AndroidPortalView');\n\nconst createFragment = (viewId: number | null) =>\n UIManager.dispatchViewManagerCommand(\n viewId,\n // we are calling the 'create' command\n // @ts-expect-error\n UIManager.AndroidPortalView.Commands.create.toString(),\n [viewId]\n );\n\nconst PortalView = (props: PortalProps) => {\n const ref = useRef(null);\n\n useEffect(() => {\n const viewId = findNodeHandle(ref.current);\n createFragment(viewId);\n }, []);\n\n return <PortalViewManager {...props} ref={ref} />;\n};\n\nexport default PortalView;\n"],"mappings":";AAAA,OAAOA,KAAK,IAAIC,SAAS,EAAEC,MAAM,QAAQ,OAAO;AAChD,SACEC,cAAc,EACdC,sBAAsB,EACtBC,SAAS,QACJ,cAAc;AAGrB,MAAMC,iBAAiB,GAAGF,sBAAsB,CAAC,mBAAmB,CAAC;AAErE,MAAMG,cAAc,GAAIC,MAAqB,IAC3CH,SAAS,CAACI,0BAA0B,CAClCD,MAAM;AACN;AACA;AACAH,SAAS,CAACK,iBAAiB,CAACC,QAAQ,CAACC,MAAM,CAACC,QAAQ,EAAE,EACtD,CAACL,MAAM,CAAC,CACT;AAEH,MAAMM,UAAU,GAAIC,KAAkB,IAAK;EACzC,MAAMC,GAAG,GAAGd,MAAM,CAAC,IAAI,CAAC;EAExBD,SAAS,CAAC,MAAM;IACd,MAAMO,MAAM,GAAGL,cAAc,CAACa,GAAG,CAACC,OAAO,CAAC;IAC1CV,cAAc,CAACC,MAAM,CAAC;EACxB,CAAC,EAAE,EAAE,CAAC;EAEN,oBAAOR,KAAA,CAAAkB,aAAA,CAACZ,iBAAiB,EAAAa,QAAA,KAAKJ,KAAK;IAAEC,GAAG,EAAEA;EAAI,GAAG;AACnD,CAAC;AAED,eAAeF,UAAU"}
@@ -1 +1 @@
1
- {"version":3,"names":["React","requireNativeComponent","HostComponentPortal","PortalView","props"],"sources":["PortalView.tsx"],"sourcesContent":["import React from 'react';\nimport { requireNativeComponent } from 'react-native';\nimport type { PortalProps } from '.';\n\nconst HostComponentPortal = requireNativeComponent('IONPortalView');\n\nconst PortalView = (props: PortalProps) => {\n return <HostComponentPortal {...props} />;\n};\n\nexport default PortalView;\n"],"mappings":"AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,SAASC,sBAAsB,QAAQ,cAAc;AAGrD,MAAMC,mBAAmB,GAAGD,sBAAsB,CAAC,eAAe,CAAC;AAEnE,MAAME,UAAU,GAAIC,KAAkB,IAAK;EACzC,oBAAO,oBAAC,mBAAmB,EAAKA,KAAK,CAAI;AAC3C,CAAC;AAED,eAAeD,UAAU"}
1
+ {"version":3,"names":["React","requireNativeComponent","HostComponentPortal","PortalView","props","createElement"],"sources":["PortalView.tsx"],"sourcesContent":["import React from 'react';\nimport { requireNativeComponent } from 'react-native';\nimport type { PortalProps } from '.';\n\nconst HostComponentPortal = requireNativeComponent('IONPortalView');\n\nconst PortalView = (props: PortalProps) => {\n return <HostComponentPortal {...props} />;\n};\n\nexport default PortalView;\n"],"mappings":"AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,SAASC,sBAAsB,QAAQ,cAAc;AAGrD,MAAMC,mBAAmB,GAAGD,sBAAsB,CAAC,eAAe,CAAC;AAEnE,MAAME,UAAU,GAAIC,KAAkB,IAAK;EACzC,oBAAOJ,KAAA,CAAAK,aAAA,CAACH,mBAAmB,EAAKE,KAAK,CAAI;AAC3C,CAAC;AAED,eAAeD,UAAU"}
@@ -1,7 +1,8 @@
1
- import { NativeEventEmitter, NativeModules } from 'react-native';
1
+ import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
2
2
  const {
3
3
  IONPortalPubSub,
4
- IONPortalsReactNative
4
+ IONPortalsReactNative,
5
+ IONPortalsWebVitals
5
6
  } = NativeModules;
6
7
  export { default as PortalView } from './PortalView';
7
8
 
@@ -29,6 +30,44 @@ export const subscribe = async (topic, onMessageReceived) => {
29
30
  subscriptionMap.set(subscriptionRef, subscriber);
30
31
  return subscriptionRef;
31
32
  };
33
+ const webVitalsMap = new Map();
34
+ const WebVitals = new NativeEventEmitter(IONPortalsWebVitals);
35
+ export const onFirstContentfulPaint = async (portalName, callback) => {
36
+ const listener = WebVitals.addListener('vitals:fcp', event => {
37
+ if (event.portalName === portalName) {
38
+ callback(event.duration);
39
+ }
40
+ });
41
+ await IONPortalsWebVitals.registerOnFirstContentfulPaint(portalName);
42
+ webVitalsMap.set(`${portalName}-vitals:fcp`, listener);
43
+ };
44
+ export const onFirstInputDelay = async (portalName, callback) => {
45
+ if (Platform.OS === 'android') {
46
+ const listener = WebVitals.addListener('vitals:fid', event => {
47
+ if (event.portalName === portalName) {
48
+ callback(event.duration);
49
+ }
50
+ });
51
+ await IONPortalsWebVitals.registerOnFirstInputDelay(portalName);
52
+ webVitalsMap.set(`${portalName}-vitals:fcp`, listener);
53
+ }
54
+ };
55
+ export const onTimeToFirstByte = async (portalName, callback) => {
56
+ if (Platform.OS === 'android') {
57
+ const listener = WebVitals.addListener('vitals:ttfb', event => {
58
+ if (event.portalName === portalName) {
59
+ callback(event.duration);
60
+ }
61
+ });
62
+ await IONPortalsWebVitals.registerOnTimeToFirstByte(portalName);
63
+ webVitalsMap.set(`${portalName}-vitals:ttfb`, listener);
64
+ }
65
+ };
66
+ export const registerWebVitals = async (portalName, firstContentfulPaint, firstInputDelay, timeToFirstByte) => {
67
+ onFirstContentfulPaint(portalName, firstContentfulPaint);
68
+ onFirstInputDelay(portalName, firstInputDelay);
69
+ onTimeToFirstByte(portalName, timeToFirstByte);
70
+ };
32
71
 
33
72
  /**
34
73
  * Unsubscribes from events for the provided topic and subscription reference
@@ -71,6 +110,15 @@ export const register = async key => {
71
110
  * The configuration of a web application to be embedded in a React Native application.
72
111
  */
73
112
 
113
+ /**
114
+ * A subset of {@link Portal} properties needed for rendering a Portal. `initialContext` can be used to override
115
+ * any initialContext defined in the original {@link Portal} definition.
116
+ */
117
+
118
+ /**
119
+ * Props needed for rendering a {@link Portal}
120
+ */
121
+
74
122
  /**
75
123
  * Adds a Portal to an internal registry. Must be called before attempting to render a {@link PortalView}.
76
124
  *
@@ -100,6 +148,11 @@ export const addPortals = async portals => {
100
148
  export const getPortal = async name => {
101
149
  return IONPortalsReactNative.getPortal(name);
102
150
  };
151
+
152
+ /** Data needed to register a live update to be managed */
153
+
154
+ /** Used for communicating sync results of multiple live updates */
155
+
103
156
  /**
104
157
  * Configures LiveUpdates to cyrptographically verify the contents of the downloaded bundles.
105
158
  * This method must be called before any LiveUpdates are registered otherwise they will no longer be tracked.
@@ -1 +1 @@
1
- {"version":3,"names":["NativeEventEmitter","NativeModules","IONPortalPubSub","IONPortalsReactNative","default","PortalView","PortalsPubSub","subscriptionMap","Map","subscribe","topic","onMessageReceived","subscriptionRef","subscriber","addListener","message","set","unsubscribe","subRef","subscription","get","undefined","remove","delete","publish","data","msg","register","key","addPortal","portal","addPortals","portals","getPortal","name","enableSecureLiveUpdates","pathToKey","syncOne","appId","syncSome","appIds","syncAll"],"sources":["index.ts"],"sourcesContent":["import {\n EmitterSubscription,\n NativeEventEmitter,\n NativeModules,\n ViewProps,\n} from 'react-native';\n\nconst { IONPortalPubSub, IONPortalsReactNative } = NativeModules;\n\nexport { default as PortalView } from './PortalView';\n\n/**\n * The data that is received from a subscription event.\n */\nexport interface Message {\n /** The unique subscription reference received from {@link subscribe}*/\n subscriptionRef: number;\n data: any;\n /** The topic the message was sent from */\n topic: string;\n}\n\nconst PortalsPubSub = new NativeEventEmitter(IONPortalPubSub);\n\nconst subscriptionMap = new Map<number, EmitterSubscription>();\n\n/**\n * Subscribes to messages for a topic\n *\n * @param topic The topic to subscribe to\n * @param onMessageReceived The callback to invoke when a message is received\n * @returns A Promise<number> containing the unique subscription reference. This will need to be stored for calling {@link unsubscribe}.\n */\nexport const subscribe = async (\n topic: string,\n onMessageReceived: (message: Message) => void\n): Promise<number> => {\n const subscriptionRef = await IONPortalPubSub.subscribe(topic);\n\n const subscriber = PortalsPubSub.addListener(\n 'PortalsSubscription',\n (message: Message) => {\n if (message.subscriptionRef === subscriptionRef) {\n onMessageReceived(message);\n }\n }\n );\n\n subscriptionMap.set(subscriptionRef, subscriber);\n\n return subscriptionRef;\n};\n\n/**\n * Unsubscribes from events for the provided topic and subscription reference\n *\n * @param topic The topic to unsubscribe from\n * @param subRef The unique subscription reference received when initially calling {@link subscribe}\n */\nexport const unsubscribe = (topic: string, subRef: number) => {\n IONPortalPubSub.unsubscribe(topic, subRef);\n\n const subscription = subscriptionMap.get(subRef);\n if (subscription !== undefined) {\n subscription.remove();\n subscriptionMap.delete(subRef);\n }\n};\n\n/**\n * Publishes a message to the provided topic\n *\n * @param topic The topic to publish the message to\n * @param data The data to publish to subscribers\n */\nexport const publish = (topic: string, data: any) => {\n const msg = { message: data };\n IONPortalPubSub.publish(topic, msg);\n};\n\n/**\n * Validates that a valid registration key has been procured from http://ionic.io/register-portals\n * @param key The registration key\n * @returns Promise<void>\n */\nexport const register = async (key: string): Promise<void> => {\n return IONPortalsReactNative.register(key);\n};\n\n/**\n * The configuration of a web application to be embedded in a React Native application.\n */\nexport interface Portal {\n /** The name of the Portal to be referenced. Must be **unique** */\n name: string;\n /** The classpath of all Capacitor plugins used in Android. (e.g. com.capacitorjs.plugins.camera.CameraPlugin) */\n androidPlugins?: string[];\n /**\n * The root directory of the web application relative to Bundle.main on iOS\n * and src/main/assets on Android. If omitted, `name` is used.\n */\n startDir?: string;\n /** The name of the initial file to load. If omitted, 'index.html' is used. */\n index?: string;\n /** Any data needed at initial render when a portal is loaded. */\n initialContext?: {\n [key: string]: any;\n };\n liveUpdate?: LiveUpdateConfig;\n}\n\n/**\n * A subset of {@link Portal} properties needed for rendering a Portal. `initialContext` can be used to override\n * any initialContext defined in the original {@link Portal} definition.\n */\nexport type PortalProp = {\n portal: Pick<Portal, 'name' | 'initialContext'>;\n};\n\n/**\n * Props needed for rendering a {@link Portal}\n */\nexport type PortalProps = PortalProp & ViewProps;\n\n/**\n * Adds a Portal to an internal registry. Must be called before attempting to render a {@link PortalView}.\n *\n * @param portal The portal to add to the internal registry.\n * @returns Promise containing the Portal that was added to the registry.\n */\nexport const addPortal = async (portal: Portal): Promise<Portal> => {\n return IONPortalsReactNative.addPortal(portal);\n};\n\n/**\n * Adds all portals to an internal registry. This or {@link addPortal} must be called before attempting to render a {@link PortalView}\n *\n * @param portals The portals to add to the internal registry.\n * @returns Promise containing the Portals that were added to the registry.\n */\nexport const addPortals = async (portals: Portal[]): Promise<Portal[]> => {\n return IONPortalsReactNative.addPortals(portals);\n};\n\n/**\n * Gets a {@link Portal} previously registered via {@link addPortal} or {@link addPortals}.\n *\n * @param name The portal name to retrieve from the internal registry.\n * @returns Promise containing the registered {@link Portal}. If the {@link Portal} was not registered, the Promise will fail.\n */\nexport const getPortal = async (name: string): Promise<Portal> => {\n return IONPortalsReactNative.getPortal(name);\n};\n\nexport interface LiveUpdate {\n /** The AppFlow application ID */\n appId: string;\n /** The AppFlow distribution channel */\n channel: string;\n}\n\n/** Data needed to register a live update to be managed */\nexport type LiveUpdateConfig = LiveUpdate & { syncOnAdd: boolean };\n\nexport interface LiveUpdateError {\n /** The AppFlow application ID relating to the failure */\n appId: string;\n /** The step in the sync process the LiveUpdate failed on. (e.g. CHECK, UNPACK)*/\n failStep: string;\n /** A human readable error message */\n message: string;\n}\n\n/** Used for communicating sync results of multiple live updates */\nexport interface SyncResults {\n liveUpdates: LiveUpdate[];\n errors: LiveUpdateError[];\n}\n\n/**\n * Configures LiveUpdates to cyrptographically verify the contents of the downloaded bundles.\n * This method must be called before any LiveUpdates are registered otherwise they will no longer be tracked.\n *\n * @param pathToKey The *relative* path to the public key for verification.\n * This path should be the same relatibe to the main application bundle on iOS and the assets directory on Android.\n * @returns Promise<void>\n */\nexport const enableSecureLiveUpdates = async (\n pathToKey: string\n): Promise<void> => {\n return IONPortalsReactNative.enableSecureLiveUpdates(pathToKey);\n};\n\n/**\n * Syncs a single live update.\n *\n * @param appId The AppFlow application ID to sync.\n * @returns A Promise<LiveUpdate>. A failure should result in a {@link LiveUpdateError}.\n */\nexport const syncOne = async (appId: string): Promise<LiveUpdate> => {\n return IONPortalsReactNative.syncOne(appId);\n};\n\n/**\n * Syncs many live updates.\n *\n * @param appIds The AppFlow application IDs to sync.\n * @returns Promise<SyncResults>\n */\nexport const syncSome = async (appIds: string[]): Promise<SyncResults> => {\n return IONPortalsReactNative.syncSome(appIds);\n};\n\n/**\n * Syncs all registered LiveUpdates\n * @returns Promise<SyncResults>\n */\nexport const syncAll = async (): Promise<SyncResults> => {\n return IONPortalsReactNative.syncAll();\n};\n"],"mappings":"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"}
1
+ {"version":3,"names":["NativeEventEmitter","NativeModules","Platform","IONPortalPubSub","IONPortalsReactNative","IONPortalsWebVitals","default","PortalView","PortalsPubSub","subscriptionMap","Map","subscribe","topic","onMessageReceived","subscriptionRef","subscriber","addListener","message","set","webVitalsMap","WebVitals","onFirstContentfulPaint","portalName","callback","listener","event","duration","registerOnFirstContentfulPaint","onFirstInputDelay","OS","registerOnFirstInputDelay","onTimeToFirstByte","registerOnTimeToFirstByte","registerWebVitals","firstContentfulPaint","firstInputDelay","timeToFirstByte","unsubscribe","subRef","subscription","get","undefined","remove","delete","publish","data","msg","register","key","addPortal","portal","addPortals","portals","getPortal","name","enableSecureLiveUpdates","pathToKey","syncOne","appId","syncSome","appIds","syncAll"],"sources":["index.ts"],"sourcesContent":["import {\n EmitterSubscription,\n NativeEventEmitter,\n NativeModules,\n Platform,\n ViewProps,\n} from 'react-native';\n\nconst { IONPortalPubSub, IONPortalsReactNative, IONPortalsWebVitals } =\n NativeModules;\n\nexport { default as PortalView } from './PortalView';\n\n/**\n * The data that is received from a subscription event.\n */\nexport interface Message {\n /** The unique subscription reference received from {@link subscribe}*/\n subscriptionRef: number;\n data: any;\n /** The topic the message was sent from */\n topic: string;\n}\n\nconst PortalsPubSub = new NativeEventEmitter(IONPortalPubSub);\n\nconst subscriptionMap = new Map<number, EmitterSubscription>();\n\n/**\n * Subscribes to messages for a topic\n *\n * @param topic The topic to subscribe to\n * @param onMessageReceived The callback to invoke when a message is received\n * @returns A Promise<number> containing the unique subscription reference. This will need to be stored for calling {@link unsubscribe}.\n */\nexport const subscribe = async (\n topic: string,\n onMessageReceived: (message: Message) => void\n): Promise<number> => {\n const subscriptionRef = await IONPortalPubSub.subscribe(topic);\n\n const subscriber = PortalsPubSub.addListener(\n 'PortalsSubscription',\n (message: Message) => {\n if (message.subscriptionRef === subscriptionRef) {\n onMessageReceived(message);\n }\n }\n );\n\n subscriptionMap.set(subscriptionRef, subscriber);\n\n return subscriptionRef;\n};\n\nconst webVitalsMap = new Map<string, EmitterSubscription>();\nconst WebVitals = new NativeEventEmitter(IONPortalsWebVitals);\n\ninterface WebVitalsEvent {\n portalName: string;\n duration: number;\n}\n\nexport const onFirstContentfulPaint = async (\n portalName: string,\n callback: (duration: number) => void\n): Promise<void> => {\n const listener = WebVitals.addListener(\n 'vitals:fcp',\n (event: WebVitalsEvent) => {\n if (event.portalName === portalName) {\n callback(event.duration);\n }\n }\n );\n\n await IONPortalsWebVitals.registerOnFirstContentfulPaint(portalName);\n\n webVitalsMap.set(`${portalName}-vitals:fcp`, listener);\n};\n\nexport const onFirstInputDelay = async (\n portalName: string,\n callback: (duration: number) => void\n) => {\n if (Platform.OS === 'android') {\n const listener = WebVitals.addListener(\n 'vitals:fid',\n (event: WebVitalsEvent) => {\n if (event.portalName === portalName) {\n callback(event.duration);\n }\n }\n );\n\n await IONPortalsWebVitals.registerOnFirstInputDelay(portalName);\n\n webVitalsMap.set(`${portalName}-vitals:fcp`, listener);\n }\n};\n\nexport const onTimeToFirstByte = async (\n portalName: string,\n callback: (duration: number) => void\n) => {\n if (Platform.OS === 'android') {\n const listener = WebVitals.addListener(\n 'vitals:ttfb',\n (event: WebVitalsEvent) => {\n if (event.portalName === portalName) {\n callback(event.duration);\n }\n }\n );\n\n await IONPortalsWebVitals.registerOnTimeToFirstByte(portalName);\n\n webVitalsMap.set(`${portalName}-vitals:ttfb`, listener);\n }\n};\n\nexport const registerWebVitals = async (\n portalName: string,\n firstContentfulPaint: (duration: number) => void,\n firstInputDelay: (duration: number) => void,\n timeToFirstByte: (duration: number) => void\n) => {\n onFirstContentfulPaint(portalName, firstContentfulPaint);\n onFirstInputDelay(portalName, firstInputDelay);\n onTimeToFirstByte(portalName, timeToFirstByte);\n};\n\n/**\n * Unsubscribes from events for the provided topic and subscription reference\n *\n * @param topic The topic to unsubscribe from\n * @param subRef The unique subscription reference received when initially calling {@link subscribe}\n */\nexport const unsubscribe = (topic: string, subRef: number) => {\n IONPortalPubSub.unsubscribe(topic, subRef);\n\n const subscription = subscriptionMap.get(subRef);\n if (subscription !== undefined) {\n subscription.remove();\n subscriptionMap.delete(subRef);\n }\n};\n\n/**\n * Publishes a message to the provided topic\n *\n * @param topic The topic to publish the message to\n * @param data The data to publish to subscribers\n */\nexport const publish = (topic: string, data: any) => {\n const msg = { message: data };\n IONPortalPubSub.publish(topic, msg);\n};\n\n/**\n * Validates that a valid registration key has been procured from http://ionic.io/register-portals\n * @param key The registration key\n * @returns Promise<void>\n */\nexport const register = async (key: string): Promise<void> => {\n return IONPortalsReactNative.register(key);\n};\n\n/**\n * The configuration of a web application to be embedded in a React Native application.\n */\nexport interface Portal {\n /** The name of the Portal to be referenced. Must be **unique** */\n name: string;\n /** Any Capacitor plugins to be made available to the Portal */\n plugins?: CapacitorPlugin[];\n /**\n * The root directory of the web application relative to Bundle.main on iOS\n * and src/main/assets on Android. If omitted, `name` is used.\n */\n startDir?: string;\n /** The name of the initial file to load. If omitted, 'index.html' is used. */\n index?: string;\n /** Any data needed at initial render when a portal is loaded. */\n initialContext?: {\n [key: string]: any;\n };\n assetMaps?: AssetMap[];\n liveUpdate?: LiveUpdateConfig;\n}\n\nexport interface CapacitorPlugin {\n /** The classpath of the plugin to be used in Android. (e.g. com.capacitorjs.plugins.camera.CameraPlugin) */\n androidClassPath: string;\n /** The class name of the plugin to be used in iOS.\n * This must be the name as it is exposed to the Objective-C runtime.\n * For example, The CameraPlugin swift class is exposed to Objective-C as CAPCameraPlugin.\n */\n iosClassName: string;\n}\n\nexport interface AssetMap {\n /** The name to index the asset map by */\n name: string;\n /** Any path to match via the web. If omitted, {@link AssetMap#name} will be used. */\n virtualPath?: string;\n /** The root directory of the assets relative to Bundle.main on iOS\n * and src/main/assets on Android. If omitted, the root of Bundle.main\n * and src/main/assets will be used.\n */\n startDir?: string;\n}\n\n/**\n * A subset of {@link Portal} properties needed for rendering a Portal. `initialContext` can be used to override\n * any initialContext defined in the original {@link Portal} definition.\n */\nexport type PortalProp = {\n portal: Pick<Portal, 'name' | 'initialContext'>;\n};\n\n/**\n * Props needed for rendering a {@link Portal}\n */\nexport type PortalProps = PortalProp & ViewProps;\n\n/**\n * Adds a Portal to an internal registry. Must be called before attempting to render a {@link PortalView}.\n *\n * @param portal The portal to add to the internal registry.\n * @returns Promise containing the Portal that was added to the registry.\n */\nexport const addPortal = async (portal: Portal): Promise<Portal> => {\n return IONPortalsReactNative.addPortal(portal);\n};\n\n/**\n * Adds all portals to an internal registry. This or {@link addPortal} must be called before attempting to render a {@link PortalView}\n *\n * @param portals The portals to add to the internal registry.\n * @returns Promise containing the Portals that were added to the registry.\n */\nexport const addPortals = async (portals: Portal[]): Promise<Portal[]> => {\n return IONPortalsReactNative.addPortals(portals);\n};\n\n/**\n * Gets a {@link Portal} previously registered via {@link addPortal} or {@link addPortals}.\n *\n * @param name The portal name to retrieve from the internal registry.\n * @returns Promise containing the registered {@link Portal}. If the {@link Portal} was not registered, the Promise will fail.\n */\nexport const getPortal = async (name: string): Promise<Portal> => {\n return IONPortalsReactNative.getPortal(name);\n};\n\nexport interface LiveUpdate {\n /** The AppFlow application ID */\n appId: string;\n /** The AppFlow distribution channel */\n channel: string;\n}\n\n/** Data needed to register a live update to be managed */\nexport type LiveUpdateConfig = LiveUpdate & { syncOnAdd: boolean };\n\nexport interface LiveUpdateError {\n /** The AppFlow application ID relating to the failure */\n appId: string;\n /** The step in the sync process the LiveUpdate failed on. (e.g. CHECK, UNPACK)*/\n failStep: string;\n /** A human readable error message */\n message: string;\n}\n\nexport interface Snapshot {\n /** The snapshot id as found in AppFlow */\n id: string;\n /** The AppFlow build id that produced the snapshot */\n buildId: string;\n}\n\nexport interface SyncResult {\n /** The {@link LiveUpdate} associated with the result */\n liveUpdate: LiveUpdate;\n /** The {@link Snapshot} that was sync'd */\n snapshot: Snapshot | null;\n /** Whether the snapshot was downloaded or already on disk */\n source: 'download' | 'cache';\n /** If the active application path was changed. A `false` value would indicate\n * the application already has the latest code for the associated {@link LiveUpdate}\n * configuration.\n */\n activeApplicationPathChanged: boolean;\n}\n\n/** Used for communicating sync results of multiple live updates */\nexport interface SyncResults {\n results: SyncResult[];\n errors: LiveUpdateError[];\n}\n\n/**\n * Configures LiveUpdates to cyrptographically verify the contents of the downloaded bundles.\n * This method must be called before any LiveUpdates are registered otherwise they will no longer be tracked.\n *\n * @param pathToKey The *relative* path to the public key for verification.\n * This path should be the same relatibe to the main application bundle on iOS and the assets directory on Android.\n * @returns Promise<void>\n */\nexport const enableSecureLiveUpdates = async (\n pathToKey: string\n): Promise<void> => {\n return IONPortalsReactNative.enableSecureLiveUpdates(pathToKey);\n};\n\n/**\n * Syncs a single live update.\n *\n * @param appId The AppFlow application ID to sync.\n * @returns A Promise<LiveUpdate>. A failure should result in a {@link LiveUpdateError}.\n */\nexport const syncOne = async (appId: string): Promise<SyncResult> => {\n return IONPortalsReactNative.syncOne(appId);\n};\n\n/**\n * Syncs many live updates.\n *\n * @param appIds The AppFlow application IDs to sync.\n * @returns Promise<SyncResults>\n */\nexport const syncSome = async (appIds: string[]): Promise<SyncResults> => {\n return IONPortalsReactNative.syncSome(appIds);\n};\n\n/**\n * Syncs all registered LiveUpdates\n * @returns Promise<SyncResults>\n */\nexport const syncAll = async (): Promise<SyncResults> => {\n return IONPortalsReactNative.syncAll();\n};\n"],"mappings":"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"}
@@ -18,6 +18,10 @@ export interface Message {
18
18
  * @returns A Promise<number> containing the unique subscription reference. This will need to be stored for calling {@link unsubscribe}.
19
19
  */
20
20
  export declare const subscribe: (topic: string, onMessageReceived: (message: Message) => void) => Promise<number>;
21
+ export declare const onFirstContentfulPaint: (portalName: string, callback: (duration: number) => void) => Promise<void>;
22
+ export declare const onFirstInputDelay: (portalName: string, callback: (duration: number) => void) => Promise<void>;
23
+ export declare const onTimeToFirstByte: (portalName: string, callback: (duration: number) => void) => Promise<void>;
24
+ export declare const registerWebVitals: (portalName: string, firstContentfulPaint: (duration: number) => void, firstInputDelay: (duration: number) => void, timeToFirstByte: (duration: number) => void) => Promise<void>;
21
25
  /**
22
26
  * Unsubscribes from events for the provided topic and subscription reference
23
27
  *
@@ -44,8 +48,8 @@ export declare const register: (key: string) => Promise<void>;
44
48
  export interface Portal {
45
49
  /** The name of the Portal to be referenced. Must be **unique** */
46
50
  name: string;
47
- /** The classpath of all Capacitor plugins used in Android. (e.g. com.capacitorjs.plugins.camera.CameraPlugin) */
48
- androidPlugins?: string[];
51
+ /** Any Capacitor plugins to be made available to the Portal */
52
+ plugins?: CapacitorPlugin[];
49
53
  /**
50
54
  * The root directory of the web application relative to Bundle.main on iOS
51
55
  * and src/main/assets on Android. If omitted, `name` is used.
@@ -57,19 +61,40 @@ export interface Portal {
57
61
  initialContext?: {
58
62
  [key: string]: any;
59
63
  };
64
+ assetMaps?: AssetMap[];
60
65
  liveUpdate?: LiveUpdateConfig;
61
66
  }
67
+ export interface CapacitorPlugin {
68
+ /** The classpath of the plugin to be used in Android. (e.g. com.capacitorjs.plugins.camera.CameraPlugin) */
69
+ androidClassPath: string;
70
+ /** The class name of the plugin to be used in iOS.
71
+ * This must be the name as it is exposed to the Objective-C runtime.
72
+ * For example, The CameraPlugin swift class is exposed to Objective-C as CAPCameraPlugin.
73
+ */
74
+ iosClassName: string;
75
+ }
76
+ export interface AssetMap {
77
+ /** The name to index the asset map by */
78
+ name: string;
79
+ /** Any path to match via the web. If omitted, {@link AssetMap#name} will be used. */
80
+ virtualPath?: string;
81
+ /** The root directory of the assets relative to Bundle.main on iOS
82
+ * and src/main/assets on Android. If omitted, the root of Bundle.main
83
+ * and src/main/assets will be used.
84
+ */
85
+ startDir?: string;
86
+ }
62
87
  /**
63
88
  * A subset of {@link Portal} properties needed for rendering a Portal. `initialContext` can be used to override
64
89
  * any initialContext defined in the original {@link Portal} definition.
65
90
  */
66
- export declare type PortalProp = {
91
+ export type PortalProp = {
67
92
  portal: Pick<Portal, 'name' | 'initialContext'>;
68
93
  };
69
94
  /**
70
95
  * Props needed for rendering a {@link Portal}
71
96
  */
72
- export declare type PortalProps = PortalProp & ViewProps;
97
+ export type PortalProps = PortalProp & ViewProps;
73
98
  /**
74
99
  * Adds a Portal to an internal registry. Must be called before attempting to render a {@link PortalView}.
75
100
  *
@@ -98,7 +123,7 @@ export interface LiveUpdate {
98
123
  channel: string;
99
124
  }
100
125
  /** Data needed to register a live update to be managed */
101
- export declare type LiveUpdateConfig = LiveUpdate & {
126
+ export type LiveUpdateConfig = LiveUpdate & {
102
127
  syncOnAdd: boolean;
103
128
  };
104
129
  export interface LiveUpdateError {
@@ -109,9 +134,28 @@ export interface LiveUpdateError {
109
134
  /** A human readable error message */
110
135
  message: string;
111
136
  }
137
+ export interface Snapshot {
138
+ /** The snapshot id as found in AppFlow */
139
+ id: string;
140
+ /** The AppFlow build id that produced the snapshot */
141
+ buildId: string;
142
+ }
143
+ export interface SyncResult {
144
+ /** The {@link LiveUpdate} associated with the result */
145
+ liveUpdate: LiveUpdate;
146
+ /** The {@link Snapshot} that was sync'd */
147
+ snapshot: Snapshot | null;
148
+ /** Whether the snapshot was downloaded or already on disk */
149
+ source: 'download' | 'cache';
150
+ /** If the active application path was changed. A `false` value would indicate
151
+ * the application already has the latest code for the associated {@link LiveUpdate}
152
+ * configuration.
153
+ */
154
+ activeApplicationPathChanged: boolean;
155
+ }
112
156
  /** Used for communicating sync results of multiple live updates */
113
157
  export interface SyncResults {
114
- liveUpdates: LiveUpdate[];
158
+ results: SyncResult[];
115
159
  errors: LiveUpdateError[];
116
160
  }
117
161
  /**
@@ -129,7 +173,7 @@ export declare const enableSecureLiveUpdates: (pathToKey: string) => Promise<voi
129
173
  * @param appId The AppFlow application ID to sync.
130
174
  * @returns A Promise<LiveUpdate>. A failure should result in a {@link LiveUpdateError}.
131
175
  */
132
- export declare const syncOne: (appId: string) => Promise<LiveUpdate>;
176
+ export declare const syncOne: (appId: string) => Promise<SyncResult>;
133
177
  /**
134
178
  * Syncs many live updates.
135
179
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ionic/portals-react-native",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Ionic Portals for React Native",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
package/src/index.ts CHANGED
@@ -2,10 +2,12 @@ import {
2
2
  EmitterSubscription,
3
3
  NativeEventEmitter,
4
4
  NativeModules,
5
+ Platform,
5
6
  ViewProps,
6
7
  } from 'react-native';
7
8
 
8
- const { IONPortalPubSub, IONPortalsReactNative } = NativeModules;
9
+ const { IONPortalPubSub, IONPortalsReactNative, IONPortalsWebVitals } =
10
+ NativeModules;
9
11
 
10
12
  export { default as PortalView } from './PortalView';
11
13
 
@@ -51,6 +53,83 @@ export const subscribe = async (
51
53
  return subscriptionRef;
52
54
  };
53
55
 
56
+ const webVitalsMap = new Map<string, EmitterSubscription>();
57
+ const WebVitals = new NativeEventEmitter(IONPortalsWebVitals);
58
+
59
+ interface WebVitalsEvent {
60
+ portalName: string;
61
+ duration: number;
62
+ }
63
+
64
+ export const onFirstContentfulPaint = async (
65
+ portalName: string,
66
+ callback: (duration: number) => void
67
+ ): Promise<void> => {
68
+ const listener = WebVitals.addListener(
69
+ 'vitals:fcp',
70
+ (event: WebVitalsEvent) => {
71
+ if (event.portalName === portalName) {
72
+ callback(event.duration);
73
+ }
74
+ }
75
+ );
76
+
77
+ await IONPortalsWebVitals.registerOnFirstContentfulPaint(portalName);
78
+
79
+ webVitalsMap.set(`${portalName}-vitals:fcp`, listener);
80
+ };
81
+
82
+ export const onFirstInputDelay = async (
83
+ portalName: string,
84
+ callback: (duration: number) => void
85
+ ) => {
86
+ if (Platform.OS === 'android') {
87
+ const listener = WebVitals.addListener(
88
+ 'vitals:fid',
89
+ (event: WebVitalsEvent) => {
90
+ if (event.portalName === portalName) {
91
+ callback(event.duration);
92
+ }
93
+ }
94
+ );
95
+
96
+ await IONPortalsWebVitals.registerOnFirstInputDelay(portalName);
97
+
98
+ webVitalsMap.set(`${portalName}-vitals:fcp`, listener);
99
+ }
100
+ };
101
+
102
+ export const onTimeToFirstByte = async (
103
+ portalName: string,
104
+ callback: (duration: number) => void
105
+ ) => {
106
+ if (Platform.OS === 'android') {
107
+ const listener = WebVitals.addListener(
108
+ 'vitals:ttfb',
109
+ (event: WebVitalsEvent) => {
110
+ if (event.portalName === portalName) {
111
+ callback(event.duration);
112
+ }
113
+ }
114
+ );
115
+
116
+ await IONPortalsWebVitals.registerOnTimeToFirstByte(portalName);
117
+
118
+ webVitalsMap.set(`${portalName}-vitals:ttfb`, listener);
119
+ }
120
+ };
121
+
122
+ export const registerWebVitals = async (
123
+ portalName: string,
124
+ firstContentfulPaint: (duration: number) => void,
125
+ firstInputDelay: (duration: number) => void,
126
+ timeToFirstByte: (duration: number) => void
127
+ ) => {
128
+ onFirstContentfulPaint(portalName, firstContentfulPaint);
129
+ onFirstInputDelay(portalName, firstInputDelay);
130
+ onTimeToFirstByte(portalName, timeToFirstByte);
131
+ };
132
+
54
133
  /**
55
134
  * Unsubscribes from events for the provided topic and subscription reference
56
135
  *
@@ -93,8 +172,8 @@ export const register = async (key: string): Promise<void> => {
93
172
  export interface Portal {
94
173
  /** The name of the Portal to be referenced. Must be **unique** */
95
174
  name: string;
96
- /** The classpath of all Capacitor plugins used in Android. (e.g. com.capacitorjs.plugins.camera.CameraPlugin) */
97
- androidPlugins?: string[];
175
+ /** Any Capacitor plugins to be made available to the Portal */
176
+ plugins?: CapacitorPlugin[];
98
177
  /**
99
178
  * The root directory of the web application relative to Bundle.main on iOS
100
179
  * and src/main/assets on Android. If omitted, `name` is used.
@@ -106,9 +185,32 @@ export interface Portal {
106
185
  initialContext?: {
107
186
  [key: string]: any;
108
187
  };
188
+ assetMaps?: AssetMap[];
109
189
  liveUpdate?: LiveUpdateConfig;
110
190
  }
111
191
 
192
+ export interface CapacitorPlugin {
193
+ /** The classpath of the plugin to be used in Android. (e.g. com.capacitorjs.plugins.camera.CameraPlugin) */
194
+ androidClassPath: string;
195
+ /** The class name of the plugin to be used in iOS.
196
+ * This must be the name as it is exposed to the Objective-C runtime.
197
+ * For example, The CameraPlugin swift class is exposed to Objective-C as CAPCameraPlugin.
198
+ */
199
+ iosClassName: string;
200
+ }
201
+
202
+ export interface AssetMap {
203
+ /** The name to index the asset map by */
204
+ name: string;
205
+ /** Any path to match via the web. If omitted, {@link AssetMap#name} will be used. */
206
+ virtualPath?: string;
207
+ /** The root directory of the assets relative to Bundle.main on iOS
208
+ * and src/main/assets on Android. If omitted, the root of Bundle.main
209
+ * and src/main/assets will be used.
210
+ */
211
+ startDir?: string;
212
+ }
213
+
112
214
  /**
113
215
  * A subset of {@link Portal} properties needed for rendering a Portal. `initialContext` can be used to override
114
216
  * any initialContext defined in the original {@link Portal} definition.
@@ -171,9 +273,30 @@ export interface LiveUpdateError {
171
273
  message: string;
172
274
  }
173
275
 
276
+ export interface Snapshot {
277
+ /** The snapshot id as found in AppFlow */
278
+ id: string;
279
+ /** The AppFlow build id that produced the snapshot */
280
+ buildId: string;
281
+ }
282
+
283
+ export interface SyncResult {
284
+ /** The {@link LiveUpdate} associated with the result */
285
+ liveUpdate: LiveUpdate;
286
+ /** The {@link Snapshot} that was sync'd */
287
+ snapshot: Snapshot | null;
288
+ /** Whether the snapshot was downloaded or already on disk */
289
+ source: 'download' | 'cache';
290
+ /** If the active application path was changed. A `false` value would indicate
291
+ * the application already has the latest code for the associated {@link LiveUpdate}
292
+ * configuration.
293
+ */
294
+ activeApplicationPathChanged: boolean;
295
+ }
296
+
174
297
  /** Used for communicating sync results of multiple live updates */
175
298
  export interface SyncResults {
176
- liveUpdates: LiveUpdate[];
299
+ results: SyncResult[];
177
300
  errors: LiveUpdateError[];
178
301
  }
179
302
 
@@ -197,7 +320,7 @@ export const enableSecureLiveUpdates = async (
197
320
  * @param appId The AppFlow application ID to sync.
198
321
  * @returns A Promise<LiveUpdate>. A failure should result in a {@link LiveUpdateError}.
199
322
  */
200
- export const syncOne = async (appId: string): Promise<LiveUpdate> => {
323
+ export const syncOne = async (appId: string): Promise<SyncResult> => {
201
324
  return IONPortalsReactNative.syncOne(appId);
202
325
  };
203
326
 
@@ -1,35 +0,0 @@
1
- //
2
- // Portal+Dict.swift
3
- // ReactNativePortals
4
- //
5
- // Created by Steven Sherry on 10/5/22.
6
- // Copyright © 2022 Ionic. All rights reserved.
7
- //
8
-
9
- import Capacitor
10
- import IonicLiveUpdates
11
- import IonicPortals
12
-
13
- extension Portal {
14
- init?(_ dict: [String: Any], _ liveUpdateManager: LiveUpdateManager) {
15
- guard let name = dict["name"] as? String else { return nil }
16
- self.init(
17
- name: name,
18
- startDir: dict["startDir"] as? String,
19
- index: dict["index"] as? String ?? "index.html",
20
- initialContext: JSTypes.coerceDictionaryToJSObject(dict["initialContext"] as? [String: Any]) ?? [:],
21
- liveUpdateManager: liveUpdateManager,
22
- liveUpdateConfig: (dict["liveUpdate"] as? [String: Any]).flatMap(LiveUpdate.init)
23
- )
24
- }
25
-
26
- var dict: [String: Any] {
27
- return [
28
- "name": name,
29
- "startDir": startDir,
30
- "index": index,
31
- "initialContext": initialContext,
32
- "liveUpdateConfig": liveUpdateConfig?.dict as Any
33
- ]
34
- }
35
- }