diff --git a/src/app/boot/app_controller.nim b/src/app/boot/app_controller.nim index 38c3121e18..6641285be0 100644 --- a/src/app/boot/app_controller.nim +++ b/src/app/boot/app_controller.nim @@ -155,7 +155,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController = result.settingsService = settings_service.newService(statusFoundation.events) result.appSettingsVariant = newQVariant(result.settingsService) result.notificationsManager = newNotificationsManager(statusFoundation.events, result.settingsService) - result.metricsService = metrics_service.newService() + result.metricsService = metrics_service.newService(statusFoundation.threadpool) result.metricsVariant = newQVariant(result.metricsService) # Global diff --git a/src/app/global/global_events.nim b/src/app/global/global_events.nim index 930de02a6a..c299da414e 100644 --- a/src/app/global/global_events.nim +++ b/src/app/global/global_events.nim @@ -55,4 +55,6 @@ QtObject: proc showCommunityMemberBannedNotification*(self: GlobalEvents, sectionId: string, title: string, message: string) {.signal.} - proc showCommunityMemberUnbannedNotification*(self: GlobalEvents, sectionId: string, title: string, message: string) {.signal.} \ No newline at end of file + proc showCommunityMemberUnbannedNotification*(self: GlobalEvents, sectionId: string, title: string, message: string) {.signal.} + + proc addCentralizedMetric*(self: GlobalEvents, eventName: string, eventValueJson: string) {.signal.} \ No newline at end of file diff --git a/src/app_service/service/metrics/async_tasks.nim b/src/app_service/service/metrics/async_tasks.nim new file mode 100644 index 0000000000..37563938d9 --- /dev/null +++ b/src/app_service/service/metrics/async_tasks.nim @@ -0,0 +1,39 @@ +include ../../common/json_utils +include ../../../app/core/tasks/common + +type + AsyncAddCentralizedMetricTaskArg = ref object of QObjectTaskArg + eventName: string + eventValueJson: string + +proc asyncAddCentralizedMetricTask(argEncoded: string) {.gcsafe, nimcall.} = + let arg = decode[AsyncAddCentralizedMetricTaskArg](argEncoded) + try: + var metric = CentralizedMetricDto() + metric.eventName = arg.eventName + metric.eventValue = if arg.eventValueJson.len > 0: parseJson(arg.eventValueJson) else: JsonNode() + metric.platform = hostOS + metric.appVersion = APP_VERSION + let payload = %* {"metric": metric.toJsonNode} + let response = status_go.addCentralizedMetric($payload) + try: + let jsonObj = response.parseJson + if jsonObj.hasKey("error"): + arg.finish(%* { + "metricId": "", + "error": jsonObj{"error"}.getStr, + }) + return + except Exception: + discard + + arg.finish(%* { + "metricId": response, + "error": "", + }) + + except Exception as e: + arg.finish(%* { + "metricId": "", + "error": e.msg, + }) diff --git a/src/app_service/service/metrics/service.nim b/src/app_service/service/metrics/service.nim index ccdca12f53..81efa4bda5 100644 --- a/src/app_service/service/metrics/service.nim +++ b/src/app_service/service/metrics/service.nim @@ -1,42 +1,55 @@ import NimQml, json, chronicles, times include ../../common/json_utils +import ../../../app/core/tasks/[qt, threadpool] +import ../../../app/global/global_singleton import backend/response_type import status_go import constants import ./dto +include async_tasks + logScope: topics = "metrics" QtObject: type MetricsService* = ref object of QObject + threadpool: ThreadPool proc delete*(self: MetricsService) = self.QObject.delete - proc newService*(): MetricsService = + proc newService*(threadpool: ThreadPool): MetricsService = new(result, delete) result.QObject.setup + result.threadpool = threadpool - # for testing, needs to be discussed - proc addCentralizedMetric*(self: MetricsService) = + signalConnect(singletonInstance.globalEvents, "addCentralizedMetric(QString, QString)", + result, "addCentralizedMetric(QString, QString)", 2) + + # eventValueJson is a json string + proc addCentralizedMetric*(self: MetricsService, eventName: string, eventValueJson: string) {.slot.} = + let arg = AsyncAddCentralizedMetricTaskArg( + tptr: asyncAddCentralizedMetricTask, + vptr: cast[ByteAddress](self.vptr), + slot: "onCentralizedMetricAdded", + eventName: eventName, + eventValueJson: eventValueJson, + ) + self.threadpool.start(arg) + + proc onCentralizedMetricAdded*(self: MetricsService, response: string) {.slot.} = try: - var metric = CentralizedMetricDto() - metric.userId = "123456" - metric.eventName = "desktop-event" - metric.eventValue = parseJson("""{"action": "section-changed"}""") - metric.timestamp = now().toTime().toUnix() - metric.platform = hostOS - metric.appVersion = APP_VERSION + let responseObj = response.parseJson + let errorString = responseObj{"error"}.getStr() + if errorString != "": + error "onCentralizedMetricAdded", error=errorString + return - let payload = %* {"metric": metric.toJsonNode} - let response = status_go.addCentralizedMetric($payload) - let jsonObj = response.parseJson - if jsonObj.hasKey("error"): - error "addCentralizedMetric", errorMsg=jsonObj["error"].getStr - except Exception: - discard + debug "onCentralizedMetricAdded", metricId=responseObj{"metricId"}.getStr() + except Exception as e: + error "onCentralizedMetricAdded", exceptionMsg = e.msg proc centralizedMetricsEnabledChaned*(self: MetricsService) {.signal.} proc isCentralizedMetricsEnabled*(self: MetricsService): bool {.slot.} = diff --git a/storybook/pages/MetricsEnablePopupPage.qml b/storybook/pages/MetricsEnablePopupPage.qml index f52bbe7c44..8239f2d777 100644 --- a/storybook/pages/MetricsEnablePopupPage.qml +++ b/storybook/pages/MetricsEnablePopupPage.qml @@ -2,6 +2,7 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 import shared.popups 1.0 +import utils 1.0 import Storybook 1.0 @@ -30,7 +31,7 @@ SplitView { anchors.centerIn: parent modal: false visible: true - isOnboarding: true + placement: Constants.metricsEnablePlacement.unknown } } diff --git a/ui/app/AppLayouts/Onboarding/views/WelcomeView.qml b/ui/app/AppLayouts/Onboarding/views/WelcomeView.qml index 3c02050270..7d7c93a882 100644 --- a/ui/app/AppLayouts/Onboarding/views/WelcomeView.qml +++ b/ui/app/AppLayouts/Onboarding/views/WelcomeView.qml @@ -28,7 +28,7 @@ Item { id: d function showMetricsAndRunAction(action) { - Global.openMetricsEnablePopupRequested(true, popup => popup.closed.connect(() => action())) + Global.openMetricsEnablePopupRequested(Constants.metricsEnablePlacement.welcome, popup => popup.closed.connect(() => action())) } } diff --git a/ui/app/AppLayouts/Profile/views/PrivacyAndSecurityView.qml b/ui/app/AppLayouts/Profile/views/PrivacyAndSecurityView.qml index b8afe209cc..e68b607929 100644 --- a/ui/app/AppLayouts/Profile/views/PrivacyAndSecurityView.qml +++ b/ui/app/AppLayouts/Profile/views/PrivacyAndSecurityView.qml @@ -39,12 +39,12 @@ SettingsContentBase { id: enableMetricsSwitch checked: root.isCentralizedMetricsEnabled onClicked: { - Global.openMetricsEnablePopupRequested(false, popup => popup.toggleMetrics.connect(refreshSwitch)) + Global.openMetricsEnablePopupRequested(Constants.metricsEnablePlacement.privacyAndSecurity, popup => popup.toggleMetrics.connect(refreshSwitch)) } } ] onClicked: { - Global.openMetricsEnablePopupRequested(false, popup => popup.toggleMetrics.connect(refreshSwitch)) + Global.openMetricsEnablePopupRequested(Constants.metricsEnablePlacement.privacyAndSecurity, popup => popup.toggleMetrics.connect(refreshSwitch)) } } } diff --git a/ui/imports/shared/popups/MetricsEnablePopup.qml b/ui/imports/shared/popups/MetricsEnablePopup.qml index b9f2b7024d..d193c15c00 100644 --- a/ui/imports/shared/popups/MetricsEnablePopup.qml +++ b/ui/imports/shared/popups/MetricsEnablePopup.qml @@ -14,7 +14,7 @@ import utils 1.0 StatusModal { id: root - property bool isOnboarding: false + property string placement: Constants.metricsEnablePlacement.unknown signal toggleMetrics(bool enabled) @@ -83,7 +83,7 @@ StatusModal { Paragraph { Layout.fillWidth: true Layout.fillHeight: true - text: qsTr("Usage data will be shared from all profiles added to device. %1").arg(root.isOnboarding ? "Sharing usage data can be turned off anytime in Settings / Privacy and Security." : "") + text: qsTr("Usage data will be shared from all profiles added to device. %1").arg(root.placement !== Constants.metricsEnablePlacement.privacyAndSecurity ? "Sharing usage data can be turned off anytime in Settings / Privacy and Security." : "") } } } diff --git a/ui/imports/shared/stores/MetricsStore.qml b/ui/imports/shared/stores/MetricsStore.qml index 037e23b89a..61c4a7caac 100644 --- a/ui/imports/shared/stores/MetricsStore.qml +++ b/ui/imports/shared/stores/MetricsStore.qml @@ -7,5 +7,10 @@ QtObject { metrics.toggleCentralizedMetrics(enabled) } + function addCentralizedMetric(eventName, eventValue = null) { + let eventValueJsonStr = !!eventValue ? JSON.stringify(eventValue) : "" + metrics.addCentralizedMetric(eventName, eventValueJsonStr) + } + readonly property bool isCentralizedMetricsEnabled : metrics.isCentralizedMetricsEnabled } diff --git a/ui/imports/utils/Constants.qml b/ui/imports/utils/Constants.qml index f6c5d53c75..f3d06c2886 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -1364,6 +1364,13 @@ QtObject { readonly property string undefinedAccount: "undefined" } + readonly property QtObject metricsEnablePlacement: QtObject { + readonly property string unknown: "unknown" + readonly property string welcome: "welcome_view" + readonly property string privacyAndSecurity: "privacy_and_security_view" + readonly property string startApp: "start_app_after_upgrade" + } + enum MutingVariations { For15min = 1, For1hr = 2, diff --git a/ui/imports/utils/Global.qml b/ui/imports/utils/Global.qml index fd49737370..ac3ba0eb6a 100644 --- a/ui/imports/utils/Global.qml +++ b/ui/imports/utils/Global.qml @@ -110,7 +110,8 @@ QtObject { signal openBuyCryptoModalRequested() // Metrics - signal openMetricsEnablePopupRequested(bool isOnboarding, var cb) + signal openMetricsEnablePopupRequested(string placement, var cb) + signal addCentralizedMetric(string eventName, var eventValue) signal openAddEditSavedAddressesPopup(var params) signal openDeleteSavedAddressesPopup(var params) diff --git a/ui/main.qml b/ui/main.qml index da2ee745fc..6cd7e95c33 100644 --- a/ui/main.qml +++ b/ui/main.qml @@ -291,6 +291,7 @@ StatusWindow { restoreAppState(); Global.openMetricsEnablePopupRequested.connect(openMetricsEnablePopup) + Global.addCentralizedMetric.connect(metricsStore.addCentralizedMetric) } signal navigateTo(string path) @@ -302,10 +303,10 @@ StatusWindow { applicationWindow.requestActivate() } - function openMetricsEnablePopup(isOnboarding, cb = null) { + function openMetricsEnablePopup(placement, cb = null) { metricsPopupLoader.active = true metricsPopupLoader.item.visible = true - metricsPopupLoader.item.isOnboarding = isOnboarding + metricsPopupLoader.item.placement = placement if (cb) cb(metricsPopupLoader.item) if(!localAppSettings.metricsPopupSeen) { @@ -362,7 +363,7 @@ StatusWindow { // animation is finished, app main will be shown // open metrics popup only if it has not been seen if(!localAppSettings.metricsPopupSeen) { - openMetricsEnablePopup(true, null) + openMetricsEnablePopup(Constants.metricsEnablePlacement.startApp, null) } } } @@ -380,7 +381,12 @@ StatusWindow { sourceComponent: MetricsEnablePopup { visible: true onClosed: metricsPopupLoader.active = false - onToggleMetrics: applicationWindow.metricsStore.toggleCentralizedMetrics(enabled) + onToggleMetrics: { + applicationWindow.metricsStore.toggleCentralizedMetrics(enabled) + if(enabled) { + Global.addCentralizedMetric("usage_data_shared", {placement: metricsPopupLoader.item.placement}) + } + } } }