Skip to content

Commit

Permalink
Merge pull request #443 from Countly/25.01-release-branch
Browse files Browse the repository at this point in the history
25.01 release branch
  • Loading branch information
turtledreams authored Jan 20, 2025
2 parents 766f87d + 0f65908 commit d8273ec
Show file tree
Hide file tree
Showing 38 changed files with 1,530 additions and 174 deletions.
37 changes: 26 additions & 11 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,52 @@
## XX.X.X
* ! Minor breaking change ! User properties will now be automatically saved under the following conditions:
* When an event is recorded
* During an internal timer tick
* Upon flushing the event queue
## 25.1.0
* ! Minor breaking change ! `Countly.userDataBulk.save()` method is now optional. SDK will save the cached data with internal triggers regularly.

* Added Content feature methods:
* `enterContentZone`, to start Content checks (Experimental!)
* `exitContentZone`, to stop Content checks (Experimental!)
* Added feedback widget convenience methods to display the first available widget or the one meets the criteria:
* `Countly.feedback.showNPS(nameIDorTag?: string, widgetClosedCallback?: WidgetCallback): void`
* `Countly.feedback.showSurvey(nameIDorTag?: string, widgetClosedCallback?: WidgetCallback): void`
* `Countly.feedback.showRating(nameIDorTag?: string, widgetClosedCallback?: WidgetCallback): void`
* Added config interface `experimental` that provides experimental config options:
* `.experimental.enablePreviousNameRecording()` for reporting previous event/view names
* `.experimental.enableVisibilityTracking()` for reporting app visibility with events
* Added `Countly.deviceId.setID` method for changing device ID based on the device ID type
* Mitigated an issue where a session could have started while the app was in the background when the device ID was changed (non-merge).
* Added support for Arrays of primitive types in event segmentation

* Deprecated following SDK calls:
* `Countly.getCurrentDeviceId` (replaced with: `Countly.deviceId.getID`)
* `Countly.getDeviceIDType` (replaced with: `Countly.deviceId.getType`)
* `Countly.changeDeviceId` (replaced with: `Countly.deviceId.setID`)

* Mitigated an issue where a session could have started while the app was in the background when the device ID was changed (non-merge).
* Mitigated an issue where intent redirection checks were disabled by default
* Mitigated an issue crash tracking was not enabled with init config
* Mitigated an issue where app start time tracking was on by default

* Android Specific Changes:
* ! Minor breaking change ! Unsupported types for user properties will now be omitted, they won't be converted to strings.
* Disabled caching for webviews.
* Mitigated an issue in the upload plugin that prevented the upload of a symbol file
* Resolved a problem where revoked consents were sent after changes without merging.
* Fixed a bug that caused the device ID to be incorrectly set after changes with merging.
* Mitigated an issue that caused the device ID to be incorrectly set after changes with merging.
* Mitigated an issue where on consent revoke, remote config values were cleared, not anymore.

* iOS Specific Changes:
* Orientation info is now also sent during initialization
* Added visionOS build support
* Updated the SDK to ensure compatibility with the latest server response models
* Improved view tracking capabilities
* Mitigated an issue with the feedback widget URL encoding on iOS 16 and earlier, which prevented the widget from displaying
* Mitigated an issue with content fetch URL encoding on iOS 16 and earlier, which caused the request to fail
* Mitigated an issue where the terms and conditions URL (`tc` key) was sent without double quotes
* Orientation info is now also sent during initialization
* Mitigated an issue where consent information was not sent when no consent was given during initialization
* Mitigated an issue where a session did not end when session consent was removed
* Updated the SDK to ensure compatibility with the latest server response models
* Mitigated an issue where pausing a view resulted in a '0' view duration.
* Mitigated an issue where the user provided URLSessionConfiguration was not applied to direct requests

* Updated the underlying Android SDK version to 24.7.4
* Updated the underlying iOS SDK version to 24.7.4
* Updated the underlying Android SDK version to 24.7.8
* Updated the underlying iOS SDK version to 24.7.9

## 24.4.1
* Added support for Feedback Widget terms and conditions
Expand Down
44 changes: 42 additions & 2 deletions Countly.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
interface Segmentation {
[key: string]: number | string | boolean;
[key: string]: number | string | boolean | (number | string | boolean)[];
}

interface CountlyEventOptions {
Expand Down Expand Up @@ -89,6 +89,29 @@ declare module "countly-sdk-react-native-bridge" {
* Countly Feedback Module
*/
namespace feedback {

/**
* Shows the first available NPS widget that meets the criteria.
* @param {String} [nameIDorTag] - name, id, or tag of the widget to show (optional)
* @param {callback} [widgetClosedCallback] - called when the widget is closed (optional)
*/
export function showNPS(nameIDorTag?: string, widgetClosedCallback?: WidgetCallback): void;

/**
* Shows the first available survey widget that meets the criteria.
* @param {String} [nameIDorTag] - name, id, or tag of the widget to show (optional)
* @param {callback} [widgetClosedCallback] - called when the widget is closed (optional)
*/
export function showSurvey(nameIDorTag?: string, widgetClosedCallback?: WidgetCallback): void;

/**
* Shows the first available rating widget that meets the criteria.
* @param {String} [nameIDorTag] - name, id, or tag of the widget to show (optional)
* @param {callback} [widgetClosedCallback] - called when the widget is closed (optional)
*/
export function showRating(nameIDorTag?: string, widgetClosedCallback?: WidgetCallback): void;


/**
* Get a list of available feedback widgets as an array of objects.
* @param {FeedbackWidgetCallback} [onFinished] - returns (retrievedWidgets, error). This parameter is optional.
Expand All @@ -106,7 +129,7 @@ declare module "countly-sdk-react-native-bridge" {
*
* @return {ErrorObject} object {error: string or null}
*/
export function presentFeedbackWidget(feedbackWidget: FeedbackWidget, closeButtonText: string, widgetShownCallback: callback, widgetClosedCallback: callback): ErrorObject;
export function presentFeedbackWidget(feedbackWidget: FeedbackWidget, closeButtonText: string, widgetShownCallback: WidgetCallback, widgetClosedCallback: WidgetCallback): ErrorObject;

/**
* Get a feedback widget's data as an object.
Expand Down Expand Up @@ -1145,6 +1168,18 @@ declare module "countly-sdk-react-native-bridge" {
}

declare module "countly-sdk-react-native-bridge/CountlyConfig" {
interface experimental {
/**
* Enables previous name recording for views and events
*/
enablePreviousNameRecording(): this;

/**
* Enables app visibility tracking with events.
*/
enableVisibilityTracking(): this;
}

/**
*
* This class holds APM specific configurations to be used with
Expand Down Expand Up @@ -1237,6 +1272,11 @@ declare module "countly-sdk-react-native-bridge/CountlyConfig" {
*/
sdkInternalLimits: CountlyConfigSDKInternalLimits;

/**
* getter for experimental features
*/
experimental: experimental;

/**
* Method to set the server url
*
Expand Down
33 changes: 30 additions & 3 deletions CountlyConfig.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { initialize } from "./Logger.js";
import CountlyConfigApm from "./lib/configuration_interfaces/countly_config_apm.js";
import CountlyConfigSDKInternalLimits from "./lib/configuration_interfaces/countly_config_limits.js";
import CountlyConfigExp from "./lib/configuration_interfaces/countly_config_experimental.js";
/**
* Countly SDK React Native Bridge
* https://github.com/Countly/countly-sdk-react-native-bridge
Expand All @@ -15,11 +16,15 @@ import CountlyConfigSDKInternalLimits from "./lib/configuration_interfaces/count
* @param {String} appKey application key
*/
class CountlyConfig {
#crashReporting = false;
#apmLegacy = false;
#disableIntentRedirectionCheck = false;
constructor(serverURL, appKey) {
this.serverURL = serverURL;
this.appKey = appKey;
this._countlyConfigApmInstance = new CountlyConfigApm();
this._countlyConfigSDKLimitsInstance = new CountlyConfigSDKInternalLimits();
this._countlyConfigExpInstance = new CountlyConfigExp();
}

/**
Expand All @@ -29,10 +34,32 @@ class CountlyConfig {
return this._countlyConfigApmInstance;
}

/**
* Getter to get the SDK internal limits
*/
get sdkInternalLimits() {
return this._countlyConfigSDKLimitsInstance;
}

/**
* Getter to get the experimental configurations
*/
get experimental() {
return this._countlyConfigExpInstance;
}

get _crashReporting() {
return this.#crashReporting;
}

get _apmLegacy() {
return this.#apmLegacy;
}

get _disableIntentRedirectionCheck() {
return this.#disableIntentRedirectionCheck;
}

/**
* Method to set the server url
*
Expand Down Expand Up @@ -79,7 +106,7 @@ class CountlyConfig {
* Method to enable crash reporting to report unhandled crashes to Countly
*/
enableCrashReporting() {
this.crashReporting = true;
this.#crashReporting = true;
return this;
}

Expand Down Expand Up @@ -142,7 +169,7 @@ class CountlyConfig {
* Method to enable application performance monitoring which includes the recording of app start time.
*/
enableApm() {
this.enableApm = true;
this.#apmLegacy = true;
return this;
}

Expand All @@ -151,7 +178,7 @@ class CountlyConfig {
* This method should be used to disable them.
*/
disableAdditionalIntentRedirectionChecks() {
this.disableAdditionalIntentRedirectionChecks = true;
this.#disableIntentRedirectionCheck = true;
return this;
}

Expand Down
2 changes: 1 addition & 1 deletion CountlyReactNative.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'CountlyReactNative'
s.version = '24.4.1'
s.version = '25.1.0'
s.license = {
:type => 'COMMUNITY',
:text => <<-LICENSE
Expand Down
72 changes: 71 additions & 1 deletion Feedback.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,76 @@ class Feedback {
this.#state = state;
}

/**
* Shows the first available NPS widget that meets the criteria.
* @param {String} [nameIDorTag] - name, id, or tag of the widget to show (optional)
* @param {callback} [callback] - called when the widget is closed (optional)
*/
showNPS(nameIDorTag, callback) {
L.i(`showNPS, Will show NPS widget with name, id, or tag: [${nameIDorTag}], callback provided: [${typeof callback === "function"}]`);
this.#showInternalFeedback("nps", nameIDorTag, callback);
}
/**
* Shows the first available Survey widget that meets the criteria.
* @param {String} [nameIDorTag] - name, id, or tag of the widget to show (optional)
* @param {callback} [callback] - called when the widget is closed (optional)
*/
showSurvey(nameIDorTag, callback) {
L.i(`showSurvey, Will show Survey widget with name, id, or tag: [${nameIDorTag}], callback provided: [${typeof callback === "function"}]`);
this.#showInternalFeedback("survey", nameIDorTag, callback);
}

/**
* Shows the first available Rating widget that meets the criteria.
* @param {String} [nameIDorTag] - name, id, or tag of the widget to show (optional)
* @param {callback} [callback] - called when the widget is closed (optional)
*/
showRating(nameIDorTag, callback) {
L.i(`showRating, Will show Rating widget with name, id, or tag: [${nameIDorTag}], callback provided: [${typeof callback === "function"}]`);
this.#showInternalFeedback("rating", nameIDorTag, callback);
}

#showInternalFeedback(widgetType, nameIDorTag, callback) {
if (!this.#state.isInitialized) {
L.e(`showInternalFeedback, 'init' must be called before 'showInternalFeedback'`);
return;
}
if (typeof nameIDorTag !== "string") {
L.d(`showInternalFeedback, unsupported data type of nameIDorTag or its not given : [${typeof nameIDorTag}]`);
}
this.getAvailableFeedbackWidgets((retrievedWidgets, error) => {
if (error) {
L.e(`showInternalFeedback, ${error}`);
return;
}
if (!retrievedWidgets || retrievedWidgets.length === 0) {
L.d(`showInternalFeedback, no feedback widgets found`);
return;
}
L.d(`showInternalFeedback, Found [${retrievedWidgets.length}] feedback widgets`);
let widget = retrievedWidgets.find(w => w.type === widgetType);
try {
if (nameIDorTag && typeof nameIDorTag === 'string') {
const matchedWidget = retrievedWidgets.find(w =>
w.type === widgetType && (w.name === nameIDorTag || w.id === nameIDorTag || w.tags.includes(nameIDorTag))
);
if (matchedWidget) {
widget = matchedWidget;
L.v(`showInternalFeedback, Found ${widgetType} widget by name, id, or tag: [${JSON.stringify(matchedWidget)}]`);
}
}
} catch (error) {
L.e(`showInternalFeedback, Error while finding widget: ${error}`);
}

if (!widget) {
L.d(`showInternalFeedback, No ${widgetType} widget found.`);
return;
}
this.presentFeedbackWidget(widget, null, null, callback);
});
}

/**
* Get a list of available feedback widgets as an array of objects.
* @param {callback} [onFinished] - returns (retrievedWidgets, error). This parameter is optional.
Expand All @@ -19,7 +89,7 @@ class Feedback {
return { error: message, data: null };
}

L.d("getAvailableFeedbackWidgets, getAvailableFeedbackWidgets");
L.d("getAvailableFeedbackWidgets, fetching available feedback widgets");
let result = null;
let error = null;
try {
Expand Down
Loading

0 comments on commit d8273ec

Please sign in to comment.