feat(@desktop/metrics): send basic metrics (#15803) (#16040)

Issue #15737

Co-authored-by: Michał Iskierko <61889657+endulab@users.noreply.github.com>
This commit is contained in:
Jonathan Rainville 2024-08-08 16:53:49 -04:00 committed by GitHub
parent ada348486e
commit 0470723a5d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 104 additions and 30 deletions

View File

@ -151,7 +151,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

View File

@ -56,3 +56,5 @@ QtObject:
proc showCommunityMemberBannedNotification*(self: GlobalEvents, sectionId: string, title: string, message: string) {.signal.}
proc showCommunityMemberUnbannedNotification*(self: GlobalEvents, sectionId: string, title: string, message: string) {.signal.}
proc addCentralizedMetric*(self: GlobalEvents, eventName: string, eventValueJson: string) {.signal.}

View File

@ -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,
})

View File

@ -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.} =

View File

@ -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
}
}

View File

@ -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()))
}
}

View File

@ -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))
}
}
}

View File

@ -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." : "")
}
}
}

View File

@ -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
}

View File

@ -1367,6 +1367,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,

View File

@ -108,7 +108,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)

View File

@ -289,6 +289,7 @@ StatusWindow {
restoreAppState();
Global.openMetricsEnablePopupRequested.connect(openMetricsEnablePopup)
Global.addCentralizedMetric.connect(metricsStore.addCentralizedMetric)
}
signal navigateTo(string path)
@ -300,10 +301,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) {
@ -360,7 +361,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)
}
}
}
@ -378,7 +379,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})
}
}
}
}