diff --git a/android/src/main/java/com/iterable/reactnative/RNIterableAPIModule.java b/android/src/main/java/com/iterable/reactnative/RNIterableAPIModule.java index 9b0bed33a..bf9ad4cfc 100644 --- a/android/src/main/java/com/iterable/reactnative/RNIterableAPIModule.java +++ b/android/src/main/java/com/iterable/reactnative/RNIterableAPIModule.java @@ -482,6 +482,36 @@ public void updateVisibleRows(ReadableArray visibleRows) { // --------------------------------------------------------------------------------------- // endregion + // --------------------------------------------------------------------------------------- + // region Embedded APIs + + @ReactMethod + public void getEmbeddedPlacements(Promise promise) { + IterableLogger.d(TAG, "getEmbeddedPlacements"); + + JSONArray testPlacements = new JSONArray(); + int[] testPlacementIds = {808, 1121, 112}; + + try { + for (int placementId : testPlacementIds) { + testPlacements.put(createTestPlacement(placementId)); + } + + promise.resolve(Serialization.convertJsonToArray(testPlacements)); + } catch (JSONException e) { + promise.reject("", "Failed to create test placements"); + } + } + + private JSONObject createTestPlacement(int placementId) throws JSONException { + JSONObject placement = new JSONObject(); + placement.put("placementId", placementId); + return placement; + } + + // --------------------------------------------------------------------------------------- + // endregion + // --------------------------------------------------------------------------------------- // region Private Serialization Functions diff --git a/ios/RNIterableAPI/RNIterableAPI.m b/ios/RNIterableAPI/RNIterableAPI.m index 8850c755a..68c90b76c 100644 --- a/ios/RNIterableAPI/RNIterableAPI.m +++ b/ios/RNIterableAPI/RNIterableAPI.m @@ -133,6 +133,11 @@ @interface RCT_EXTERN_REMAP_MODULE(RNIterableAPI, ReactIterableAPI, NSObject) RCT_EXTERN_METHOD(updateVisibleRows: (nonnull NSArray *) visibleRows) +// MARK: - SDK Embedded Manager Functions + +RCT_EXTERN_METHOD(getEmbeddedPlacements: (RCTPromiseResolveBlock) resolve + rejecter: (RCTPromiseRejectBlock) reject) + // MARK: - SDK Auth Manager Functions RCT_EXTERN_METHOD(passAlongAuthToken: (NSString *) authToken) diff --git a/ios/RNIterableAPI/ReactIterableAPI.swift b/ios/RNIterableAPI/ReactIterableAPI.swift index 4db314f20..7c15becdc 100644 --- a/ios/RNIterableAPI/ReactIterableAPI.swift +++ b/ios/RNIterableAPI/ReactIterableAPI.swift @@ -470,6 +470,22 @@ class ReactIterableAPI: RCTEventEmitter { inboxSessionManager.updateVisibleRows(visibleRows: serializedRows) } + + // MARK: - SDK Embedded Manager Functions + + @objc(getEmbeddedPlacements:rejecter:) + func getEmbeddedPlacements(resolver: @escaping RCTPromiseResolveBlock, + rejecter: @escaping RCTPromiseRejectBlock) { + ITBInfo() + + // Create test data + let testPlacements: [[String: Any]] = [ + ["placementId": "meow"], + ["placementId": "woof woof"] + ] + + resolver(testPlacements) + } // MARK: - SDK Auth Manager Functions @@ -481,7 +497,7 @@ class ReactIterableAPI: RCTEventEmitter { authHandlerSemaphore.signal() } - + // MARK: Private private var shouldEmit = false private let _methodQueue = DispatchQueue(label: String(describing: ReactIterableAPI.self)) diff --git a/package.json b/package.json index fb42dc80b..6098258ae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@iterable/react-native-sdk", - "version": "2.0.0-beta.1", + "version": "2.0.0-beta.1-rc.19", "description": "Iterable SDK for React Native.", "source": "./src/index.tsx", "main": "./lib/module/index.js", diff --git a/src/__tests__/IterableEmbeddedMessageMetadata.test.ts b/src/__tests__/IterableEmbeddedMessageMetadata.test.ts new file mode 100644 index 000000000..f57786a9d --- /dev/null +++ b/src/__tests__/IterableEmbeddedMessageMetadata.test.ts @@ -0,0 +1,58 @@ +import { IterableEmbeddedMessageMetadata } from '../embedded/classes/IterableEmbeddedMessageMetadata'; +import { Iterable } from '../core'; + +describe('IterableEmbeddedMessage', () => { + test('should create an instance of IterableEmbeddedMessageMetadata from a dictionary', () => { + Iterable.logger.log( + 'iterableEmbeddedMessageMetadata_fromDict_valid_dictionary' + ); + + const dict = { + messageId: '123', + placementId: 456, + campaignId: 789, + isProof: false, + }; + + const result = IterableEmbeddedMessageMetadata.fromDict(dict); + + expect(result).toBeInstanceOf(IterableEmbeddedMessageMetadata); + expect(result.messageId).toBe('123'); + expect(result.placementId).toBe(456); + expect(result.campaignId).toBe(789); + expect(result.isProof).toBe(false); + }); + + test('should handle optional fields', () => { + Iterable.logger.log( + 'iterableEmbeddedMessageMetadata_fromDict_optional_fields_omitted' + ); + + const dict = { + messageId: '123', + placementId: 456, + }; + + const result = IterableEmbeddedMessageMetadata.fromDict(dict); + + expect(result).toBeInstanceOf(IterableEmbeddedMessageMetadata); + expect(result.messageId).toBe('123'); + expect(result.placementId).toBe(456); + expect(result.campaignId).toBeUndefined(); + expect(result.isProof).toBe(false); + }); + + test('should throw an error if messageId is not provided', () => { + Iterable.logger.log( + 'iterableEmbeddedMessageMetadata_fromDict_missing_messageId' + ); + + const dict = { + placementId: 456, + }; + + expect(() => { + IterableEmbeddedMessageMetadata.fromDict(dict); + }).toThrow('messageId and placementId are required'); + }); +}); diff --git a/src/embedded/classes/IterableEmbeddedManager.ts b/src/embedded/classes/IterableEmbeddedManager.ts new file mode 100644 index 000000000..e570d7500 --- /dev/null +++ b/src/embedded/classes/IterableEmbeddedManager.ts @@ -0,0 +1,24 @@ +import { NativeModules } from 'react-native'; + +import { Iterable } from '../../core/classes/Iterable'; +import { IterableEmbeddedPlacement } from './IterableEmbeddedPlacement'; + +const RNIterableAPI = NativeModules.RNIterableAPI; + +/** + * Manages embedded messages for the current user. + * + * This class provides methods to interact with embedded messages, including retrieving placements. + */ +export class IterableEmbeddedManager { + /** + * Retrieve the current user's list of embedded placements. + * + * @returns A Promise that resolves to an array of embedded placements. + */ + getPlacements(): Promise { + Iterable?.logger?.log('EmbeddedManager.getPlacements'); + + return RNIterableAPI.getEmbeddedPlacements(); + } +} diff --git a/src/embedded/classes/IterableEmbeddedMessageMetadata.ts b/src/embedded/classes/IterableEmbeddedMessageMetadata.ts new file mode 100644 index 000000000..02bc032d0 --- /dev/null +++ b/src/embedded/classes/IterableEmbeddedMessageMetadata.ts @@ -0,0 +1,65 @@ +/** + * Metadata for an embedded message. + */ +export class IterableEmbeddedMessageMetadata { + /** The ID for the embedded message */ + readonly messageId: string; + /** The placement ID for the embedded message */ + readonly placementId: number; + /** The campaign ID for the embedded message */ + readonly campaignId?: number; + /** Whether the embedded message is a proof */ + readonly isProof: boolean; + + /** + * Constructs an instance of IterableEmbeddedMessageMetadata. + * + * @param messageId - The ID for the embedded message. + * @param placementId - The placement ID for the embedded message. + * @param campaignId - The campaign ID for the embedded message. + * @param isProof - Whether the embedded message is a proof. + */ + constructor( + messageId: string, + placementId: number, + campaignId: number | undefined, + isProof: boolean = false + ) { + this.messageId = messageId; + this.placementId = placementId; + this.campaignId = campaignId; + this.isProof = isProof; + } + + /** + * Creates an instance of `IterableEmbeddedMessageMetadata` from a dictionary object. + * + * @param dict - The dictionary objectcontaining the metadata properties. + * This corresponds to the properties in {@link IterableEmbeddedMessageMetadata} + * + * @returns A new instance of `IterableEmbeddedMessageMetadata` with the provided properties. + */ + static fromDict( + dict: Partial + ): IterableEmbeddedMessageMetadata { + if (!dict.messageId || !dict.placementId) { + throw new Error('messageId and placementId are required'); + } + return new IterableEmbeddedMessageMetadata( + dict.messageId, + dict.placementId, + dict.campaignId, + dict.isProof + ); + } +} + +/** + * An interface defining the dictionary object containing the metadata properties for an embedded message. + */ +export interface EmbeddedMessageMetadataDict { + messageId: string; + placementId: number; + campaignId?: number; + isProof?: boolean; +} diff --git a/src/embedded/classes/IterableEmbeddedPlacement.ts b/src/embedded/classes/IterableEmbeddedPlacement.ts new file mode 100644 index 000000000..6a02aabfe --- /dev/null +++ b/src/embedded/classes/IterableEmbeddedPlacement.ts @@ -0,0 +1,11 @@ +/** + * Iterable embedded placement + * Contains placement id and the associated embedded messages + */ +export class IterableEmbeddedPlacement { + readonly placementId: number; + + constructor(placementId: number) { + this.placementId = placementId; + } +} diff --git a/src/embedded/classes/index.ts b/src/embedded/classes/index.ts new file mode 100644 index 000000000..9b605f651 --- /dev/null +++ b/src/embedded/classes/index.ts @@ -0,0 +1,2 @@ +export * from './IterableEmbeddedManager'; +export * from './IterableEmbeddedPlacement'; diff --git a/src/embedded/index.ts b/src/embedded/index.ts new file mode 100644 index 000000000..d7d17c691 --- /dev/null +++ b/src/embedded/index.ts @@ -0,0 +1 @@ +export * from './classes'; diff --git a/src/inApp/classes/IterableInAppManager.ts b/src/inApp/classes/IterableInAppManager.ts index 640b99d50..a96782b13 100644 --- a/src/inApp/classes/IterableInAppManager.ts +++ b/src/inApp/classes/IterableInAppManager.ts @@ -77,7 +77,7 @@ export class IterableInAppManager { * }); * ``` * - * @param message - The message to show (an {@link_IterableInAppMessage} object) + * @param message - The message to show (an {@link IterableInAppMessage} object) * @param consume - Whether or not the message should be consumed from the user's message queue after being shown. This should be defaulted to true. * * @returns A Promise that resolves to the URL of the button or link the user tapped to close the in-app message. diff --git a/src/index.tsx b/src/index.tsx index 885cd74bd..9a6da1347 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -25,6 +25,7 @@ export { type IterableDeviceOrientation, } from './core/hooks'; export { type IterableEdgeInsetDetails } from './core/types'; +export { IterableEmbeddedManager, IterableEmbeddedPlacement } from './embedded'; export { IterableHtmlInAppContent, IterableInAppCloseSource, diff --git a/src/itblBuildInfo.ts b/src/itblBuildInfo.ts index a14a1a9c0..cf948921f 100644 --- a/src/itblBuildInfo.ts +++ b/src/itblBuildInfo.ts @@ -3,5 +3,5 @@ * It contains the version of the package */ export const buildInfo = { - version: '2.0.0-beta.1', + version: '2.0.0-beta.1-rc.19', }; diff --git a/yarn.lock b/yarn.lock index cf30f91fc..94b937e20 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,19 +15,19 @@ __metadata: languageName: node linkType: hard -"@ark/schema@npm:0.45.9": - version: 0.45.9 - resolution: "@ark/schema@npm:0.45.9" +"@ark/schema@npm:0.46.0": + version: 0.46.0 + resolution: "@ark/schema@npm:0.46.0" dependencies: - "@ark/util": 0.45.9 - checksum: 8d28c910ef6ae379c61a82db2f7e8160d96eb25fb73a56bda5f9c63cc86abca12552d2bb8cd3dd9aff010f5464f9834e33285eca51ea7da16f2143e050cc901a + "@ark/util": 0.46.0 + checksum: a4e7bc0e1c23009c7702ada7cfcbb1638af76f9721c43f96432844ec8616da6fc8121057fb87b0b80142558cf5c3e6141f40443cf43dd026ada8fd4acd635565 languageName: node linkType: hard -"@ark/util@npm:0.45.9": - version: 0.45.9 - resolution: "@ark/util@npm:0.45.9" - checksum: ddd1fc89c45b61e5d52cb92203990492a5115aea58a8e8bf5ff24e28103fce331593e0c374a086554fb6feb375ddd759c07e751aadc7f3ab3c6138dc3ee362cf +"@ark/util@npm:0.46.0": + version: 0.46.0 + resolution: "@ark/util@npm:0.46.0" + checksum: 0c0ceeb10aa0806860f7a7922586a05cda2945f7f598b414b4ebf268a6b45b00f9a854d6afd6b59df58c733e90d00b230194dd6a180a3a23d0eb64612be1b0e0 languageName: node linkType: hard @@ -365,6 +365,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-plugin-utils@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/helper-plugin-utils@npm:7.27.1" + checksum: 5d715055301badab62bdb2336075a77f8dc8bd290cad2bc1b37ea3bf1b3efc40594d308082229f239deb4d6b5b80b0a73bce000e595ea74416e0339c11037047 + languageName: node + linkType: hard + "@babel/helper-remap-async-to-generator@npm:^7.24.7, @babel/helper-remap-async-to-generator@npm:^7.25.0": version: 7.25.0 resolution: "@babel/helper-remap-async-to-generator@npm:7.25.0" @@ -841,6 +848,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-flow@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-syntax-flow@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7baca3171ed595d04c865b0ce46fca7f21900686df9d7fcd1017036ce78bb5483e33803de810831e68d39cf478953db69f49ae3f3de2e3207bc4ba49a96b6739 + languageName: node + linkType: hard + "@babel/plugin-syntax-import-assertions@npm:^7.24.7": version: 7.25.6 resolution: "@babel/plugin-syntax-import-assertions@npm:7.25.6" @@ -1427,6 +1445,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-flow-strip-types@npm:^7.26.5": + version: 7.27.1 + resolution: "@babel/plugin-transform-flow-strip-types@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + "@babel/plugin-syntax-flow": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 0885028866fadefef35292d5a27f878d6a12b6f83778f8731481d4503b49c258507882a7de2aafda9b62d5f6350042f1a06355b998d5ed5e85d693bfcb77b939 + languageName: node + linkType: hard + "@babel/plugin-transform-for-of@npm:^7.0.0, @babel/plugin-transform-for-of@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-for-of@npm:7.24.7" @@ -5149,12 +5179,12 @@ __metadata: linkType: hard "arktype@npm:^2.1.15": - version: 2.1.19 - resolution: "arktype@npm:2.1.19" + version: 2.1.20 + resolution: "arktype@npm:2.1.20" dependencies: - "@ark/schema": 0.45.9 - "@ark/util": 0.45.9 - checksum: cf656f9aa3797d56572d49a8499a4156fbbe25eacbd075f4c60770876fd1fbde6b6285f1b367de93e14858651f6a1df1db3de99d6a5f642e1fa61f421fae0712 + "@ark/schema": 0.46.0 + "@ark/util": 0.46.0 + checksum: 5c02dda98606b83b35bbc66934259e3f30c4b4486c32e470e199da533c0af568951502173d7d7a5e64a2e53667eb36d10d772ce46c0bff204fab759430614c9b languageName: node linkType: hard @@ -5441,6 +5471,15 @@ __metadata: languageName: node linkType: hard +"babel-plugin-syntax-hermes-parser@npm:^0.28.0": + version: 0.28.1 + resolution: "babel-plugin-syntax-hermes-parser@npm:0.28.1" + dependencies: + hermes-parser: 0.28.1 + checksum: 2cbc921e663463480ead9ccc8bb229a5196032367ba2b5ccb18a44faa3afa84b4dc493297749983b9a837a3d76b0b123664aecc06f9122618c3246f03e076a9d + languageName: node + linkType: hard + "babel-plugin-transform-flow-enums@npm:^0.0.2": version: 0.0.2 resolution: "babel-plugin-transform-flow-enums@npm:0.0.2" @@ -7885,6 +7924,19 @@ __metadata: languageName: node linkType: hard +"fast-glob@npm:^3.3.3": + version: 3.3.3 + resolution: "fast-glob@npm:3.3.3" + dependencies: + "@nodelib/fs.stat": ^2.0.2 + "@nodelib/fs.walk": ^1.2.3 + glob-parent: ^5.1.2 + merge2: ^1.3.0 + micromatch: ^4.0.8 + checksum: 0704d7b85c0305fd2cef37777337dfa26230fdd072dce9fb5c82a4b03156f3ffb8ed3e636033e65d45d2a5805a4e475825369a27404c0307f2db0c8eb3366fbd + languageName: node + linkType: hard + "fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0": version: 2.1.0 resolution: "fast-json-stable-stringify@npm:2.1.0" @@ -8724,6 +8776,13 @@ __metadata: languageName: node linkType: hard +"hermes-estree@npm:0.28.1": + version: 0.28.1 + resolution: "hermes-estree@npm:0.28.1" + checksum: 4f7b4e0491352012a6cb799315a0aae16abdcc894335e901552ee6c64732d0cf06f0913c579036f9f452b7c4ad9bb0b6ab14e510c13bd7e5997385f77633ab00 + languageName: node + linkType: hard + "hermes-parser@npm:0.22.0": version: 0.22.0 resolution: "hermes-parser@npm:0.22.0" @@ -8742,6 +8801,15 @@ __metadata: languageName: node linkType: hard +"hermes-parser@npm:0.28.1": + version: 0.28.1 + resolution: "hermes-parser@npm:0.28.1" + dependencies: + hermes-estree: 0.28.1 + checksum: 0d95280d527e1ad46e8caacd56b24d07e4aec39704de86cf164600f2c4fb00f406dd74a37b2103433ef7ec388a549072da20438e224bd47def21f973c36aab7d + languageName: node + linkType: hard + "hoist-non-react-statics@npm:^3.3.0": version: 3.3.2 resolution: "hoist-non-react-statics@npm:3.3.2" @@ -11235,7 +11303,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.4": +"micromatch@npm:^4.0.4, micromatch@npm:^4.0.8": version: 4.0.8 resolution: "micromatch@npm:4.0.8" dependencies: @@ -12690,17 +12758,17 @@ __metadata: linkType: hard "react-native-builder-bob@npm:^0.40.4": - version: 0.40.4 - resolution: "react-native-builder-bob@npm:0.40.4" + version: 0.40.11 + resolution: "react-native-builder-bob@npm:0.40.11" dependencies: "@babel/core": ^7.25.2 + "@babel/plugin-transform-flow-strip-types": ^7.26.5 "@babel/plugin-transform-strict-mode": ^7.24.7 "@babel/preset-env": ^7.25.2 - "@babel/preset-flow": ^7.24.7 "@babel/preset-react": ^7.24.7 "@babel/preset-typescript": ^7.24.7 arktype: ^2.1.15 - babel-plugin-module-resolver: ^5.0.2 + babel-plugin-syntax-hermes-parser: ^0.28.0 browserslist: ^4.20.4 cross-spawn: ^7.0.3 dedent: ^0.7.0 @@ -12713,11 +12781,12 @@ __metadata: kleur: ^4.1.4 metro-config: ^0.80.9 prompts: ^2.4.2 + react-native-monorepo-config: ^0.1.8 which: ^2.0.2 yargs: ^17.5.1 bin: bob: bin/bob - checksum: 2b3576a5b3afb142400427f51197485fd0a8d7611b2ca6edb610fbcf320279c5951846c3ebe00715904e808f91106367e1f95c4e32f29405a07fb517a87927bb + checksum: a165c284bb9a938238c7fee526399b06d831dc6ce01762db466f79f768708536f34f0c01e01f49fae732bc160d6198a5499200fdb254b317ce04b69e1c814fdc languageName: node linkType: hard @@ -12747,6 +12816,16 @@ __metadata: languageName: node linkType: hard +"react-native-monorepo-config@npm:^0.1.8": + version: 0.1.9 + resolution: "react-native-monorepo-config@npm:0.1.9" + dependencies: + escape-string-regexp: ^5.0.0 + fast-glob: ^3.3.3 + checksum: 6356c362c517c49e17d54ee764c3566ba71491fa0d755618ecf2ca548348668e84fe448c24066645983acbc2bd4c0ed47594f9b3ec9dcc0558c0fd9594d2391e + languageName: node + linkType: hard + "react-native-safe-area-context@npm:^4.11.1": version: 4.11.1 resolution: "react-native-safe-area-context@npm:4.11.1"