react-native-test-app/windows/ReactTestApp/ReactInstance.cpp

224 строки
6.3 KiB
C++

//
// Copyright (c) Microsoft Corporation
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
//
#include "pch.h"
#include "ReactInstance.h"
#include <NativeModules.h>
#include <filesystem>
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.Web.Http.Headers.h>
#include "AutolinkedNativeModules.g.h"
#include "ReactPackageProvider.h"
using ReactTestApp::ReactInstance;
using winrt::ReactTestApp::implementation::ReactPackageProvider;
using winrt::Windows::Foundation::IAsyncOperation;
using winrt::Windows::Foundation::IInspectable;
using winrt::Windows::Foundation::PropertyValue;
using winrt::Windows::Foundation::Uri;
using winrt::Windows::Storage::ApplicationData;
using winrt::Windows::Web::Http::HttpClient;
#if REACT_NATIVE_VERSION >= 6400
using winrt::Microsoft::ReactNative::InstanceLoadedEventArgs;
#endif // REACT_NATIVE_VERSION >= 6400
namespace
{
winrt::hstring const kBreakOnFirstLine = L"breakOnFirstLine";
winrt::hstring const kUseDirectDebugger = L"useDirectDebugger";
winrt::hstring const kUseFastRefresh = L"useFastRefresh";
winrt::hstring const kUseWebDebugger = L"useWebDebugger";
bool RetrieveLocalSetting(winrt::hstring const &key, bool defaultValue)
{
auto localSettings = ApplicationData::Current().LocalSettings();
auto values = localSettings.Values();
return winrt::unbox_value_or<bool>(values.Lookup(key), defaultValue);
}
void StoreLocalSetting(winrt::hstring const &key, bool value)
{
auto localSettings = ApplicationData::Current().LocalSettings();
auto values = localSettings.Values();
values.Insert(key, PropertyValue::CreateBoolean(value));
}
} // namespace
std::vector<std::wstring> const ReactTestApp::JSBundleNames = {
L"index.windows",
L"main.windows",
L"index.native",
L"main.native",
L"index",
L"main",
};
ReactInstance::ReactInstance()
{
reactNativeHost_.PackageProviders().Append(winrt::make<ReactPackageProvider>());
winrt::Microsoft::ReactNative::RegisterAutolinkedNativeModulePackages(
reactNativeHost_.PackageProviders());
#if REACT_NATIVE_VERSION >= 6400
reactNativeHost_.InstanceSettings().InstanceLoaded(
[this](IInspectable const & /*sender*/, InstanceLoadedEventArgs const &args) {
context_ = args.Context();
});
#endif // REACT_NATIVE_VERSION >= 6400
}
bool ReactInstance::LoadJSBundleFrom(JSBundleSource source)
{
source_ = source;
auto instanceSettings = reactNativeHost_.InstanceSettings();
switch (source) {
case JSBundleSource::DevServer:
#if REACT_NATIVE_VERSION > 0 && REACT_NATIVE_VERSION < 6400
instanceSettings.JavaScriptMainModuleName(L"index");
instanceSettings.JavaScriptBundleFile(L"");
#else
instanceSettings.JavaScriptBundleFile(L"index");
#endif
break;
case JSBundleSource::Embedded:
auto const bundleName = GetBundleName();
if (!bundleName.has_value()) {
return false;
}
instanceSettings.JavaScriptBundleFile(bundleName.value());
break;
}
Reload();
return true;
}
void ReactInstance::Reload()
{
auto instanceSettings = reactNativeHost_.InstanceSettings();
instanceSettings.UseWebDebugger(UseWebDebugger());
instanceSettings.UseDirectDebugger(UseDirectDebugger());
auto useFastRefresh = UseFastRefresh();
instanceSettings.UseFastRefresh(useFastRefresh);
instanceSettings.UseLiveReload(useFastRefresh);
instanceSettings.EnableDeveloperMenu(!UseCustomDeveloperMenu());
#ifdef _DEBUG
instanceSettings.UseDeveloperSupport(true);
#else
instanceSettings.UseDeveloperSupport(false);
#endif
reactNativeHost_.ReloadInstance();
}
bool ReactInstance::BreakOnFirstLine() const
{
return RetrieveLocalSetting(kBreakOnFirstLine, false);
}
void ReactInstance::BreakOnFirstLine(bool breakOnFirstLine)
{
StoreLocalSetting(kBreakOnFirstLine, breakOnFirstLine);
Reload();
}
void ReactInstance::ToggleElementInspector() const
{
if (!context_) {
return;
}
context_.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", L"toggleElementInspector");
}
bool ReactInstance::UseCustomDeveloperMenu() const
{
// `ReactInstanceSettings::InstanceLoaded` was introduced in 0.64. Before
// then, the only other way to retrieve `ReactContext` is by overriding the
// `DevSettings` module, but this breaks web debugging. `ReactContext` is
// needed for being able to toggle the element inspector.
#if REACT_NATIVE_VERSION >= 6400
return true;
#else
return false;
#endif // REACT_NATIVE_VERSION >= 6400
}
bool ReactInstance::UseDirectDebugger() const
{
return RetrieveLocalSetting(kUseDirectDebugger, false);
}
void ReactInstance::UseDirectDebugger(bool useDirectDebugger)
{
if (useDirectDebugger) {
// Remote debugging is incompatible with direct debugging
StoreLocalSetting(kUseWebDebugger, false);
}
StoreLocalSetting(kUseDirectDebugger, useDirectDebugger);
Reload();
}
bool ReactInstance::UseFastRefresh() const
{
return IsFastRefreshAvailable() && RetrieveLocalSetting(kUseFastRefresh, true);
}
void ReactInstance::UseFastRefresh(bool useFastRefresh)
{
StoreLocalSetting(kUseFastRefresh, useFastRefresh);
Reload();
}
bool ReactInstance::UseWebDebugger() const
{
return IsWebDebuggerAvailable() && RetrieveLocalSetting(kUseWebDebugger, false);
}
void ReactInstance::UseWebDebugger(bool useWebDebugger)
{
if (useWebDebugger) {
// Remote debugging is incompatible with direct debugging
StoreLocalSetting(kUseDirectDebugger, false);
}
StoreLocalSetting(kUseWebDebugger, useWebDebugger);
Reload();
}
std::optional<std::wstring> ReactTestApp::GetBundleName()
{
for (auto &&main : JSBundleNames) {
if (std::filesystem::exists(L"Bundle\\" + main + L".bundle")) {
return main;
}
}
return std::nullopt;
}
IAsyncOperation<bool> ReactTestApp::IsDevServerRunning()
{
Uri uri(L"http://localhost:8081/status");
HttpClient httpClient;
try {
auto r = co_await httpClient.GetAsync(uri);
co_return r.IsSuccessStatusCode();
} catch (winrt::hresult_error &) {
co_return false;
}
}