diff --git a/CHANGELOG.md b/CHANGELOG.md index 242b8ad5..bcfc2b95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,40 @@ Change Log ========== +Version 4.3.0 *(August 11 2026)* +------------------------------------------- +**What's new** +* **[Android Platform]** + * Supports [CleverTap Android SDK v8.4.1](https://github.com/CleverTap/clevertap-android-sdk/blob/master/docs/CTCORECHANGELOG.md#version-841-august-2026). + * Spin-the-Wheel and Scratch Card gamified in-app templates — available to campaigns built with the Advanced InApp Builder, no app-side wiring needed. + * Per-element click attribution for in-apps — the `Notification Clicked` event now records which element of the in-app was tapped, across all templates. Swipe-to-dismiss is now configurable per campaign and is tracked as a `Notification Clicked` event too. + * Accessibility improvements across in-apps and App Inbox — dynamic text scaling, screen-reader announcements, content descriptions for close buttons/images/media controls, a larger 48dp dismiss tap target, and corrected carousel navigation for TalkBack. + * Custom-HTML header and footer in-apps now render when the host Activity is not a `FragmentActivity` (for example Unity or Unreal game engines). Opt in with the `CLEVERTAP_INAPP_FRAGMENTLESS_BANNERS` manifest flag; it is off by default, so existing apps are unaffected. + +* **[iOS Platform]** + * Supports [CleverTap iOS SDK v7.8.1](https://github.com/CleverTap/clevertap-ios-sdk/blob/master/CHANGELOG.md#version-781-august-07-2026). + * Spin-the-Wheel and Scratch Card gamified in-app templates — available to campaigns built with the Advanced InApp Builder, no app-side wiring needed. + * Per-element click attribution for in-apps — the `Notification Clicked` event now records which element of the in-app was tapped, across all templates. Tap-outside and swipe-to-dismiss gestures are now configurable per campaign and are tracked as `Notification Clicked` events too. + * Accessibility improvements across in-apps and App Inbox — VoiceOver labels, a larger dismiss-button tap area, and better handling of image content descriptions. + +**API changes** +* **[Android and iOS Platform]** + * Adds `dismissPipInApp()` — dismisses the Picture-in-Picture in-app notification currently on screen. It does nothing when no PIP in-app is visible, and never affects other in-app types. + +**Bug Fixes** +* **[Android Platform]** + * Fixes App Inbox messages never being saved on Android 6.0 through Android 10 (API 23–29), which left the inbox permanently empty on those devices. Affects every app using App Inbox on RN SDK v4.1.0 and v4.2.0; messages reappear on the next inbox fetch after updating, with no code change on your side. + * Fixes the built-in App Inbox not repainting the message list after a pull-to-refresh — newly fetched messages previously appeared only after closing and reopening the inbox. + * Fixes a crash when a Picture-in-Picture in-app played video on Android 6.0. Only affects apps whose `minSdkVersion` is below 24; the same fix also covers in-app and App Inbox video playback on that OS version. + +* **[iOS Platform]** + * Fixes the built-in App Inbox not repainting the message list after a pull-to-refresh — newly fetched messages previously appeared only after closing and reopening the inbox. + * Fixes in-apps that were missed while the app was in the background not being shown when the app returned to the foreground. + * Fixes the `Notification Clicked` event not carrying deeplink properties for custom-HTML in-apps. + * Fixes client-side in-apps not being persisted when an empty campaign list was received. + * Fixes full-screen cover in-apps built with the Advanced InApp Builder not respecting the device safe area. + * Fixes two potential crashes — one while saving variable diffs, and one during simultaneous variable updates. + Version 4.2.0 *(June 5 2026)* ------------------------------------------- **What's new** diff --git a/Example/app/App.js b/Example/app/App.js index 1de9d707..3e3c0a6b 100644 --- a/Example/app/App.js +++ b/Example/app/App.js @@ -370,6 +370,7 @@ export default class App extends Component { { action: Actions.IN_APPS_DISCARD, name: 'discardInAppNotifications' }, { action: Actions.IN_APPS_DISCARD_WITH_DISMISS, name: 'discardInAppNotifications(true)' }, { action: Actions.IN_APPS_RESUME, name: 'resumeInAppNotifications' }, + { action: Actions.IN_APPS_DISMISS_PIP, name: 'dismissPipInApp' }, ], }, { @@ -676,6 +677,9 @@ export default class App extends Component { case Actions.IN_APPS_RESUME: CleverTap.resumeInAppNotifications(); break; + case Actions.IN_APPS_DISMISS_PIP: + AppUtils.dismissPipInApp(); + break; case Actions.ENABLE_PERSONALIZATION: AppUtils.enablePersonalization(); break; diff --git a/Example/app/app-utils.js b/Example/app/app-utils.js index 5ea40c8a..db4842c2 100644 --- a/Example/app/app-utils.js +++ b/Example/app/app-utils.js @@ -710,6 +710,13 @@ export const getFeatureFlag = () => { }); }; +// InApp Controls +export const dismissPipInApp = () => { + CleverTap.dismissPipInApp(); + console.log('dismissPipInApp -> requested dismissal of the visible PIP InApp, if any'); + showToast('Dismiss PIP InApp', 'No-op when no PIP InApp is on screen'); +}; + // App Personalisation export const enablePersonalization = () => { CleverTap.enablePersonalization(); diff --git a/Example/app/constants.js b/Example/app/constants.js index 7d11192f..e811f660 100644 --- a/Example/app/constants.js +++ b/Example/app/constants.js @@ -65,6 +65,7 @@ export const Actions = { IN_APPS_DISCARD: 'IN_APPS_DISCARD', IN_APPS_DISCARD_WITH_DISMISS: 'IN_APPS_DISCARD_WITH_DISMISS', IN_APPS_RESUME: 'IN_APPS_RESUME', + IN_APPS_DISMISS_PIP: 'IN_APPS_DISMISS_PIP', ENABLE_PERSONALIZATION: 'ENABLE_PERSONALIZATION', ATTRIBUTION_IDENTIFIER: 'ATTRIBUTION_IDENTIFIER', OPT_IN: 'OPT_IN', diff --git a/Example/package-lock.json b/Example/package-lock.json index 8f25ee8b..4e5cea19 100644 --- a/Example/package-lock.json +++ b/Example/package-lock.json @@ -49,7 +49,8 @@ } }, "..": { - "version": "4.1.0", + "name": "clevertap-react-native", + "version": "4.3.0", "license": "MIT", "devDependencies": { "eslint": "^4.18.2", diff --git a/android/build.gradle b/android/build.gradle index 8d7b409c..172fff4d 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -35,8 +35,8 @@ android { defaultConfig { minSdkVersion 23 targetSdkVersion 36 - versionCode 420 - versionName "4.2.0" + versionCode 430 + versionName "4.3.0" buildConfigField("boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()) } @@ -54,7 +54,7 @@ android { } dependencies { - api 'com.clevertap.android:clevertap-android-sdk:8.3.0' + api 'com.clevertap.android:clevertap-android-sdk:8.4.1' implementation 'com.android.installreferrer:installreferrer:2.2' //compile 'com.android.support:appcompat-v7:28.0.0' implementation 'com.facebook.react:react-native:+' diff --git a/android/src/main/java/com/clevertap/react/CleverTapModuleImpl.java b/android/src/main/java/com/clevertap/react/CleverTapModuleImpl.java index e4fd98b0..13922063 100644 --- a/android/src/main/java/com/clevertap/react/CleverTapModuleImpl.java +++ b/android/src/main/java/com/clevertap/react/CleverTapModuleImpl.java @@ -1188,6 +1188,13 @@ public void resumeInAppNotifications() { } } + public void dismissPipInApp() { + CleverTapAPI cleverTap = getCleverTapAPI(); + if (cleverTap != null) { + cleverTap.dismissPipInApp(); + } + } + public void unmute() { CleverTapAPI cleverTap = getCleverTapAPI(); if (cleverTap != null) { diff --git a/android/src/newarch/CleverTapModule.kt b/android/src/newarch/CleverTapModule.kt index 9842b172..f233abb6 100644 --- a/android/src/newarch/CleverTapModule.kt +++ b/android/src/newarch/CleverTapModule.kt @@ -444,6 +444,10 @@ class CleverTapModule(reactContext: ReactApplicationContext?) : cleverTapModuleImpl.resumeInAppNotifications() } + override fun dismissPipInApp() { + cleverTapModuleImpl.dismissPipInApp() + } + override fun unmute() { cleverTapModuleImpl.unmute() } diff --git a/android/src/oldarch/CleverTapModule.kt b/android/src/oldarch/CleverTapModule.kt index cadf279c..0aeac4fa 100644 --- a/android/src/oldarch/CleverTapModule.kt +++ b/android/src/oldarch/CleverTapModule.kt @@ -523,6 +523,11 @@ class CleverTapModule(reactContext: ReactApplicationContext?) : cleverTapModuleImpl.resumeInAppNotifications() } + @ReactMethod + fun dismissPipInApp() { + cleverTapModuleImpl.dismissPipInApp() + } + @ReactMethod fun unmute() { cleverTapModuleImpl.unmute() diff --git a/clevertap-react-native.podspec b/clevertap-react-native.podspec index 0a57b38d..7250a5c1 100644 --- a/clevertap-react-native.podspec +++ b/clevertap-react-native.podspec @@ -24,7 +24,7 @@ Pod::Spec.new do |s| s.dependency 'React-Core' end - s.dependency 'CleverTap-iOS-SDK', '7.7.1' + s.dependency 'CleverTap-iOS-SDK', '7.8.1' if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then s.pod_target_xcconfig = { diff --git a/docs/install.md b/docs/install.md index 9e755348..290dd313 100644 --- a/docs/install.md +++ b/docs/install.md @@ -86,7 +86,7 @@ dependencies { //clevertap - implementation 'com.clevertap.android:clevertap-android-sdk:8.3.0' + implementation 'com.clevertap.android:clevertap-android-sdk:8.4.1' // other libs diff --git a/docs/usage.md b/docs/usage.md index 5d1abbe7..0bda0312 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -553,6 +553,18 @@ CleverTap.discardInAppNotifications(); CleverTap.resumeInAppNotifications(); ``` +#### Dismiss PIP InApp Notification + +*Available from CleverTap React Native SDK v4.3.0.* + +Dismisses the Picture-in-Picture (PIP) InApp notification that is currently on screen. Calling it when no PIP InApp is visible does nothing, and other InApp notification types are never affected. + +Dismissing frees the InApp display slot, so the next queued InApp notification (if any) may appear right away. To keep a screen free of all InApp notifications, call `suspendInAppNotifications()` when the screen opens and `resumeInAppNotifications()` when it closes. + +```javascript +CleverTap.dismissPipInApp(); +``` + ### For more information, - [See included Example Application](/Example/app/App.js) diff --git a/ios/CleverTapReact/CleverTapReact.mm b/ios/CleverTapReact/CleverTapReact.mm index 9e8e3355..2c60946b 100644 --- a/ios/CleverTapReact/CleverTapReact.mm +++ b/ios/CleverTapReact/CleverTapReact.mm @@ -960,6 +960,11 @@ - (void)messageDidSelect:(CleverTapInboxMessage *_Nonnull)message atIndex:(int)i [[self cleverTapInstance] resumeInAppNotifications]; } +RCT_EXPORT_METHOD(dismissPipInApp) { + RCTLogInfo(@"[CleverTap dismissPipInApp]"); + [[self cleverTapInstance] dismissPipInApp]; +} + RCT_EXPORT_METHOD(unmute) { RCTLogInfo(@"[CleverTap unmute]"); [[self cleverTapInstance] unmute]; diff --git a/package.json b/package.json index 2ff20286..52c403cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "clevertap-react-native", - "version": "4.2.0", + "version": "4.3.0", "description": "CleverTap React Native SDK.", "main": "src/index.js", "types": "src/index.d.ts", diff --git a/src/NativeCleverTapModule.ts b/src/NativeCleverTapModule.ts index c3f82dcf..17f494f9 100644 --- a/src/NativeCleverTapModule.ts +++ b/src/NativeCleverTapModule.ts @@ -209,6 +209,7 @@ export interface Spec extends TurboModule { suspendInAppNotifications(): void; discardInAppNotifications(dismissInAppIfVisible?: boolean): void; resumeInAppNotifications(): void; + dismissPipInApp(): void; fetchInApps(callback: ((error: Object, result: boolean) => void) | null): void; clearInAppResources(expiredOnly: boolean): void; customTemplateSetDismissed(templateName: string): Promise; diff --git a/src/index.d.ts b/src/index.d.ts index dfd1861f..94613995 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -782,6 +782,18 @@ export function isPushPermissionGranted(callback: CallbackString): void; */ export function resumeInAppNotifications(): void; + /** + * Dismisses the currently visible Picture-in-Picture (PIP) InApp Notification, if any. + * + * This is a no-op when no PIP InApp Notification is visible, and other InApp Notification + * types are never affected. + * + * Note: dismissing frees the InApp display slot, so the next queued InApp Notification (if + * any) may show immediately. To keep a screen free of all InApp Notifications, pair this with + * suspendInAppNotifications() on screen entry and resumeInAppNotifications() on exit. + */ + export function dismissPipInApp(): void; + /** * Fetches In Apps from server. * diff --git a/src/index.js b/src/index.js index e489919f..6546004c 100644 --- a/src/index.js +++ b/src/index.js @@ -12,7 +12,7 @@ const EventEmitter = Platform.select({ * @param {int} libVersion - The updated library version. If current version is 1.1.0 then pass as 10100 */ const libName = 'React-Native'; -const libVersion = 40200; +const libVersion = 40300; CleverTapReact.setLibrary(libName,libVersion); function defaultCallback(method, err, res) { @@ -981,6 +981,20 @@ var CleverTap = { CleverTapReact.resumeInAppNotifications(); }, + /** + * Dismisses the currently visible Picture-in-Picture (PIP) InApp Notification, if any. + * + * This is a no-op when no PIP InApp Notification is visible, and other InApp Notification + * types are never affected. + * + * Note: dismissing frees the InApp display slot, so the next queued InApp Notification (if + * any) may show immediately. To keep a screen free of all InApp Notifications, pair this with + * suspendInAppNotifications() on screen entry and resumeInAppNotifications() on exit. + */ + dismissPipInApp: function () { + CleverTapReact.dismissPipInApp(); + }, + /** * Set the SDK debug level * @param {int} 0 = off, 1 = on