@adobe/react-native-aepmessaging 7.1.1 → 7.2.1

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 (46) hide show
  1. package/README.md +61 -2
  2. package/android/src/main/java/com/adobe/marketing/mobile/reactnative/messaging/RCTAEPMessagingConstants.java +19 -0
  3. package/android/src/main/java/com/adobe/marketing/mobile/reactnative/messaging/RCTAEPMessagingModule.java +152 -18
  4. package/android/src/main/java/com/adobe/marketing/mobile/reactnative/messaging/RCTAEPMessagingUtil.java +1 -1
  5. package/dist/Messaging.d.ts +17 -0
  6. package/dist/Messaging.js +34 -7
  7. package/dist/Messaging.js.map +1 -1
  8. package/dist/index.d.ts +5 -4
  9. package/dist/index.js +11 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/models/ContentCard.d.ts +6 -1
  12. package/dist/models/ContentCard.js +9 -0
  13. package/dist/models/ContentCard.js.map +1 -1
  14. package/dist/models/HTMLProposition.d.ts +6 -2
  15. package/dist/models/HTMLProposition.js +9 -0
  16. package/dist/models/HTMLProposition.js.map +1 -1
  17. package/dist/models/JSONProposition.d.ts +12 -0
  18. package/dist/models/{JSONPropositionItem.js → JSONProposition.js} +10 -1
  19. package/dist/models/JSONProposition.js.map +1 -0
  20. package/dist/models/Message.d.ts +14 -0
  21. package/dist/models/Message.js +51 -1
  22. package/dist/models/Message.js.map +1 -1
  23. package/dist/models/MessagingProposition.d.ts +10 -3
  24. package/dist/models/MessagingProposition.js +45 -0
  25. package/dist/models/MessagingProposition.js.map +1 -1
  26. package/dist/models/MessagingPropositionItem.d.ts +3 -2
  27. package/dist/models/PropositionItem.d.ts +83 -0
  28. package/dist/models/PropositionItem.js +78 -0
  29. package/dist/models/PropositionItem.js.map +1 -0
  30. package/ios/src/RCTAEPMessaging.mm +32 -0
  31. package/ios/src/RCTAEPMessaging.swift +150 -45
  32. package/ios/src/RCTAEPMessagingConstants.swift +5 -1
  33. package/ios/src/RCTAEPMessagingDataBridge.swift +19 -0
  34. package/package.json +2 -2
  35. package/src/Messaging.ts +46 -13
  36. package/src/index.ts +13 -5
  37. package/src/models/ContentCard.ts +12 -1
  38. package/src/models/HTMLProposition.ts +15 -6
  39. package/src/models/{JSONPropositionItem.ts → JSONProposition.ts} +15 -6
  40. package/src/models/Message.ts +61 -2
  41. package/src/models/MessagingProposition.ts +45 -3
  42. package/src/models/MessagingPropositionItem.ts +6 -2
  43. package/src/models/PropositionItem.ts +135 -0
  44. package/tutorials/In-App Messaging.md +67 -0
  45. package/dist/models/JSONPropositionItem.d.ts +0 -8
  46. package/dist/models/JSONPropositionItem.js.map +0 -1
@@ -21,6 +21,7 @@ import WebKit
21
21
  @objc(RCTAEPMessaging)
22
22
  public class RCTAEPMessaging: RCTEventEmitter, MessagingDelegate {
23
23
  private var messageCache = [String: Message]()
24
+ private var jsHandlerMessageCache = [String: Message]()
24
25
  private var latestMessage: Message? = nil
25
26
  private let semaphore = DispatchSemaphore(value: 0)
26
27
  private var shouldSaveMessage = false
@@ -64,7 +65,7 @@ public class RCTAEPMessaging: RCTEventEmitter, MessagingDelegate {
64
65
  ) {
65
66
  resolve(self.latestMessage != nil ? RCTAEPMessagingDataBridge.transformToMessage(message: self.latestMessage!) : nil)
66
67
  }
67
-
68
+
68
69
  @objc
69
70
  func getPropositionsForSurfaces(
70
71
  _ surfaces: [String],
@@ -72,16 +73,29 @@ public class RCTAEPMessaging: RCTEventEmitter, MessagingDelegate {
72
73
  withRejecter reject: @escaping RCTPromiseRejectBlock
73
74
  ) {
74
75
  let surfacePaths = surfaces.map { $0.isEmpty ? Surface() : Surface(path: $0) }
75
- Messaging.getPropositionsForSurfaces(surfacePaths) { propositions, error in
76
+ Messaging.getPropositionsForSurfaces(surfacePaths) { [weak self] propositions, error in
77
+ guard let self = self else { return }
76
78
  guard error == nil else {
77
79
  reject("Unable to Retrieve Propositions", nil, nil)
78
80
  return
79
81
  }
80
- if (propositions != nil && propositions!.isEmpty) {
81
- resolve([String: Any]());
82
- return;
82
+ guard let propositions = propositions, !propositions.isEmpty else {
83
+ resolve([String: Any]())
84
+ return
83
85
  }
84
- resolve(RCTAEPMessagingDataBridge.transformPropositionDict(dict: propositions!))
86
+
87
+ // Populate uuid->Proposition map using scopeDetails.activity.id
88
+ for (_, list) in propositions {
89
+ for proposition in list {
90
+ if let pMap = proposition.asDictionary() {
91
+ if let key = RCTAEPMessagingDataBridge.extractActivityId(from: pMap) {
92
+ self.propositionByUuid[key] = proposition
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ resolve(RCTAEPMessagingDataBridge.transformPropositionDict(dict: propositions))
85
99
  }
86
100
  }
87
101
 
@@ -113,92 +127,81 @@ public class RCTAEPMessaging: RCTEventEmitter, MessagingDelegate {
113
127
  ) {
114
128
  let mapped = surfaces.map { Surface(path: $0) }
115
129
  Messaging.updatePropositionsForSurfaces(mapped)
130
+ propositionByUuid.removeAll()
116
131
  resolve(nil)
117
132
  }
118
133
 
119
134
  /// Message Class Methods
120
135
  @objc
121
- func clearMessage(
122
- _ id: String,
123
- withResolver resolve: @escaping RCTPromiseResolveBlock,
124
- withRejecter reject: @escaping RCTPromiseRejectBlock
136
+ func clear(
137
+ _ id: String
125
138
  ) {
126
139
  let msg = messageCache[id]
127
140
  if msg != nil {
128
141
  messageCache.removeValue(forKey: msg!.id)
129
- resolve(nil)
142
+ print("clear: \(id) removed")
143
+ } else {
144
+ print("clear: \(id) not found")
130
145
  }
131
- reject(Constants.CACHE_MISS, nil, nil)
132
146
  }
133
147
 
134
148
  @objc
135
- func dismissMessage(
149
+ func dismiss(
136
150
  _ id: String,
137
- withSuppressAutoTrack suppressAutoTrack: Bool,
138
- withResolver resolve: @escaping RCTPromiseResolveBlock,
139
- withRejecter reject: @escaping RCTPromiseRejectBlock
151
+ suppressAutoTrack: Bool
140
152
  ) {
141
153
  let msg = messageCache[id]
142
154
  if msg != nil {
143
155
  msg!.dismiss(suppressAutoTrack: suppressAutoTrack)
144
- resolve(nil)
145
- return
156
+ print("dismiss: \(id) to \(suppressAutoTrack)")
157
+ } else {
158
+ print("dismiss: \(id) not found")
146
159
  }
147
- reject(Constants.CACHE_MISS, nil, nil)
148
160
  }
149
161
 
150
162
  @objc
151
163
  func setAutoTrack(
152
164
  _ id: String,
153
- withSuppressAutoTrack suppressAutoTrack: Bool,
154
- withResolver resolve: @escaping RCTPromiseResolveBlock,
155
- withRejecter reject: @escaping RCTPromiseRejectBlock
165
+ autoTrack: Bool
156
166
  ) {
157
-
158
167
  let msg = messageCache[id]
159
- if msg != nil {
160
- msg!.autoTrack = suppressAutoTrack
161
- resolve(nil)
162
- return
168
+ if (msg != nil) {
169
+ msg!.autoTrack = autoTrack
170
+ print("setAutoTrack: \(id) to \(autoTrack)")
171
+ } else {
172
+ print("setAutoTrack: \(id) not found")
163
173
  }
164
- reject(Constants.CACHE_MISS, nil, nil)
165
174
  }
166
175
 
167
176
  @objc
168
- private func showMessage(
169
- _ id: String,
170
- withResolver resolve: @escaping RCTPromiseResolveBlock,
171
- withRejecter reject: @escaping RCTPromiseRejectBlock
177
+ private func show(
178
+ _ id: String
172
179
  ) {
173
180
  let msg = messageCache[id]
174
181
  if msg != nil {
175
182
  msg!.show()
176
- resolve(nil)
177
- return
183
+ print("show: \(id) shown")
184
+ } else {
185
+ print("show: \(id) not found")
178
186
  }
179
- reject(Constants.CACHE_MISS, nil, nil)
180
-
181
187
  }
182
188
 
183
189
  @objc
184
- func trackMessage(
190
+ func track(
185
191
  _ id: String,
186
- withInteraction interaction: String,
187
- withEventType eventType: Int,
188
- withResolver resolve: @escaping RCTPromiseResolveBlock,
189
- withRejecter reject: @escaping RCTPromiseRejectBlock
192
+ interaction: String,
193
+ eventType: Int
190
194
  ) {
191
-
192
195
  let msg = messageCache[id]
193
196
  let eventType =
194
197
  MessagingEdgeEventType.init(rawValue: eventType)
195
198
  ?? MessagingEdgeEventType.dismiss
196
199
  if msg != nil {
197
200
  msg!.track(interaction, withEdgeEventType: eventType)
198
- resolve(nil)
199
- return
201
+ print("track: \(id) to \(interaction) and \(eventType)")
202
+ } else {
203
+ print("track: \(id) not found")
200
204
  }
201
- reject(Constants.CACHE_MISS, nil, nil)
202
205
  }
203
206
 
204
207
  @objc
@@ -249,11 +252,95 @@ public class RCTAEPMessaging: RCTEventEmitter, MessagingDelegate {
249
252
  }
250
253
  }
251
254
 
255
+ @objc
256
+ func handleJavascriptMessage(
257
+ _ messageId: String,
258
+ handlerName: String
259
+ ) {
260
+ guard let message = jsHandlerMessageCache[messageId] else {
261
+ print("[RCTAEPMessaging] handleJavascriptMessage: No message found in cache for messageId: \(messageId)")
262
+ return
263
+ }
264
+
265
+ message.handleJavascriptMessage(handlerName) { [weak self] content in
266
+ self?.emitNativeEvent(
267
+ name: Constants.ON_JAVASCRIPT_MESSAGE_EVENT,
268
+ body: [
269
+ Constants.MESSAGE_ID_KEY: messageId,
270
+ Constants.HANDLER_NAME_KEY: handlerName,
271
+ Constants.CONTENT_KEY: content ?? ""
272
+ ]
273
+ )
274
+ }
275
+ }
276
+
277
+ /// MARK: - Unified PropositionItem Tracking Methods
278
+
279
+ /**
280
+ * Tracks interactions with a PropositionItem using the provided interaction and event type.
281
+ * This method is used by the React Native PropositionItem.track() method.
282
+ *
283
+ * - Parameters:
284
+ * - uuid: The UUID mapped to the PropositionItem (derived from activityId)
285
+ * - interaction: A custom string value to be recorded in the interaction (optional)
286
+ * - eventType: The MessagingEdgeEventType numeric value
287
+ * - tokens: Array containing the sub-item tokens for recording interaction (optional)
288
+ */
289
+
290
+ @objc
291
+ func trackPropositionItem(
292
+ _ uuid: String,
293
+ interaction: String?,
294
+ eventType: Int,
295
+ tokens: [String]?,
296
+ withResolver resolve: @escaping RCTPromiseResolveBlock,
297
+ withRejecter reject: @escaping RCTPromiseRejectBlock
298
+ ) {
299
+ NSLog("[MessagingBridge] trackPropositionItem called with eventType=\(eventType), uuid=\(uuid), interaction=\(String(describing: interaction)), tokens=\(String(describing: tokens))")
300
+
301
+ guard !uuid.isEmpty else {
302
+ NSLog("[MessagingBridge] Empty uuid provided; no-op.")
303
+ resolve(nil)
304
+ return
305
+ }
306
+
307
+ guard let proposition = propositionByUuid[uuid] else {
308
+ NSLog("[MessagingBridge] No cached proposition for uuid=\(uuid); no-op.")
309
+ resolve(nil)
310
+ return
311
+ }
312
+
313
+ NSLog("[MessagingBridge] Found proposition for uuid=\(uuid). scope=\(proposition.scope), items=\(proposition.items.count)")
314
+
315
+ // Event type mapping (Android parity)
316
+ let edgeEventType = mapEdgeEventType(eventType) ?? .display
317
+
318
+ // Track on the first item under this proposition
319
+ guard let item = proposition.items.first else {
320
+ NSLog("[MessagingBridge] Proposition for uuid=\(uuid) has no items; no-op.")
321
+ resolve(nil)
322
+ return
323
+ }
324
+
325
+ // Direct call without normalization (expecting valid inputs)
326
+ NSLog("[MessagingBridge] Tracking (direct) uuid=\(uuid), interaction=\(String(describing: interaction)), tokens=\(String(describing: tokens)), eventType=\(edgeEventType.rawValue)")
327
+ item.track(interaction, withEdgeEventType: edgeEventType, forTokens: tokens)
328
+
329
+ NSLog("[MessagingBridge] Tracking complete for uuid=\(uuid)")
330
+ resolve(nil)
331
+ }
332
+
333
+
334
+
335
+ // Map uuid (scopeDetails.activity.id) -> parent Proposition
336
+ private var propositionByUuid = [String: Proposition]()
337
+
252
338
  // Messaging Delegate Methods
253
339
  public func onDismiss(message: Showable) {
254
340
  if let fullscreenMessage = message as? FullscreenMessage,
255
341
  let parentMessage = fullscreenMessage.parent
256
342
  {
343
+ jsHandlerMessageCache.removeValue(forKey: parentMessage.id)
257
344
  emitNativeEvent(
258
345
  name: Constants.ON_DISMISS_EVENT,
259
346
  body: RCTAEPMessagingDataBridge.transformToMessage(
@@ -267,6 +354,7 @@ public class RCTAEPMessaging: RCTEventEmitter, MessagingDelegate {
267
354
  if let fullscreenMessage = message as? FullscreenMessage,
268
355
  let message = fullscreenMessage.parent
269
356
  {
357
+ jsHandlerMessageCache[message.id] = message
270
358
  emitNativeEvent(
271
359
  name: Constants.ON_SHOW_EVENT,
272
360
  body: RCTAEPMessagingDataBridge.transformToMessage(message: message)
@@ -328,3 +416,20 @@ public class RCTAEPMessaging: RCTEventEmitter, MessagingDelegate {
328
416
  RCTAEPMessaging.emitter.sendEvent(withName: name, body: body)
329
417
  }
330
418
  }
419
+
420
+ // MARK: - Private helpers
421
+ private extension RCTAEPMessaging {
422
+ /// Maps JS MessagingEdgeEventType integer values to AEPMessaging.MessagingEdgeEventType cases
423
+ /// JS enum values: DISMISS=0, INTERACT=1, TRIGGER=2, DISPLAY=3, PUSH_APPLICATION_OPENED=4, PUSH_CUSTOM_ACTION=5
424
+ func mapEdgeEventType(_ value: Int) -> MessagingEdgeEventType? {
425
+ switch value {
426
+ case 0: return .dismiss
427
+ case 1: return .interact
428
+ case 2: return .trigger
429
+ case 3: return .display
430
+ case 4: return .pushApplicationOpened
431
+ case 5: return .pushCustomAction
432
+ default: return nil
433
+ }
434
+ }
435
+ }
@@ -16,7 +16,11 @@ class Constants {
16
16
  static let ON_SHOW_EVENT = "onShow"
17
17
  static let SHOULD_SHOW_MESSAGE_EVENT = "shouldShowMessage"
18
18
  static let URL_LOADED_EVENT = "urlLoaded"
19
+ static let ON_JAVASCRIPT_MESSAGE_EVENT = "onJavascriptMessage"
19
20
  static let SUPPORTED_EVENTS = [
20
- ON_DISMISS_EVENT, ON_SHOW_EVENT, SHOULD_SHOW_MESSAGE_EVENT, URL_LOADED_EVENT,
21
+ ON_DISMISS_EVENT, ON_SHOW_EVENT, SHOULD_SHOW_MESSAGE_EVENT, URL_LOADED_EVENT, ON_JAVASCRIPT_MESSAGE_EVENT
21
22
  ]
23
+ static let MESSAGE_ID_KEY = "messageId"
24
+ static let HANDLER_NAME_KEY = "handlerName"
25
+ static let CONTENT_KEY = "content"
22
26
  }
@@ -27,4 +27,23 @@ public class RCTAEPMessagingDataBridge: NSObject {
27
27
  .map({ $0.asDictionary() })
28
28
  }
29
29
  }
30
+
31
+ /// Extracts the activity identifier from a proposition dictionary at scopeDetails.activity.id
32
+ static func extractActivityId(from propositionDict: [String: Any]) -> String? {
33
+ guard let scopeDetails = propositionDict["scopeDetails"] as? [String: Any] else {
34
+ NSLog("[MessagingBridge] Missing scopeDetails on proposition; cannot extract activity.id")
35
+ return nil
36
+ }
37
+
38
+ guard let activity = scopeDetails["activity"] as? [String: Any] else {
39
+ NSLog("[MessagingBridge] Missing activity under scopeDetails; cannot extract activity.id")
40
+ return nil
41
+ }
42
+
43
+ guard let id = activity["id"] as? String, !id.isEmpty else {
44
+ NSLog("[MessagingBridge] Missing or empty activity.id; skipping uuid cache mapping")
45
+ return nil
46
+ }
47
+ return id
48
+ }
30
49
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/react-native-aepmessaging",
3
- "version": "7.1.1",
3
+ "version": "7.2.1",
4
4
  "description": "Adobe Experience Platform support for React Native apps.",
5
5
  "homepage": "https://developer.adobe.com/client-sdks/documentation/",
6
6
  "license": "Apache-2.0",
@@ -39,5 +39,5 @@
39
39
  "installConfig": {
40
40
  "hoistingLimits": "dependencies"
41
41
  },
42
- "gitHead": "2e43601ac53a6982a643ff43d5af8276c675bf29"
42
+ "gitHead": "9e1fa8cd101f66602d9f9250fe8aa1f32b341da9"
43
43
  }
package/src/Messaging.ts CHANGED
@@ -37,6 +37,7 @@ export interface NativeMessagingModule {
37
37
  updatePropositionsForSurfaces: (surfaces: string[]) => void;
38
38
  trackContentCardDisplay: (proposition: MessagingProposition, contentCard: ContentCard) => void;
39
39
  trackContentCardInteraction: (proposition: MessagingProposition, contentCard: ContentCard) => void;
40
+ trackPropositionItem: (itemId: string, interaction: string | null, eventType: number, tokens: string[] | null) => void;
40
41
  }
41
42
 
42
43
  const RCTAEPMessaging: NativeModule & NativeMessagingModule =
@@ -90,17 +91,46 @@ class Messaging {
90
91
  static async getPropositionsForSurfaces(
91
92
  surfaces: string[]
92
93
  ): Promise<Record<string, MessagingProposition[]>> {
93
- return await RCTAEPMessaging.getPropositionsForSurfaces(surfaces);
94
- }
95
94
 
95
+ const propositionsList = await RCTAEPMessaging.getPropositionsForSurfaces(surfaces);
96
+ let messagingPropositionsForSurfaces: Record<string, MessagingProposition[]> = {};
97
+
98
+ for (const [surface, propositions] of Object.entries(propositionsList)) {
99
+ messagingPropositionsForSurfaces[surface] = propositions.map(
100
+ (proposition) => new MessagingProposition(proposition)
101
+ );
102
+ }
103
+
104
+ return messagingPropositionsForSurfaces;
105
+ }
106
+
107
+ /**
108
+ * @deprecated Use PropositionItem.track(...) instead.
109
+ */
96
110
  static trackContentCardDisplay(proposition: MessagingProposition, contentCard: ContentCard): void {
97
111
  RCTAEPMessaging.trackContentCardDisplay(proposition, contentCard);
98
112
  }
99
113
 
114
+ /**
115
+ * @deprecated Use PropositionItem.track(...) instead.
116
+ */
100
117
  static trackContentCardInteraction(proposition: MessagingProposition, contentCard: ContentCard): void {
101
118
  RCTAEPMessaging.trackContentCardInteraction(proposition, contentCard);
102
119
  }
103
120
 
121
+ /**
122
+ * Tracks interactions with a PropositionItem using the provided interaction and event type.
123
+ * This method is used internally by the PropositionItem.track() method.
124
+ *
125
+ * @param {string} itemId - The unique identifier of the PropositionItem
126
+ * @param {string | null} interaction - A custom string value to be recorded in the interaction
127
+ * @param {number} eventType - The MessagingEdgeEventType numeric value
128
+ * @param {string[] | null} tokens - Array containing the sub-item tokens for recording interaction
129
+ */
130
+ static trackPropositionItem(itemId: string, interaction: string | null, eventType: number, tokens: string[] | null): void {
131
+ RCTAEPMessaging.trackPropositionItem(itemId, interaction, eventType, tokens);
132
+ }
133
+
104
134
  /**
105
135
  * Function to set the UI Message delegate to listen the Message lifecycle events.
106
136
  * @returns A function to unsubscribe from all event listeners
@@ -110,31 +140,34 @@ class Messaging {
110
140
 
111
141
  const eventEmitter = new NativeEventEmitter(RCTAEPMessaging);
112
142
 
113
- eventEmitter.addListener('onShow', (message) =>
114
- messagingDelegate?.onShow?.(message)
143
+ eventEmitter.addListener('onShow', (message: Message) =>
144
+ messagingDelegate?.onShow?.(new Message(message))
115
145
  );
116
146
 
117
- eventEmitter.addListener('onDismiss', (message) => {
118
- messagingDelegate?.onDismiss?.(message);
147
+ eventEmitter.addListener('onDismiss', (message: Message) => {
148
+ const messageInstance = new Message(message);
149
+ messageInstance._clearJavascriptMessageHandlers();
150
+ messagingDelegate?.onDismiss?.(messageInstance);
119
151
  });
120
152
 
121
- eventEmitter.addListener('shouldShowMessage', (message) => {
153
+ eventEmitter.addListener('shouldShowMessage', (message: Message) => {
154
+ const messageInstance = new Message(message);
122
155
  const shouldShowMessage =
123
- messagingDelegate?.shouldShowMessage?.(message) ?? true;
156
+ messagingDelegate?.shouldShowMessage?.(messageInstance) ?? true;
124
157
  const shouldSaveMessage =
125
- messagingDelegate?.shouldSaveMessage?.(message) ?? false;
158
+ messagingDelegate?.shouldSaveMessage?.(messageInstance) ?? false;
126
159
  RCTAEPMessaging.setMessageSettings(shouldShowMessage, shouldSaveMessage);
127
160
  });
128
161
 
129
162
  if (Platform.OS === 'ios') {
130
- eventEmitter.addListener('urlLoaded', (event) =>
131
- messagingDelegate?.urlLoaded?.(event.url, event.message)
163
+ eventEmitter.addListener('urlLoaded', (event: {url: string, message: Message}) =>
164
+ messagingDelegate?.urlLoaded?.(event.url, new Message(event.message))
132
165
  );
133
166
  }
134
167
 
135
168
  if (Platform.OS === 'android') {
136
- eventEmitter.addListener('onContentLoaded', (event) =>
137
- messagingDelegate?.onContentLoaded?.(event.message)
169
+ eventEmitter.addListener('onContentLoaded', (event: {message: Message}) =>
170
+ messagingDelegate?.onContentLoaded?.(new Message(event.message))
138
171
  );
139
172
  }
140
173
 
package/src/index.ts CHANGED
@@ -11,25 +11,27 @@ governing permissions and limitations under the License.
11
11
  */
12
12
 
13
13
  import Messaging from './Messaging';
14
- import { ContentCard } from './models/ContentCard';
15
- import { HTMLProposition } from './models/HTMLProposition';
14
+ import { ContentCard, ContentCardData } from './models/ContentCard';
15
+
16
16
  import { InAppMessage } from './models/InAppMessage';
17
- import { JSONPropositionItem } from './models/JSONPropositionItem';
17
+ import { HTMLProposition, HTMLPropositionData } from './models/HTMLProposition';
18
+ import { JSONPropositionItem, JSONPropositionData } from './models/JSONProposition';
19
+
18
20
  import Message from './models/Message';
19
21
  import { MessagingDelegate } from './models/MessagingDelegate';
20
22
  import MessagingEdgeEventType from './models/MessagingEdgeEventType';
21
23
  import { MessagingProposition } from './models/MessagingProposition';
22
24
  import { MessagingPropositionItem } from './models/MessagingPropositionItem';
23
25
  import { PersonalizationSchema } from './models/PersonalizationSchema';
26
+ import { PropositionItem, PropositionItemData } from './models/PropositionItem';
24
27
  import { Activity, Characteristics } from './models/ScopeDetails';
25
28
 
26
29
  export {
27
30
  Activity,
28
31
  Characteristics,
29
32
  ContentCard,
30
- HTMLProposition,
33
+ ContentCardData,
31
34
  InAppMessage,
32
- JSONPropositionItem,
33
35
  Messaging,
34
36
  Message,
35
37
  MessagingDelegate,
@@ -37,4 +39,10 @@ export {
37
39
  MessagingProposition,
38
40
  MessagingPropositionItem,
39
41
  PersonalizationSchema,
42
+ PropositionItem,
43
+ PropositionItemData,
44
+ HTMLProposition,
45
+ HTMLPropositionData,
46
+ JSONPropositionItem,
47
+ JSONPropositionData,
40
48
  };
@@ -11,11 +11,12 @@
11
11
  */
12
12
 
13
13
  import { PersonalizationSchema } from './PersonalizationSchema';
14
+ import { PropositionItem, PropositionItemData } from './PropositionItem';
14
15
 
15
16
  type ContentCardTemplate = 'SmallImage';
16
17
  type DismissButtonStyle = 'circle' | 'none' | 'simple';
17
18
 
18
- export interface ContentCard {
19
+ export interface ContentCardData extends PropositionItemData {
19
20
  id: string;
20
21
  data: {
21
22
  contentType: 'application/json';
@@ -45,3 +46,13 @@ export interface ContentCard {
45
46
  };
46
47
  schema: PersonalizationSchema.CONTENT_CARD;
47
48
  }
49
+
50
+ export class ContentCard extends PropositionItem {
51
+ declare data: ContentCardData['data']; // Override data type for better typing
52
+
53
+ constructor(contentCardData: ContentCardData) {
54
+ super(contentCardData);
55
+ this.data = contentCardData.data;
56
+ }
57
+
58
+ }
@@ -11,11 +11,20 @@
11
11
  */
12
12
 
13
13
  import { PersonalizationSchema } from './PersonalizationSchema';
14
+ import { PropositionItem, PropositionItemData } from './PropositionItem';
14
15
 
15
- export interface HTMLProposition {
16
- id: string;
17
- data: {
18
- content: string;
19
- };
20
- schema: PersonalizationSchema.HTML_CONTENT;
16
+ export interface HTMLPropositionData extends PropositionItemData {
17
+ data: {
18
+ content: string;
19
+ };
20
+ schema: PersonalizationSchema.HTML_CONTENT;
21
21
  }
22
+
23
+ export class HTMLProposition extends PropositionItem {
24
+ declare data: HTMLPropositionData['data'];
25
+
26
+ constructor(htmlData: HTMLPropositionData) {
27
+ super(htmlData);
28
+ this.data = htmlData.data;
29
+ }
30
+ }
@@ -11,11 +11,20 @@
11
11
  */
12
12
 
13
13
  import { PersonalizationSchema } from './PersonalizationSchema';
14
+ import { PropositionItem, PropositionItemData } from './PropositionItem';
14
15
 
15
- export interface JSONPropositionItem {
16
- id: string;
17
- data: {
18
- content: string;
19
- };
20
- schema: PersonalizationSchema.JSON_CONTENT;
16
+ export interface JSONPropositionData extends PropositionItemData {
17
+ data: {
18
+ content: any;
19
+ };
20
+ schema: PersonalizationSchema.JSON_CONTENT;
21
21
  }
22
+
23
+ export class JSONPropositionItem extends PropositionItem {
24
+ declare data: JSONPropositionData['data'];
25
+
26
+ constructor(jsonData: JSONPropositionData) {
27
+ super(jsonData);
28
+ this.data = jsonData.data;
29
+ }
30
+ }
@@ -10,9 +10,23 @@ OF ANY KIND, either express or implied. See the License for the specific languag
10
10
  governing permissions and limitations under the License.
11
11
  */
12
12
 
13
- import { NativeModules } from 'react-native';
13
+ import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
14
+
14
15
  const RCTAEPMessaging = NativeModules.AEPMessaging;
15
16
 
17
+ // Registery to store inAppMessage callbacks for each message in Message.handleJavascriptMessage
18
+ // Record - {messageId : {handlerName : callback}}
19
+ const jsMessageHandlers: Record<string, Record<string, (content: string) => void>> = {};
20
+ const handleJSMessageEventEmitter = new NativeEventEmitter(RCTAEPMessaging);
21
+
22
+ // invokes the callback registered in Message.handleJavascriptMessage with the content received from the inAppMessage webview
23
+ handleJSMessageEventEmitter.addListener('onJavascriptMessage', (event) => {
24
+ const {messageId, handlerName, content} = event;
25
+ if (jsMessageHandlers[messageId] && jsMessageHandlers[messageId][handlerName]) {
26
+ jsMessageHandlers[messageId][handlerName](content);
27
+ }
28
+ });
29
+
16
30
  class Message {
17
31
  id: string;
18
32
  autoTrack: boolean;
@@ -47,7 +61,16 @@ class Message {
47
61
  * of the autoTrack setting.
48
62
  */
49
63
  dismiss(suppressAutoTrack?: boolean) {
50
- RCTAEPMessaging.dismiss(this.id, suppressAutoTrack ? true : false);
64
+ // iOS message.dismiss() accepts a boolean parameter to suppress autoTrack
65
+ // but on android side, message.dismiss() does not accept any parameters
66
+ if (Platform.OS === 'android') {
67
+ RCTAEPMessaging.dismiss(this.id);
68
+ }
69
+
70
+ if (Platform.OS === 'ios') {
71
+ RCTAEPMessaging.dismiss(this.id, suppressAutoTrack ? true : false);
72
+ }
73
+
51
74
  }
52
75
 
53
76
  /**
@@ -67,6 +90,42 @@ class Message {
67
90
  clear() {
68
91
  RCTAEPMessaging.clear(this.id);
69
92
  }
93
+
94
+ /**
95
+ * Adds a handler for named JavaScript messages sent from the message's WebView.
96
+ * The parameter passed to handler will contain the body of the message passed from the WebView's JavaScript.
97
+ * @param {string} handlerName: The name of the message that should be handled by the handler
98
+ * @param {function} handler: The method or closure to be called with the body of the message created in the Message's JavaScript
99
+ */
100
+ handleJavascriptMessage(handlerName: string, handler: (content: string) => void) {
101
+ // Validate parameters
102
+ if (!handlerName) {
103
+ console.warn('[AEP Messaging] handleJavascriptMessage: handlerName is required');
104
+ return;
105
+ }
106
+
107
+ if (typeof handler !== 'function') {
108
+ console.warn('[AEP Messaging] handleJavascriptMessage: handler must be a function');
109
+ return;
110
+ }
111
+
112
+ // cache the callback
113
+ if (!jsMessageHandlers[this.id]) {
114
+ jsMessageHandlers[this.id] = {};
115
+ }
116
+ jsMessageHandlers[this.id][handlerName] = handler;
117
+ RCTAEPMessaging.handleJavascriptMessage(this.id, handlerName);
118
+ }
119
+
120
+ /**
121
+ * @internal - For internal use only.
122
+ * Clears all the javascript message handlers for the message.
123
+ * This function must be called if the callbacks registered in handleJavascriptMessage are no longer needed.
124
+ * Failure to call this function may lead to memory leaks.
125
+ */
126
+ _clearJavascriptMessageHandlers() {
127
+ delete jsMessageHandlers[this.id];
128
+ }
70
129
  }
71
130
 
72
131
  export default Message;