From 4f29fb151cacf2ef3ed4768098b51e227155858c Mon Sep 17 00:00:00 2001 From: turtledreams Date: Mon, 18 Sep 2023 16:16:20 +0900 Subject: [PATCH 01/10] added feedback widget segmentation --- CHANGELOG.md | 3 +++ examples/examples_feedback_widgets.html | 7 +++++-- lib/countly.js | 21 +++++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d8d7df5..c167552a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## X.X.X +- Added a new method ('addFeedbackWidgetSegmentation') to send segmentation with Feedback Widgets + ## 23.6.0 - Added a new flag, 'loadAPMScriptsAsync', which can load the APM related scripts automatically for Async implementations - Adding SDK health check requests after init diff --git a/examples/examples_feedback_widgets.html b/examples/examples_feedback_widgets.html index e08a522d..61bf7c85 100644 --- a/examples/examples_feedback_widgets.html +++ b/examples/examples_feedback_widgets.html @@ -14,6 +14,10 @@ debug: true }); + // add feedback widget segmentation + const segmentation = {page: "home_page"}; + Countly.addFeedbackWidgetSegmentation(segmentation); + //================================================= // Fetching and displaying feedback widgets //================================================= @@ -30,7 +34,7 @@ } // Decide which which widget to show. Here the first rating widget is selected. - var i = countlyPresentableFeedback.length - 1; + var i = countlyPresentableFeedback.length; var countlyFeedbackWidget = countlyPresentableFeedback[0]; while (i--) { // You can change 'rating' to 'nps' or 'survey'. Or you can create your own logic here. @@ -48,7 +52,6 @@ Countly.present_feedback_widget(countlyFeedbackWidget, selectorId, selectorClass); } - //================================================= // Fetching and reporting feedback widgets manually //================================================= diff --git a/lib/countly.js b/lib/countly.js index c11fc815..c2868390 100644 --- a/lib/countly.js +++ b/lib/countly.js @@ -248,6 +248,7 @@ var trackingScrolls = false; var currentViewId = null; // this is the global variable for tracking the current view's ID. Used in view tracking. Becomes previous view ID at the end. var previousViewId = null; // this is the global variable for tracking the previous view's ID. Used in view tracking. First view has no previous view ID. + var feedbackWidgetSegmentation = null; // this is the global variable for tracking the segmentation of the feedback widget. Used in feedback widget. try { localStorage.setItem("cly_testLocal", true); @@ -3097,6 +3098,9 @@ url += "&platform=" + this.platform; url += "&app_version=" + this.app_version; url += "&sdk_version=" + SDK_VERSION; + if (feedbackWidgetSegmentation) { + url += "&segmentation=" + JSON.stringify(feedbackWidgetSegmentation); + } // Origin is passed to the popup so that it passes it back in the postMessage event // Only web SDK passes origin and web url += "&origin=" + passedOrigin; @@ -3471,6 +3475,23 @@ } }; + /** + * Add any key value pairs to be send with the feedback widgets + * @param {Object} segmentation - segmentation object with key value pairs to add custom segmentation to feedback widgets + */ + this.addFeedbackWidgetSegmentation = function(segmentation) { + log(logLevelEnums.INFO, "[addFeedbackWidgetSegmentation] Adding segmentation to feedback widgets:[" + JSON.stringify(segmentation) + "]"); + if (!segmentation || typeof segmentation !== "object") { + log(logLevelEnums.ERROR, "[addFeedbackWidgetSegmentation] Segmentation is not an object"); + return; + } + if (Object.keys(segmentation).length === 0) { + log(logLevelEnums.ERROR, "[addFeedbackWidgetSegmentation] Segmentation is empty"); + return; + } + feedbackWidgetSegmentation = segmentation; + }; + /** * Check if user or visit should be ignored */ From 531724c4442cfdcbb35403b1d765afe487009638 Mon Sep 17 00:00:00 2001 From: turtledreams Date: Mon, 18 Sep 2023 16:19:53 +0900 Subject: [PATCH 02/10] lint --- examples/examples_feedback_widgets.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/examples_feedback_widgets.html b/examples/examples_feedback_widgets.html index 61bf7c85..a83128ab 100644 --- a/examples/examples_feedback_widgets.html +++ b/examples/examples_feedback_widgets.html @@ -14,9 +14,9 @@ debug: true }); - // add feedback widget segmentation - const segmentation = {page: "home_page"}; - Countly.addFeedbackWidgetSegmentation(segmentation); + // add feedback widget segmentation + const segmentation = { page: "home_page" }; + Countly.addFeedbackWidgetSegmentation(segmentation); //================================================= // Fetching and displaying feedback widgets From 7c0bc317fe87b23a9b87d5850c09fe46692a5e95 Mon Sep 17 00:00:00 2001 From: turtledreams Date: Mon, 25 Sep 2023 00:23:46 +0900 Subject: [PATCH 03/10] changes --- CHANGELOG.md | 2 +- examples/examples_feedback_widgets.html | 9 ++++----- lib/countly.js | 27 ++++++++----------------- 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c167552a..45041680 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ ## X.X.X -- Added a new method ('addFeedbackWidgetSegmentation') to send segmentation with Feedback Widgets +- You can now add a segmentation while presenting a widget ## 23.6.0 - Added a new flag, 'loadAPMScriptsAsync', which can load the APM related scripts automatically for Async implementations diff --git a/examples/examples_feedback_widgets.html b/examples/examples_feedback_widgets.html index a83128ab..04cd6d1d 100644 --- a/examples/examples_feedback_widgets.html +++ b/examples/examples_feedback_widgets.html @@ -14,10 +14,6 @@ debug: true }); - // add feedback widget segmentation - const segmentation = { page: "home_page" }; - Countly.addFeedbackWidgetSegmentation(segmentation); - //================================================= // Fetching and displaying feedback widgets //================================================= @@ -48,8 +44,11 @@ var selectorId = ""; var selectorClass = ""; + // Define the segmentation (optional) + const segmentation = { page: "home_page" }; + // Display the feedback widget to the end user - Countly.present_feedback_widget(countlyFeedbackWidget, selectorId, selectorClass); + Countly.present_feedback_widget(countlyFeedbackWidget, selectorId, selectorClass, segmentation); } //================================================= diff --git a/lib/countly.js b/lib/countly.js index c2868390..3ea2d642 100644 --- a/lib/countly.js +++ b/lib/countly.js @@ -248,7 +248,6 @@ var trackingScrolls = false; var currentViewId = null; // this is the global variable for tracking the current view's ID. Used in view tracking. Becomes previous view ID at the end. var previousViewId = null; // this is the global variable for tracking the previous view's ID. Used in view tracking. First view has no previous view ID. - var feedbackWidgetSegmentation = null; // this is the global variable for tracking the segmentation of the feedback widget. Used in feedback widget. try { localStorage.setItem("cly_testLocal", true); @@ -3039,8 +3038,9 @@ * @param {Object} presentableFeedback - Current presentable feedback * @param {String} id - DOM id to append the feedback widget * @param {String} className - Class name to append the feedback widget + * @param {Object} feedbackWidgetSegmentation - Segmentation object to be passed to the feedback widget * */ - this.present_feedback_widget = function(presentableFeedback, id, className) { + this.present_feedback_widget = function(presentableFeedback, id, className, feedbackWidgetSegmentation) { log(logLevelEnums.INFO, "present_feedback_widget, Presenting the feedback widget by appending to the element with ID: [ " + id + " ] and className: [ " + className + " ]"); if (!this.check_consent(featureEnums.FEEDBACK)) { return; @@ -3054,6 +3054,12 @@ return; } + log(logLevelEnums.INFO, "present_feedback_widget, Adding segmentation to feedback widgets:[" + JSON.stringify(feedbackWidgetSegmentation) + "]"); + if (!feedbackWidgetSegmentation || typeof feedbackWidgetSegmentation !== "object" || Object.keys(feedbackWidgetSegmentation).length === 0) { + log(logLevelEnums.DEBUG, "present_feedback_widget, Segmentation is not an object or empty"); + feedbackWidgetSegmentation = null; + } + try { var url = this.url; @@ -3475,23 +3481,6 @@ } }; - /** - * Add any key value pairs to be send with the feedback widgets - * @param {Object} segmentation - segmentation object with key value pairs to add custom segmentation to feedback widgets - */ - this.addFeedbackWidgetSegmentation = function(segmentation) { - log(logLevelEnums.INFO, "[addFeedbackWidgetSegmentation] Adding segmentation to feedback widgets:[" + JSON.stringify(segmentation) + "]"); - if (!segmentation || typeof segmentation !== "object") { - log(logLevelEnums.ERROR, "[addFeedbackWidgetSegmentation] Segmentation is not an object"); - return; - } - if (Object.keys(segmentation).length === 0) { - log(logLevelEnums.ERROR, "[addFeedbackWidgetSegmentation] Segmentation is empty"); - return; - } - feedbackWidgetSegmentation = segmentation; - }; - /** * Check if user or visit should be ignored */ From 317c67f7c51aa4f165cc94528563d58aaba19319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Art=C5=ABrs=20Kadi=C4=B7is?= Date: Wed, 27 Sep 2023 16:18:32 +0300 Subject: [PATCH 04/10] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45041680..0eb6ca08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ ## X.X.X -- You can now add a segmentation while presenting a widget +- You can now add segmentation while presenting a widget ## 23.6.0 - Added a new flag, 'loadAPMScriptsAsync', which can load the APM related scripts automatically for Async implementations From a686a467b3b38679024efecf6e607881c63440a9 Mon Sep 17 00:00:00 2001 From: turtledreams Date: Wed, 4 Oct 2023 16:46:53 +0900 Subject: [PATCH 05/10] send a custom object instead of segmentation --- lib/countly.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/countly.js b/lib/countly.js index 3ea2d642..8fa97d8c 100644 --- a/lib/countly.js +++ b/lib/countly.js @@ -3041,6 +3041,7 @@ * @param {Object} feedbackWidgetSegmentation - Segmentation object to be passed to the feedback widget * */ this.present_feedback_widget = function(presentableFeedback, id, className, feedbackWidgetSegmentation) { + // TODO: feedbackWidgetSegmentation implementation only assumes we want to send segmentation data. Change it if we add more data to the custom object. log(logLevelEnums.INFO, "present_feedback_widget, Presenting the feedback widget by appending to the element with ID: [ " + id + " ] and className: [ " + className + " ]"); if (!this.check_consent(featureEnums.FEEDBACK)) { return; @@ -3105,7 +3106,9 @@ url += "&app_version=" + this.app_version; url += "&sdk_version=" + SDK_VERSION; if (feedbackWidgetSegmentation) { - url += "&segmentation=" + JSON.stringify(feedbackWidgetSegmentation); + var customObjectToSendWithTheWidget = {}; + customObjectToSendWithTheWidget.sg = feedbackWidgetSegmentation; + url += "&custom=" + JSON.stringify(customObjectToSendWithTheWidget); } // Origin is passed to the popup so that it passes it back in the postMessage event // Only web SDK passes origin and web From 6263d95cdef304d9de76ed90ebd3ea3308edfd53 Mon Sep 17 00:00:00 2001 From: turtledreams Date: Thu, 2 Nov 2023 16:17:38 +0900 Subject: [PATCH 06/10] changelog improvement --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88cfaf21..78378c38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ## 23.6.1 - Mitigated an issue where numerical device IDs were parsed at the initialization -- You can now add segmentation while presenting a widget +- You can now add segmentation while presenting a widget with 'present_feedback_widget' ## 23.6.0 - Added a new flag, 'loadAPMScriptsAsync', which can load the APM related scripts automatically for Async implementations From 2f6b1b8c65c10f9aadfcecf2b4bcd6f62d88aecd Mon Sep 17 00:00:00 2001 From: turtledreams Date: Thu, 9 Nov 2023 14:14:58 +0900 Subject: [PATCH 07/10] more succint example --- examples/examples_feedback_widgets.html | 78 +++++++++++-------------- 1 file changed, 33 insertions(+), 45 deletions(-) diff --git a/examples/examples_feedback_widgets.html b/examples/examples_feedback_widgets.html index 04cd6d1d..35bd78b5 100644 --- a/examples/examples_feedback_widgets.html +++ b/examples/examples_feedback_widgets.html @@ -30,19 +30,16 @@ } // Decide which which widget to show. Here the first rating widget is selected. - var i = countlyPresentableFeedback.length; - var countlyFeedbackWidget = countlyPresentableFeedback[0]; - while (i--) { - // You can change 'rating' to 'nps' or 'survey'. Or you can create your own logic here. - if (countlyPresentableFeedback[i].type === 'rating') { - countlyFeedbackWidget = countlyPresentableFeedback[i]; - break; - } + const widgetType = "rating"; + const countlyFeedbackWidget = countlyPresentableFeedback.find(widget => widget.type === widgetType); + if (!countlyFeedbackWidget) { + console.error(`[Countly] No ${widgetType} widget found`); + return; } // Define the element ID and the class name (optional) - var selectorId = ""; - var selectorClass = ""; + const selectorId = ""; + const selectorClass = ""; // Define the segmentation (optional) const segmentation = { page: "home_page" }; @@ -54,8 +51,6 @@ //================================================= // Fetching and reporting feedback widgets manually //================================================= - var CountlyFeedbackWidget; - var CountlyWidgetData; // an example of getting the widget list, using it to get widget data and then recording data for it manually. widgetType can be 'nps', 'survey' or 'rating' function getFeedbackWidgetListAndDoThings(widgetType) { // get the widget list @@ -68,43 +63,36 @@ } // find the widget object with the given widget type. This or a similar implementation can be used while using fetchAndDisplayWidget() as well - var i = feedbackList.length - 1; - while (i--) { - if (feedbackList[i].type === widgetType) { - CountlyFeedbackWidget = feedbackList[i]; - break; - } + const countlyFeedbackWidget = feedbackList.find(widget => widget.type === widgetType); + if (!countlyFeedbackWidget) { + console.error(`[Countly] No ${widgetType} widget found`); + return; } - // if the widget object is found - if (CountlyFeedbackWidget) { - // Get data with the widget object - Countly.getFeedbackWidgetData(CountlyFeedbackWidget, - // callback function, 1st param is the feedback widget data - function (feedbackData, err) { - if (err) { // error handling - console.log(err); - return; - } - - CountlyWidgetData = feedbackData; - // record data according to the widget type - if (CountlyWidgetData.type === 'nps') { - Countly.reportFeedbackWidgetManually(CountlyFeedbackWidget, CountlyWidgetData, { rating: 3, comment: "comment" }); - } else if (CountlyWidgetData.type === 'survey') { - var widgetResponse = {}; - // form the key/value pairs according to data - widgetResponse["answ-" + CountlyWidgetData.questions[0].id] = CountlyWidgetData.questions[0].type === "rating" ? 3 : "answer"; - Countly.reportFeedbackWidgetManually(CountlyFeedbackWidget, CountlyWidgetData, widgetResponse); - } else if (CountlyWidgetData.type === 'rating') { - Countly.reportFeedbackWidgetManually(CountlyFeedbackWidget, CountlyWidgetData, { rating: 3, comment: "comment", email: "email", contactMe: true }); - } + // Get data with the widget object + Countly.getFeedbackWidgetData(CountlyFeedbackWidget, + // callback function, 1st param is the feedback widget data + function (feedbackData, err) { + if (err) { // error handling + console.error(err); + return; } - ); - } else { - console.error("The widget type you are looking for does not exist") - } + const CountlyWidgetData = feedbackData; + // record data according to the widget type + if (CountlyWidgetData.type === 'nps') { + Countly.reportFeedbackWidgetManually(CountlyFeedbackWidget, CountlyWidgetData, { rating: 3, comment: "comment" }); + } else if (CountlyWidgetData.type === 'survey') { + var widgetResponse = {}; + // form the key/value pairs according to data + widgetResponse["answ-" + CountlyWidgetData.questions[0].id] = CountlyWidgetData.questions[0].type === "rating" ? 3 : "answer"; + Countly.reportFeedbackWidgetManually(CountlyFeedbackWidget, CountlyWidgetData, widgetResponse); + } else if (CountlyWidgetData.type === 'rating') { + Countly.reportFeedbackWidgetManually(CountlyFeedbackWidget, CountlyWidgetData, { rating: 3, comment: "comment", email: "email", contactMe: true }); + } + } + + ); }) } From 208c958f53bf18dd791ac521ee7cd58d6c4f3603 Mon Sep 17 00:00:00 2001 From: turtledreams Date: Thu, 9 Nov 2023 14:38:28 +0900 Subject: [PATCH 08/10] changelog --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0767685..967da13b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 23.6.3 +- You can now add segmentation while presenting a widget with 'present_feedback_widget' + ## 23.6.2 - Adding app version information to every request @@ -6,8 +9,6 @@ ## 23.6.1 - Mitigated an issue where numerical device IDs were parsed at the initialization -- You can now add segmentation while presenting a widget with 'present_feedback_widget' - ## 23.6.0 - Added a new flag, 'loadAPMScriptsAsync', which can load the APM related scripts automatically for Async implementations - Adding SDK health check requests after init From 681150439c6d638cc8c847cda91b3fa2e920064d Mon Sep 17 00:00:00 2001 From: turtledreams Date: Thu, 9 Nov 2023 20:48:18 +0900 Subject: [PATCH 09/10] jsdoc for optionals --- lib/countly.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/countly.js b/lib/countly.js index 4b741eb4..a362b344 100644 --- a/lib/countly.js +++ b/lib/countly.js @@ -3064,9 +3064,9 @@ /** * Present the feedback widget in webview * @param {Object} presentableFeedback - Current presentable feedback - * @param {String} id - DOM id to append the feedback widget - * @param {String} className - Class name to append the feedback widget - * @param {Object} feedbackWidgetSegmentation - Segmentation object to be passed to the feedback widget + * @param {String} [id] - DOM id to append the feedback widget (optional) + * @param {String} [className] - Class name to append the feedback widget (optional) + * @param {Object} [feedbackWidgetSegmentation] - Segmentation object to be passed to the feedback widget (optional) * */ this.present_feedback_widget = function(presentableFeedback, id, className, feedbackWidgetSegmentation) { // TODO: feedbackWidgetSegmentation implementation only assumes we want to send segmentation data. Change it if we add more data to the custom object. From ef553becf775ee737004943aa8046021c45acd63 Mon Sep 17 00:00:00 2001 From: turtledreams Date: Thu, 9 Nov 2023 20:52:32 +0900 Subject: [PATCH 10/10] optional --- lib/countly.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/countly.js b/lib/countly.js index a362b344..15529cd9 100644 --- a/lib/countly.js +++ b/lib/countly.js @@ -3064,8 +3064,8 @@ /** * Present the feedback widget in webview * @param {Object} presentableFeedback - Current presentable feedback - * @param {String} [id] - DOM id to append the feedback widget (optional) - * @param {String} [className] - Class name to append the feedback widget (optional) + * @param {String} [id] - DOM id to append the feedback widget (optional, in case not used pass undefined) + * @param {String} [className] - Class name to append the feedback widget (optional, in case not used pass undefined) * @param {Object} [feedbackWidgetSegmentation] - Segmentation object to be passed to the feedback widget (optional) * */ this.present_feedback_widget = function(presentableFeedback, id, className, feedbackWidgetSegmentation) {