Skip to content

Commit f59141b

Browse files
feat: add embedded login support (#3972)
1 parent b6bca3d commit f59141b

21 files changed

+696
-318
lines changed
Binary file not shown.
732 Bytes
Binary file not shown.
Binary file not shown.

Source/Immutable/Private/Immutable/Actions/ImtblConnectImxAsyncAction.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,24 @@ void UImtblConnectionAsyncActions::Activate()
3737
{
3838
FString Error = "Connect failed due to missing world or world context object.";
3939
IMTBL_WARN("%s", *Error)
40-
Failed.Broadcast(Error);
40+
Internal_DynamicMulticastDelegate_OnFailed.Broadcast(Error);
4141

4242
return;
4343
}
4444

4545
GetSubsystem()->WhenReady(this, &UImtblConnectionAsyncActions::DoConnect);
4646
}
4747

48+
UImtblConnectionAsyncActions::FPassportConnectOutputPin* UImtblConnectionAsyncActions::DynamicMulticastDelegate_OnSuccess()
49+
{
50+
return &Internal_DynamicMulticastDelegate_OnSuccess;
51+
}
52+
53+
UImtblConnectionAsyncActions::FPassportConnectOutputPin* UImtblConnectionAsyncActions::DynamicMulticastDelegate_OnFailed()
54+
{
55+
return &Internal_DynamicMulticastDelegate_OnFailed;
56+
}
57+
4858
void UImtblConnectionAsyncActions::DoConnect(TWeakObjectPtr<UImtblJSConnector> JSConnector)
4959
{
5060
auto Passport = GetSubsystem()->GetPassport();
@@ -68,10 +78,10 @@ void UImtblConnectionAsyncActions::OnConnect(FImmutablePassportResult Result)
6878
{
6979
if (Result.Success)
7080
{
71-
Success.Broadcast(TEXT(""));
81+
Internal_DynamicMulticastDelegate_OnSuccess.Broadcast(TEXT(""));
7282
}
7383
else
7484
{
75-
Failed.Broadcast(Result.Error);
85+
Internal_DynamicMulticastDelegate_OnFailed.Broadcast(Result.Error);
7686
}
7787
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#include "Immutable/Actions/ImtblEmbeddedLoginAsyncAction.h"
2+
3+
#include "Immutable/Actions/ImtblConnectImxAsyncAction.h"
4+
#include "Immutable/Browser/ImmutableJSConnectorBrowserWidget.h"
5+
6+
UImtblEmbeddedLoginAsyncAction* UImtblEmbeddedLoginAsyncAction::Login(UObject* WorldContextObject, UImmutableJSConnectorBrowserWidget* JSConnectorBrowserWidget)
7+
{
8+
ThisClass* Action = NewObject<ThisClass>();
9+
10+
if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
11+
{
12+
Action->BindedWorld = World;
13+
Action->BindedJSConnectorBrowserWidget = JSConnectorBrowserWidget;
14+
Action->RegisterWithGameInstance(WorldContextObject);
15+
}
16+
17+
return Action;
18+
}
19+
20+
void UImtblEmbeddedLoginAsyncAction::Activate()
21+
{
22+
Super::Activate();
23+
24+
if (UWorld* World = BindedWorld.Get())
25+
{
26+
if (UImmutableJSConnectorBrowserWidget* JSConnectorBrowserWidget = BindedJSConnectorBrowserWidget.Get())
27+
{
28+
JSConnectorBrowserWidget->GetJSConnector()->MulticastDelegate_OnEventToGame()->AddWeakLambda(this, [this, World, JSConnectorBrowserWidget](const FString& Event, const FString& Message, const TOptional<FImtblJSResponse>& Response)
29+
{
30+
if (Event == TEXT("HandleLoginData") && Response.IsSet())
31+
{
32+
FImmutableDirectLoginOptions DirectLoginOptions;
33+
if (UImmutableDirectLoginOptionsStatics::FromJSResponse(Response.GetValue(), DirectLoginOptions))
34+
{
35+
if ((LoginAsyncAction = UImtblConnectionAsyncActions::Login(World, DirectLoginOptions)))
36+
{
37+
LoginAsyncAction->DynamicMulticastDelegate_OnSuccess()->AddDynamic(this, &ThisClass::LoginAsyncAction_DynamicMulticastDelegate_OnSuccess);
38+
LoginAsyncAction->DynamicMulticastDelegate_OnFailed()->AddDynamic(this, &ThisClass::LoginAsyncAction_DynamicMulticastDelegate_OnFailed);
39+
LoginAsyncAction->Activate();
40+
}
41+
else
42+
{
43+
SetReadyToDestroy();
44+
}
45+
}
46+
}
47+
else if (Event == TEXT("HandleClose"))
48+
{
49+
JSConnectorBrowserWidget->LoadURL(TEXT("about:blank"));
50+
Internal_Closed_MulticastDelegate.Broadcast();
51+
Internal_Closed_DynamicMulticastDelegate.Broadcast();
52+
SetReadyToDestroy();
53+
}
54+
});
55+
JSConnectorBrowserWidget->LoadURL(TEXT("https://auth.immutable.com/im-embedded-login-prompt?isWebView=true"));
56+
}
57+
else
58+
{
59+
SetReadyToDestroy();
60+
}
61+
}
62+
else
63+
{
64+
SetReadyToDestroy();
65+
}
66+
}
67+
68+
void UImtblEmbeddedLoginAsyncAction::SetReadyToDestroy()
69+
{
70+
if (LoginAsyncAction)
71+
{
72+
LoginAsyncAction->DynamicMulticastDelegate_OnSuccess()->RemoveAll(this);
73+
LoginAsyncAction->DynamicMulticastDelegate_OnFailed()->RemoveAll(this);
74+
LoginAsyncAction->SetReadyToDestroy();
75+
LoginAsyncAction = nullptr;
76+
}
77+
if (UImmutableJSConnectorBrowserWidget* JSConnectorBrowserWidget = BindedJSConnectorBrowserWidget.Get())
78+
{
79+
JSConnectorBrowserWidget->GetJSConnector()->MulticastDelegate_OnEventToGame()->RemoveAll(this);
80+
}
81+
Super::SetReadyToDestroy();
82+
}
83+
84+
UImtblConnectionAsyncActions* UImtblEmbeddedLoginAsyncAction::GetLoginAsyncAction() const
85+
{
86+
return LoginAsyncAction;
87+
}
88+
89+
FImmutableMessageMulticastDelegate* UImtblEmbeddedLoginAsyncAction::LoginFailed_MulticastDelegate()
90+
{
91+
return &Internal_LoginFailed_MulticastDelegate;
92+
}
93+
94+
FImmutableMessageDynamicMulticastDelegate* UImtblEmbeddedLoginAsyncAction::LoginFailed_DynamicMulticastDelegate()
95+
{
96+
return &Internal_LoginFailed_DynamicMulticastDelegate;
97+
}
98+
99+
FSimpleMulticastDelegate* UImtblEmbeddedLoginAsyncAction::LoginSuccess_MulticastDelegate()
100+
{
101+
return &Internal_LoginSuccess_MulticastDelegate;
102+
}
103+
104+
FImmutableSimpleDynamicMulticastDelegate* UImtblEmbeddedLoginAsyncAction::LoginSuccess_DynamicMulticastDelegate()
105+
{
106+
return &Internal_LoginSuccess_DynamicMulticastDelegate;
107+
}
108+
109+
FSimpleMulticastDelegate* UImtblEmbeddedLoginAsyncAction::Closed_MulticastDelegate()
110+
{
111+
return &Internal_Closed_MulticastDelegate;
112+
}
113+
114+
FImmutableSimpleDynamicMulticastDelegate* UImtblEmbeddedLoginAsyncAction::Closed_DynamicMulticastDelegate()
115+
{
116+
return &Internal_Closed_DynamicMulticastDelegate;
117+
}
118+
119+
void UImtblEmbeddedLoginAsyncAction::LoginAsyncAction_DynamicMulticastDelegate_OnSuccess(FString String)
120+
{
121+
Internal_LoginSuccess_MulticastDelegate.Broadcast();
122+
Internal_LoginSuccess_DynamicMulticastDelegate.Broadcast();
123+
SetReadyToDestroy();
124+
}
125+
126+
void UImtblEmbeddedLoginAsyncAction::LoginAsyncAction_DynamicMulticastDelegate_OnFailed(FString String)
127+
{
128+
Internal_LoginFailed_MulticastDelegate.Broadcast(String);
129+
Internal_LoginFailed_DynamicMulticastDelegate.Broadcast(String);
130+
SetReadyToDestroy();
131+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#include "Immutable/Browser/ImmutableBaseBrowserWidget.h"
2+
3+
#include "Immutable/Misc/ImtblLogging.h"
4+
5+
#define LOCTEXT_NAMESPACE "BaseBrowserWidget"
6+
7+
void UImmutableBaseBrowserWidget::ReleaseSlateResources(bool bReleaseChildren)
8+
{
9+
Super::ReleaseSlateResources(bReleaseChildren);
10+
11+
#if USING_BUNDLED_CEF
12+
WebBrowserWidget.Reset();
13+
#endif
14+
}
15+
16+
bool UImmutableBaseBrowserWidget::IsPageLoaded() const
17+
{
18+
#if USING_BUNDLED_CEF
19+
return WebBrowserWidget.IsValid() && WebBrowserWidget->IsLoaded();
20+
#endif
21+
return false;
22+
}
23+
24+
void UImmutableBaseBrowserWidget::LoadURL(FString NewURL) const
25+
{
26+
#if USING_BUNDLED_CEF
27+
if (WebBrowserWidget.IsValid())
28+
{
29+
return WebBrowserWidget->LoadURL(NewURL);
30+
}
31+
#endif
32+
}
33+
34+
void UImmutableBaseBrowserWidget::LoadString(FString Contents, FString DummyURL)
35+
{
36+
#if USING_BUNDLED_CEF
37+
if (WebBrowserWidget.IsValid())
38+
{
39+
WebBrowserWidget->LoadString(Contents, DummyURL);
40+
}
41+
#endif
42+
}
43+
44+
FImmutableBrowserConsoleMessageDynamicMulticastDelegate* UImmutableBaseBrowserWidget::DynamicMulticastDelegate_OnConsoleMessage()
45+
{
46+
return &Internal_DynamicMulticastDelegate_OnConsoleMessage;
47+
}
48+
49+
FSimpleMulticastDelegate UImmutableBaseBrowserWidget::MulticastDelegate_OnLoadCompleted()
50+
{
51+
return Internal_MulticastDelegate_OnLoadCompleted;
52+
}
53+
54+
FSimpleMulticastDelegate* UImmutableBaseBrowserWidget::MulticastDelegate_OnBrowserCreated()
55+
{
56+
return &Internal_MulticastDelegate_OnBrowserCreated;
57+
}
58+
59+
TSharedRef<SWidget> UImmutableBaseBrowserWidget::RebuildWidget()
60+
{
61+
if (IsDesignTime())
62+
{
63+
// Show placeholder in editor
64+
return SNew(SBox)
65+
.HAlign(HAlign_Center)
66+
.VAlign(VAlign_Center)
67+
[
68+
SNew(STextBlock)
69+
.Text(LOCTEXT("BrowserPlaceholder", "Browser Widget"))
70+
];
71+
}
72+
else
73+
{
74+
#if USING_BUNDLED_CEF
75+
// Create the web browser widget
76+
WebBrowserWidget = SNew(SWebBrowser)
77+
.InitialURL(InitialURL)
78+
.ShowControls(false)
79+
.SupportsTransparency(bSupportsTransparency)
80+
.ShowInitialThrobber(bShowInitialThrobber)
81+
#if PLATFORM_ANDROID || PLATFORM_IOS
82+
.OnLoadCompleted(BIND_UOBJECT_DELEGATE(FSimpleDelegate, HandleOnLoadCompleted))
83+
#endif
84+
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1
85+
.OnConsoleMessage(BIND_UOBJECT_DELEGATE(FOnConsoleMessageDelegate, HandleOnConsoleMessage))
86+
#endif
87+
;
88+
89+
return WebBrowserWidget.ToSharedRef();
90+
#else
91+
// Fallback for non-CEF builds
92+
return
93+
SNew(SBox)
94+
.HAlign(HAlign_Center)
95+
.VAlign(VAlign_Center)
96+
[
97+
SNew(STextBlock)
98+
.Text(LOCTEXT("NoCEF", "Browser Not Available"))
99+
];
100+
#endif
101+
}
102+
}
103+
104+
void UImmutableBaseBrowserWidget::OnWidgetRebuilt()
105+
{
106+
Super::OnWidgetRebuilt();
107+
108+
OnBrowserCreated();
109+
}
110+
111+
#if PLATFORM_ANDROID || PLATFORM_IOS
112+
void UImmutableBaseBrowserWidget::HandleOnLoadCompleted()
113+
{
114+
// Default mobile load handling
115+
HandleLoadCompleted();
116+
}
117+
#endif
118+
119+
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1
120+
void UImmutableBaseBrowserWidget::HandleOnConsoleMessage(const FString& Message, const FString& Source, int32 Line, EWebBrowserConsoleLogSeverity Severity)
121+
{
122+
UE_LOG(LogImmutable, Log, TEXT("Browser console message: %s, Source: %s, Line: %d, Severity: %d"), *Message, *Source, Line, Severity);
123+
HandleConsoleMessage(Message, Source, Line, static_cast<int32>(Severity));
124+
}
125+
#endif
126+
127+
void UImmutableBaseBrowserWidget::HandleLoadCompleted()
128+
{
129+
Internal_MulticastDelegate_OnLoadCompleted.Broadcast();
130+
}
131+
132+
void UImmutableBaseBrowserWidget::HandleConsoleMessage(const FString& Message, const FString& Source, int32 Line, int32 Severity)
133+
{
134+
Internal_DynamicMulticastDelegate_OnConsoleMessage.Broadcast(Message, Source, Line, Severity);
135+
}
136+
137+
void UImmutableBaseBrowserWidget::OnBrowserCreated()
138+
{
139+
Internal_MulticastDelegate_OnBrowserCreated.Broadcast();
140+
}
141+
142+
#undef LOCTEXT_NAMESPACE
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#include "Immutable/Browser/ImmutableJSConnectorBrowserWidget.h"
2+
3+
#include "Immutable/ImtblJSConnector.h"
4+
5+
void UImmutableJSConnectorBrowserWidget::PostInitProperties()
6+
{
7+
Super::PostInitProperties();
8+
9+
if (!IsTemplate())
10+
{
11+
JSConnector = NewObject<UImtblJSConnector>(this);
12+
JSConnector->ExecuteJs.BindUObject(this, &ThisClass::ExecuteJavaScript);
13+
}
14+
}
15+
16+
UImtblJSConnector* UImmutableJSConnectorBrowserWidget::GetJSConnector() const
17+
{
18+
return JSConnector;
19+
}
20+
21+
void UImmutableJSConnectorBrowserWidget::ExecuteJavaScript(const FString& ScriptText) const
22+
{
23+
#if USING_BUNDLED_CEF
24+
if (WebBrowserWidget.IsValid())
25+
{
26+
WebBrowserWidget->ExecuteJavascript(ScriptText);
27+
}
28+
#endif
29+
}
30+
31+
bool UImmutableJSConnectorBrowserWidget::BindUObject(const FString& Name, UObject* Object, bool bIsPermanent) const
32+
{
33+
#if USING_BUNDLED_CEF
34+
if (!WebBrowserWidget.IsValid() || !Object)
35+
{
36+
UE_LOG(LogImmutable, Warning, TEXT("Could not bind UObject '%s' to browser, WebBrowserWidget is null"), *Object->GetName());
37+
return false;
38+
}
39+
40+
WebBrowserWidget->BindUObject(Name, Object, bIsPermanent);
41+
return true;
42+
#endif
43+
return false;
44+
}
45+
46+
void UImmutableJSConnectorBrowserWidget::OnBrowserCreated()
47+
{
48+
Super::OnBrowserCreated();
49+
50+
SetupJavaScriptBindings();
51+
}
52+
53+
void UImmutableJSConnectorBrowserWidget::SetupJavaScriptBindings()
54+
{
55+
if (JSConnector && JSConnector->IsBound())
56+
{
57+
return;
58+
}
59+
60+
if (JSConnector)
61+
{
62+
if (BindUObject(UImtblJSConnector::JSObjectName(), JSConnector))
63+
{
64+
JSConnector->Init(IsPageLoaded());
65+
}
66+
}
67+
}

0 commit comments

Comments
 (0)