diff --git a/.gitignore b/.gitignore
index ccfaa2e13..1cc37aefa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -109,6 +109,8 @@ build/
## User settings
xcuserdata/
packages/.DS_Store
+packages/*/.DS_Store
+packages/*/*/.DS_Store
dist/
test/.DS_Store
diff --git a/apps/AEPSampleAppNewArchEnabled/app/MessagingView.tsx b/apps/AEPSampleAppNewArchEnabled/app/MessagingView.tsx
index 87c2b6d72..270d5adbf 100644
--- a/apps/AEPSampleAppNewArchEnabled/app/MessagingView.tsx
+++ b/apps/AEPSampleAppNewArchEnabled/app/MessagingView.tsx
@@ -13,11 +13,12 @@ governing permissions and limitations under the License.
import React from 'react';
import {Button, Text, View, ScrollView} from 'react-native';
import {MobileCore} from '@adobe/react-native-aepcore';
-import {Messaging} from '@adobe/react-native-aepmessaging'
+import {Messaging, PersonalizationSchema} from '@adobe/react-native-aepmessaging'
import styles from '../styles/styles';
import { useRouter } from 'expo-router';
-const SURFACES = ['android-cb-preview'];
+const SURFACES = ['android-cbe-preview', 'cbe/json', 'android-cc'];
+const SURFACES_WITH_CONTENT_CARDS = ['android-cc'];
const messagingExtensionVersion = async () => {
const version = await Messaging.extensionVersion();
@@ -64,6 +65,42 @@ const getLatestMessage = async () => {
console.log('Latest Message:', message);
};
+// this method can be used to track click interactions with content cards
+const trackContentCardInteraction = async () => {
+ const messages = await Messaging.getPropositionsForSurfaces(SURFACES_WITH_CONTENT_CARDS);
+
+ for (const surface of SURFACES_WITH_CONTENT_CARDS) {
+ const propositions = messages[surface] || [];
+
+ for (const proposition of propositions) {
+ for (const propositionItem of proposition.items) {
+ if (propositionItem.schema === PersonalizationSchema.CONTENT_CARD) {
+ Messaging.trackContentCardInteraction(proposition, propositionItem);
+ console.log('trackContentCardInteraction', proposition, propositionItem);
+ }
+ }
+ }
+ }
+}
+
+// this method can be used to track display interactions with content cards
+const trackContentCardDisplay = async () => {
+ const messages = await Messaging.getPropositionsForSurfaces(SURFACES_WITH_CONTENT_CARDS);
+
+ for (const surface of SURFACES_WITH_CONTENT_CARDS) {
+ const propositions = messages[surface] || [];
+
+ for (const proposition of propositions) {
+ for (const propositionItem of proposition.items) {
+ if (propositionItem.schema === PersonalizationSchema.CONTENT_CARD) {
+ Messaging.trackContentCardDisplay(proposition, propositionItem);
+ console.log('trackContentCardDisplay', proposition, propositionItem);
+ }
+ }
+ }
+ }
+}
+
function MessagingView() {
const router = useRouter();
@@ -86,6 +123,8 @@ function MessagingView() {
+
+
);
diff --git a/packages/messaging/README.md b/packages/messaging/README.md
index 3a77a398e..85f50b963 100644
--- a/packages/messaging/README.md
+++ b/packages/messaging/README.md
@@ -434,3 +434,23 @@ function otherWorkflowFinished() {
currentMessage.clearMessage();
}
```
+
+## Tracking interactions with content cards
+
+### trackContentCardDisplay
+
+Tracks a Display interaction with the given ContentCard
+
+**Syntax**
+```javascript
+Messaging.trackContentCardDisplay(proposition, contentCard);
+```
+
+### trackContentCardInteraction
+
+Tracks a Click interaction with the given ContentCard
+
+**Syntax**
+```javascript
+Messaging.trackContentCardInteraction(proposition, contentCard);
+```
\ No newline at end of file
diff --git a/packages/messaging/__tests__/MessagingTests.ts b/packages/messaging/__tests__/MessagingTests.ts
index 143be26a6..f0ed77cd8 100644
--- a/packages/messaging/__tests__/MessagingTests.ts
+++ b/packages/messaging/__tests__/MessagingTests.ts
@@ -104,4 +104,26 @@ describe('Messaging', () => {
]);
expect(spy).toHaveBeenCalledWith(['testSurface1', 'testSurface2']);
});
+
+ it('should call trackContentCardDisplay', async () => {
+ const spy = jest.spyOn(
+ NativeModules.AEPMessaging,
+ 'trackContentCardDisplay'
+ );
+ const mockProposition = { propositionId: 'mockPropositionId' } as any;
+ const mockContentCard = { contentCardId: 'mockContentCardId' } as any;
+ await Messaging.trackContentCardDisplay(mockProposition, mockContentCard);
+ expect(spy).toHaveBeenCalledWith(mockProposition, mockContentCard);
+ });
+
+ it('should call trackContentCardInteraction', async () => {
+ const spy = jest.spyOn(
+ NativeModules.AEPMessaging,
+ 'trackContentCardInteraction'
+ );
+ const mockProposition = { propositionId: 'mockPropositionId' } as any;
+ const mockContentCard = { contentCardId: 'mockContentCardId' } as any;
+ await Messaging.trackContentCardInteraction(mockProposition, mockContentCard);
+ expect(spy).toHaveBeenCalledWith(mockProposition, mockContentCard);
+ });
});
diff --git a/packages/messaging/android/src/main/java/com/adobe/marketing/mobile/reactnative/messaging/RCTAEPMessagingModule.java b/packages/messaging/android/src/main/java/com/adobe/marketing/mobile/reactnative/messaging/RCTAEPMessagingModule.java
index a45a5732d..2d52139a6 100644
--- a/packages/messaging/android/src/main/java/com/adobe/marketing/mobile/reactnative/messaging/RCTAEPMessagingModule.java
+++ b/packages/messaging/android/src/main/java/com/adobe/marketing/mobile/reactnative/messaging/RCTAEPMessagingModule.java
@@ -27,6 +27,7 @@
import com.adobe.marketing.mobile.MobileCore;
import com.adobe.marketing.mobile.messaging.MessagingUtils;
import com.adobe.marketing.mobile.messaging.Proposition;
+import com.adobe.marketing.mobile.messaging.PropositionItem;
import com.adobe.marketing.mobile.messaging.Surface;
import com.adobe.marketing.mobile.services.ServiceProvider;
import com.adobe.marketing.mobile.services.ui.InAppMessage;
@@ -38,6 +39,7 @@
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
+import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import java.util.HashMap;
@@ -269,4 +271,28 @@ private void emitEvent(final String name, final Map data) {
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(name, eventData);
}
-}
\ No newline at end of file
+
+ @ReactMethod
+ public void trackContentCardDisplay(ReadableMap propositionMap, ReadableMap contentCardMap) {
+ final Map eventData = RCTAEPMessagingUtil.convertReadableMapToMap(propositionMap);
+ final Proposition proposition = Proposition.fromEventData(eventData);
+ for (PropositionItem item : proposition.getItems()) {
+ if (item.getItemId().equals(contentCardMap.getString("id"))) {
+ item.track(MessagingEdgeEventType.DISPLAY);
+ break;
+ }
+ }
+ }
+
+ @ReactMethod
+ public void trackContentCardInteraction(ReadableMap propositionMap, ReadableMap contentCardMap) {
+ final Map eventData = RCTAEPMessagingUtil.convertReadableMapToMap(propositionMap);
+ final Proposition proposition = Proposition.fromEventData(eventData);
+ for (PropositionItem item : proposition.getItems()) {
+ if (item.getItemId().equals(contentCardMap.getString("id"))) {
+ item.track("click", MessagingEdgeEventType.INTERACT, null);
+ break;
+ }
+ }
+ }
+}
diff --git a/packages/messaging/android/src/main/java/com/adobe/marketing/mobile/reactnative/messaging/RCTAEPMessagingUtil.java b/packages/messaging/android/src/main/java/com/adobe/marketing/mobile/reactnative/messaging/RCTAEPMessagingUtil.java
index cc8645166..ce6199ca5 100644
--- a/packages/messaging/android/src/main/java/com/adobe/marketing/mobile/reactnative/messaging/RCTAEPMessagingUtil.java
+++ b/packages/messaging/android/src/main/java/com/adobe/marketing/mobile/reactnative/messaging/RCTAEPMessagingUtil.java
@@ -19,6 +19,8 @@
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
+ import com.facebook.react.bridge.ReadableMapKeySetIterator;
+ import com.facebook.react.bridge.ReadableType;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeArray;
@@ -216,4 +218,66 @@ static ReadableMap convertToReadableMap(Map map) {
}
return writableMap;
}
- }
\ No newline at end of file
+
+ /**
+ * Converts {@link ReadableMap} Map to {@link Map}
+ *
+ * @param readableMap instance of {@code ReadableMap}
+ * @return instance of {@code Map}
+ */
+ static Map convertReadableMapToMap(final ReadableMap readableMap) {
+ ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
+ Map map = new HashMap<>();
+ while (iterator.hasNextKey()) {
+ String key = iterator.nextKey();
+ ReadableType type = readableMap.getType(key);
+ switch (type) {
+ case Boolean:
+ map.put(key, readableMap.getBoolean(key));
+ break;
+ case Number:
+ map.put(key, readableMap.getDouble(key));
+ break;
+ case String:
+ map.put(key, readableMap.getString(key));
+ break;
+ case Map:
+ map.put(key, convertReadableMapToMap(readableMap.getMap(key)));
+ break;
+ case Array:
+ map.put(key, convertReadableArrayToList(readableMap.getArray(key)));
+ break;
+ default:
+ break;
+ }
+ }
+ return map;
+ }
+
+ static List