Skip to content

Commit aa7a00d

Browse files
authored
feat: added analytics (#86)
1 parent 73c457b commit aa7a00d

File tree

6 files changed

+175
-3
lines changed

6 files changed

+175
-3
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#include "ImmutableAnalytics.h"
2+
3+
#include "JsonDomBuilder.h"
4+
5+
#define EVENT_DATA_SUCCESS "succeeded"
6+
7+
8+
void UImmutableAnalytics::Track(EEventName EventName)
9+
{
10+
check(JSConnector.IsValid());
11+
12+
FString FullData;
13+
14+
FJsonObjectConverter::UStructToJsonObjectString<FEventData>({ "unrealSdk", GetEventName(EventName), "" }, FullData);
15+
16+
JSConnector->CallJS(ImmutablePassportAction::TRACK, FullData, FImtblJSResponseDelegate::CreateUObject(this, &UImmutableAnalytics::OnTrackResponse));
17+
}
18+
19+
void UImmutableAnalytics::Track(EEventName EventName, bool Success)
20+
{
21+
TMap<FString, FString> EventData = {{EVENT_DATA_SUCCESS, Success ? TEXT("true") : TEXT("false") } };
22+
Track(EventName, EventData);
23+
}
24+
25+
void UImmutableAnalytics::Track(EEventName EventName, TMap<FString, FString>& EventData)
26+
{
27+
check(JSConnector.IsValid());
28+
29+
FJsonDomBuilder::FObject Object;
30+
31+
for (auto data : EventData)
32+
{
33+
Object.Set(data.Key, data.Value);
34+
}
35+
36+
FString FullData;
37+
38+
FJsonObjectConverter::UStructToJsonObjectString<FEventData>({ "unrealSdk", GetEventName(EventName), Object.ToString() }, FullData);
39+
40+
JSConnector->CallJS(ImmutablePassportAction::TRACK, FullData, FImtblJSResponseDelegate::CreateUObject(this, &UImmutableAnalytics::OnTrackResponse));
41+
}
42+
43+
void UImmutableAnalytics::Setup(TWeakObjectPtr<UImtblJSConnector> Connector)
44+
{
45+
IMTBL_LOG_FUNCSIG
46+
47+
if (!Connector.IsValid())
48+
{
49+
IMTBL_ERR("Invalid JSConnector passed to UImmutableAnalytics::Setup.")
50+
return;
51+
}
52+
53+
JSConnector = Connector.Get();
54+
}
55+
56+
void UImmutableAnalytics::OnTrackResponse(FImtblJSResponse Response)
57+
{
58+
// Currently, we ignore tracking errors. Edit if it is required
59+
}
60+
61+
FString UImmutableAnalytics::GetEventName(EEventName EventName)
62+
{
63+
switch(EventName)
64+
{
65+
#define CONVERT(EventName, EventNameString) case EEventName::EventName: return EventNameString;
66+
EVENT_NAME_LIST
67+
#undef CONVERT
68+
default:
69+
return "";
70+
}
71+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#pragma once
2+
3+
#include "Immutable/ImmutablePassport.h"
4+
5+
#include "ImmutableAnalytics.generated.h"
6+
7+
USTRUCT(BlueprintType)
8+
struct FEventData
9+
{
10+
GENERATED_BODY()
11+
12+
UPROPERTY()
13+
FString moduleName;
14+
15+
UPROPERTY()
16+
FString eventName;
17+
18+
UPROPERTY()
19+
FString properties;
20+
};
21+
22+
/**
23+
* Immutable bridge sdk analytics utility
24+
*/
25+
UCLASS()
26+
class IMMUTABLE_API UImmutableAnalytics : public UObject
27+
{
28+
GENERATED_BODY()
29+
30+
public:
31+
32+
/**
33+
* Event names to track
34+
*/
35+
#define EVENT_NAME_LIST \
36+
CONVERT(INIT_PASSPORT, "initialisedPassport") \
37+
CONVERT(START_LOGIN, "startedLogin") \
38+
CONVERT(COMPLETE_LOGIN, "performedLogin") \
39+
CONVERT(START_LOGIN_PKCE, "startedLoginPkce") \
40+
CONVERT(COMPLETE_LOGIN_PKCE, "performedLoginPkce") \
41+
CONVERT(COMPLETE_RELOGIN, "performedRelogin") \
42+
CONVERT(START_CONNECT_IMX, "startedConnectImx") \
43+
CONVERT(COMPLETE_CONNECT_IMX, "performedConnectImx") \
44+
CONVERT(START_CONNECT_IMX_PKCE, "startedConnectImxPkce") \
45+
CONVERT(COMPLETE_CONNECT_IMX_PKCE, "performedConnectImxPkce") \
46+
CONVERT(COMPLETE_RECONNECT, "performedReconnect") \
47+
CONVERT(COMPLETE_LOGOUT, "performedLogout") \
48+
CONVERT(COMPLETE_LOGOUT_PKCE, "performedLogoutPkce")
49+
50+
enum class EEventName: uint8
51+
{
52+
#define CONVERT(name, nameString) name,
53+
EVENT_NAME_LIST
54+
#undef CONVERT
55+
};
56+
57+
public:
58+
void Setup(TWeakObjectPtr<class UImtblJSConnector> Connector);
59+
/**
60+
* Performs the call to game bridge track method
61+
* @param EventName Name that will be tracked
62+
* @param Success Single event data record that track "succeeded" field
63+
* @param EventData Map with customed data, converted to json object
64+
*/
65+
void Track(EEventName EventName);
66+
void Track(EEventName EventName, bool Success);
67+
void Track(EEventName EventName, TMap<FString, FString>& EventData);
68+
69+
private:
70+
// Convert enum to string
71+
FString GetEventName(EEventName EventName);
72+
// Callback method for Track from bridge
73+
void OnTrackResponse(FImtblJSResponse Response);
74+
75+
private:
76+
TWeakObjectPtr<class UImtblJSConnector> JSConnector;
77+
78+
};

Source/Immutable/Private/Immutable/ImmutablePassport.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "Immutable/ImmutablePassport.h"
44

5+
#include "ImmutableAnalytics.h"
56
#include "Immutable/Misc/ImtblLogging.h"
67
#include "Immutable/ImmutableResponses.h"
78
#include "Immutable/ImtblJSConnector.h"
@@ -57,6 +58,7 @@ void UImmutablePassport::Connect(bool IsConnectImx, bool TryToRelogin, const FIm
5758
}
5859
else
5960
{
61+
Analytics->Track(IsConnectImx ? UImmutableAnalytics::EEventName::START_CONNECT_IMX : UImmutableAnalytics::EEventName::START_LOGIN);
6062
CallJS(ImmutablePassportAction::INIT_DEVICE_FLOW, TEXT(""), ResponseDelegate, FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnInitDeviceFlowResponse));
6163
}
6264
}
@@ -70,6 +72,7 @@ void UImmutablePassport::ConnectPKCE(bool IsConnectImx, const FImtblPassportResp
7072
SetStateFlags(IPS_IMX);
7173
}
7274
PKCEResponseDelegate = ResponseDelegate;
75+
Analytics->Track(IsConnectImx ? UImmutableAnalytics::EEventName::START_CONNECT_IMX_PKCE : UImmutableAnalytics::EEventName::START_LOGIN_PKCE);
7376
CallJS(ImmutablePassportAction::GetPKCEAuthUrl, TEXT(""), PKCEResponseDelegate, FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnGetPKCEAuthUrlResponse));
7477
}
7578
#endif
@@ -210,11 +213,15 @@ void UImmutablePassport::Setup(const TWeakObjectPtr<UImtblJSConnector> Connector
210213

211214
if (!Connector.IsValid())
212215
{
213-
IMTBL_ERR("Invalid JSConnector passed to UImmutablePassport::Initialize.")
216+
IMTBL_ERR("Invalid JSConnector passed to UImmutablePassport::Setup.")
214217
return;
215218
}
216219

217220
JSConnector = Connector.Get();
221+
222+
// Analytics
223+
Analytics = NewObject<UImmutableAnalytics>(this);
224+
Analytics->Setup(Connector);
218225
}
219226

220227
void UImmutablePassport::ReinstateConnection(FImtblJSResponse Response)
@@ -224,15 +231,19 @@ void UImmutablePassport::ReinstateConnection(FImtblJSResponse Response)
224231
if (auto ResponseDelegate = GetResponseDelegate(Response))
225232
{
226233
// currently, this response has to be called only for RELOGIN AND RECONNECT bridge routines
227-
const FString CallbackName = (Response.responseFor.Compare(ImmutablePassportAction::RELOGIN, ESearchCase::IgnoreCase) == 0) ? "Relogin" : "Reconnect";
234+
bool IsRelogin = Response.responseFor.Compare(ImmutablePassportAction::RELOGIN, ESearchCase::IgnoreCase) == 0;
235+
const FString CallbackName = IsRelogin ? "Relogin" : "Reconnect";
236+
UImmutableAnalytics::EEventName EventName = IsRelogin ? UImmutableAnalytics::EEventName::COMPLETE_RELOGIN : UImmutableAnalytics::EEventName::COMPLETE_RECONNECT;
228237

229238
if (Response.JsonObject->GetBoolField(TEXT("result")))
230239
{
231240
SetStateFlags(IPS_CONNECTED);
232241
ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{true, "", Response});
242+
Analytics->Track(EventName, true);
233243
}
234244
else
235245
{
246+
Analytics->Track(EventName, false);
236247
#if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC
237248
if (IsStateFlagsSet(IPS_PKCE))
238249
{
@@ -296,7 +307,7 @@ void UImmutablePassport::OnInitializeResponse(FImtblJSResponse Response)
296307
IMTBL_ERR("Passport initialization failed.")
297308
Response.Error.IsSet() ? Msg = Response.Error->ToString() : Msg = Response.JsonObject->GetStringField(TEXT("error"));
298309
}
299-
310+
Analytics->Track(UImmutableAnalytics::EEventName::INIT_PASSPORT, Response.success);
300311
ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{Response.success, Msg, Response});
301312
}
302313
}
@@ -395,6 +406,7 @@ void UImmutablePassport::OnLogoutResponse(FImtblJSResponse Response)
395406

396407
return;
397408
}
409+
Analytics->Track(UImmutableAnalytics::EEventName::COMPLETE_LOGOUT);
398410
Message = "Logged out";
399411
IMTBL_LOG("%s", *Message)
400412
ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{ Response.success, Message });
@@ -498,6 +510,7 @@ void UImmutablePassport::OnConnectPKCEResponse(FImtblJSResponse Response)
498510
ResetStateFlags(IPS_PKCE);
499511
Response.Error.IsSet() ? Msg = Response.Error->ToString() : Msg = Response.JsonObject->GetStringField(TEXT("error"));
500512
}
513+
Analytics->Track(IsStateFlagsSet(IPS_IMX) ? UImmutableAnalytics::EEventName::COMPLETE_CONNECT_IMX_PKCE : UImmutableAnalytics::EEventName::COMPLETE_LOGIN_PKCE, Response.success);
501514
PKCEResponseDelegate.ExecuteIfBound(FImmutablePassportResult{Response.success, Msg});
502515
PKCEResponseDelegate = nullptr;
503516

@@ -675,6 +688,7 @@ void UImmutablePassport::OnConfirmCodeResponse(FImtblJSResponse Response)
675688
{
676689
FString Msg;
677690
FString TypeOfConnection = IsStateFlagsSet(IPS_IMX) ? TEXT("connect") : TEXT("login");
691+
UImmutableAnalytics::EEventName EventName = IsStateFlagsSet(IPS_IMX) ? UImmutableAnalytics::EEventName::COMPLETE_CONNECT_IMX : UImmutableAnalytics::EEventName::COMPLETE_LOGIN;
678692

679693
ResetStateFlags(IPS_CONNECTING);
680694
if (Response.success)
@@ -687,6 +701,7 @@ void UImmutablePassport::OnConfirmCodeResponse(FImtblJSResponse Response)
687701
IMTBL_LOG("%s code not confirmed.", *TypeOfConnection)
688702
Response.Error.IsSet() ? Msg = Response.Error->ToString() : Msg = Response.JsonObject->GetStringField(TEXT("error"));
689703
}
704+
Analytics->Track(EventName, Response.success);
690705
ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{Response.success, Msg, Response});
691706
}
692707
}
@@ -850,6 +865,7 @@ void UImmutablePassport::OnDeepLinkActivated(FString DeepLink)
850865
{
851866
FGraphEventRef GameThreadTask = FFunctionGraphTask::CreateAndDispatchWhenReady([this]()
852867
{
868+
Analytics->Track(UImmutableAnalytics::EEventName::COMPLETE_LOGOUT_PKCE);
853869
PKCELogoutResponseDelegate.ExecuteIfBound(FImmutablePassportResult{true, "Logged out"});
854870
PKCELogoutResponseDelegate = nullptr;
855871
ResetStateFlags(IPS_CONNECTED | IPS_PKCE | IPS_IMX);

Source/Immutable/Public/Immutable/ImmutableNames.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,6 @@ namespace ImmutablePassportAction
3232
const FString EnvProduction = TEXT("production");
3333
const FString ImxIsRegisteredOffchain = TEXT("isRegisteredOffchain");
3434
const FString ImxRegisterOffchain = TEXT("registerOffchain");
35+
36+
const FString TRACK = TEXT("track");
3537
} // namespace ImmutablePassportAction

Source/Immutable/Public/Immutable/ImmutablePassport.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,4 +254,8 @@ class IMMUTABLE_API UImmutablePassport : public UObject
254254
};
255255

256256
uint8 StateFlags = IPS_NONE;
257+
258+
UPROPERTY()
259+
class UImmutableAnalytics* Analytics = nullptr;
260+
257261
};

Source/Immutable/Public/Immutable/ImtblJSConnector.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class IMMUTABLE_API UImtblJSConnector : public UObject
2727
GENERATED_BODY()
2828

2929
friend class UImmutablePassport;
30+
friend class UImmutableAnalytics;
3031

3132
public:
3233
DECLARE_MULTICAST_DELEGATE(FOnBridgeReadyDelegate);

0 commit comments

Comments
 (0)