Skip to content

Commit

Permalink
qml: added onboarding logic to allow for custom datadir setting
Browse files Browse the repository at this point in the history
  • Loading branch information
D33r-Gee committed Jun 19, 2024
1 parent 5cbebf4 commit aebfc89
Showing 1 changed file with 201 additions and 90 deletions.
291 changes: 201 additions & 90 deletions src/qml/bitcoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickWindow>
#include <QSettings>
#include <QString>
#include <QStyleHints>
#include <QUrl>
Expand Down Expand Up @@ -77,7 +78,7 @@ void SetupUIArgs(ArgsManager& argsman)
argsman.AddArg("-resetguisettings", "Reset all settings changed in the GUI", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
}

AppMode SetupAppMode()
AppMode* SetupAppMode()
{
bool wallet_enabled;
AppMode::Mode mode;
Expand All @@ -93,7 +94,7 @@ AppMode SetupAppMode()
wallet_enabled = false;
#endif // ENABLE_WALLET

return AppMode(mode, wallet_enabled);
return new AppMode(mode, wallet_enabled);
}

bool InitErrorMessageBox(
Expand All @@ -103,9 +104,9 @@ bool InitErrorMessageBox(
{
QQmlApplicationEngine engine;

AppMode app_mode = SetupAppMode();
AppMode* app_mode = SetupAppMode();

qmlRegisterSingletonInstance<AppMode>("org.bitcoincore.qt", 1, 0, "AppMode", &app_mode);
qmlRegisterSingletonInstance<AppMode>("org.bitcoincore.qt", 1, 0, "AppMode", app_mode);
engine.rootContext()->setContextProperty("message", QString::fromStdString(message.translated));
engine.load(QUrl(QStringLiteral("qrc:///qml/pages/initerrormessage.qml")));
if (engine.rootObjects().isEmpty()) {
Expand Down Expand Up @@ -158,6 +159,188 @@ void setupChainQSettings(QGuiApplication* app, QString chain)
app->setApplicationName(QAPP_APP_NAME_REGTEST);
}
}

// added this function to set custom data directory when starting node
bool setCustomDataDir(QString strDataDir)
{
if(fs::exists(GUIUtil::QStringToPath(strDataDir))){
gArgs.SoftSetArg("-datadir", fs::PathToString(GUIUtil::QStringToPath(strDataDir)));
gArgs.ClearPathCache();
return true;
} else {
return false;
}
}

QGuiApplication* m_app;
QQmlApplicationEngine* m_engine;
// ArgsManager* m_gArgs;
QQuickWindow* m_window;
boost::signals2::connection m_handler_message_box;
std::unique_ptr<interfaces::Init> m_init;
std::unique_ptr<interfaces::Node> m_node;
std::unique_ptr<interfaces::Chain> m_chain;
NodeModel* m_node_model{nullptr};
InitExecutor* m_executor{nullptr};
ChainModel* m_chain_model{nullptr};
OptionsQmlModel* m_options_model{nullptr};
// OnboardingModel* m_onboarding_model{nullptr};
int m_argc;
char** m_argv;
bool m_prune;
int m_prune_size_gb;
NetworkTrafficTower* m_network_traffic_tower;
PeerTableModel* m_peer_model;
PeerListSortProxy* m_peer_model_sort_proxy;
QThread* m_introThread;
bool m_isOnboarded;

bool createNode(QGuiApplication& app, QQmlApplicationEngine& engine, int& argc, char* argv[], ArgsManager& gArgs)
{
m_engine = &engine;

InitLogging(gArgs);
InitParameterInteraction(gArgs);

m_init = interfaces::MakeGuiInit(argc, argv);

m_node = m_init->makeNode();
m_chain = m_init->makeChain();

if (!m_node->baseInitialize()) {
// A dialog with detailed error will have been shown by InitError().
return EXIT_FAILURE;
}

m_handler_message_box.disconnect();

m_node_model = new NodeModel{*m_node};
m_executor = new InitExecutor{*m_node};
QObject::connect(m_node_model, &NodeModel::requestedInitialize, m_executor, &InitExecutor::initialize);
QObject::connect(m_node_model, &NodeModel::requestedShutdown, m_executor, &InitExecutor::shutdown);
QObject::connect(m_executor, &InitExecutor::initializeResult, m_node_model, &NodeModel::initializeResult);
QObject::connect(m_executor, &InitExecutor::shutdownResult, qGuiApp, &QGuiApplication::quit, Qt::QueuedConnection);

m_network_traffic_tower = new NetworkTrafficTower{*m_node_model};
#ifdef __ANDROID__
AndroidNotifier android_notifier{*m_node_model};
#endif

m_chain_model = new ChainModel{*m_chain};
m_chain_model->setCurrentNetworkName(QString::fromStdString(ChainTypeToString(gArgs.GetChainType())));
setupChainQSettings(m_app, m_chain_model->currentNetworkName());

QObject::connect(m_node_model, &NodeModel::setTimeRatioList, m_chain_model, &ChainModel::setTimeRatioList);
QObject::connect(m_node_model, &NodeModel::setTimeRatioListInitial, m_chain_model, &ChainModel::setTimeRatioListInitial);

qGuiApp->setQuitOnLastWindowClosed(false);
QObject::connect(qGuiApp, &QGuiApplication::lastWindowClosed, [&] {
m_node->startShutdown();
});

m_peer_model = new PeerTableModel{*m_node, nullptr};
m_peer_model_sort_proxy = new PeerListSortProxy{nullptr};
m_peer_model_sort_proxy->setSourceModel(m_peer_model);

m_engine->rootContext()->setContextProperty("networkTrafficTower", m_network_traffic_tower);
m_engine->rootContext()->setContextProperty("nodeModel", m_node_model);
m_engine->rootContext()->setContextProperty("chainModel", m_chain_model);
m_engine->rootContext()->setContextProperty("peerTableModel", m_peer_model);
m_engine->rootContext()->setContextProperty("peerListModelProxy", m_peer_model_sort_proxy);

m_options_model->setNode(&(*m_node), m_isOnboarded);

QObject::connect(m_options_model, &OptionsQmlModel::requestedShutdown, m_executor, &InitExecutor::shutdown);

m_engine->rootContext()->setContextProperty("optionsModel", m_options_model);

m_node_model->startShutdownPolling();

return true;
}

void startNodeAndTransitionSlot() { createNode(*m_app, *m_engine, m_argc, m_argv, gArgs); }

bool startNode(QGuiApplication& app, QQmlApplicationEngine& engine, int& argc, char* argv[])
{
m_engine = &engine;
QScopedPointer<const NetworkStyle> network_style{NetworkStyle::instantiate(Params().GetChainType())};
assert(!network_style.isNull());
m_engine->addImageProvider(QStringLiteral("images"), new ImageProvider{network_style.data()});

m_isOnboarded = true;

m_options_model = new OptionsQmlModel{nullptr, m_isOnboarded};
m_engine->rootContext()->setContextProperty("optionsModel", m_options_model);

// moved this so that the settings.json file is read and parsed before creating the node
std::string error;
/// Read and parse settings.json file.
std::vector<std::string> errors;
if (!gArgs.ReadSettingsFile(&errors)) {
error = strprintf("Failed loading settings file:\n%s\n", MakeUnorderedList(errors));
InitError(Untranslated(error));
return EXIT_FAILURE;
}

createNode(*m_app, *m_engine, argc, argv, gArgs);

AppMode* app_mode = SetupAppMode();

qmlRegisterSingletonInstance<AppMode>("org.bitcoincore.qt", 1, 0, "AppMode", app_mode);
qmlRegisterType<BlockClockDial>("org.bitcoincore.qt", 1, 0, "BlockClockDial");
qmlRegisterType<LineGraph>("org.bitcoincore.qt", 1, 0, "LineGraph");

m_engine->load(QUrl(QStringLiteral("qrc:///qml/pages/main.qml")));
if (m_engine->rootObjects().isEmpty()) {
return EXIT_FAILURE;
}

auto window = qobject_cast<QQuickWindow*>(engine.rootObjects().first());
if (!window) {
return EXIT_FAILURE;
}

// Install qDebug() message handler to route to debug.log
qInstallMessageHandler(DebugMessageHandler);

qInfo() << "Graphics API in use:" << QmlUtil::GraphicsApi(window);

return qGuiApp->exec();
}

bool startOnboarding(QGuiApplication& app, QQmlApplicationEngine& engine, ArgsManager& gArgs)
{
m_engine = &engine;
QScopedPointer<const NetworkStyle> network_style{NetworkStyle::instantiate(Params().GetChainType())};
assert(!network_style.isNull());
m_engine->addImageProvider(QStringLiteral("images"), new ImageProvider{network_style.data()});

m_isOnboarded = false;

m_options_model = new OptionsQmlModel{nullptr, m_isOnboarded};
m_engine->rootContext()->setContextProperty("optionsModel", m_options_model);

QObject::connect(m_options_model, &OptionsQmlModel::onboardingFinished, startNodeAndTransitionSlot);

AppMode* app_mode = SetupAppMode();

qmlRegisterSingletonInstance<AppMode>("org.bitcoincore.qt", 1, 0, "AppMode", app_mode);
qmlRegisterType<BlockClockDial>("org.bitcoincore.qt", 1, 0, "BlockClockDial");
qmlRegisterType<LineGraph>("org.bitcoincore.qt", 1, 0, "LineGraph");

m_engine->load(QUrl(QStringLiteral("qrc:///qml/pages/main.qml")));
if (m_engine->rootObjects().isEmpty()) {
return EXIT_FAILURE;
}

auto window = qobject_cast<QQuickWindow*>(m_engine->rootObjects().first());
if (!window) {
return EXIT_FAILURE;
}

return qGuiApp->exec();
}
} // namespace


Expand All @@ -175,9 +358,7 @@ int QmlGuiMain(int argc, char* argv[])
QGuiApplication::styleHints()->setTabFocusBehavior(Qt::TabFocusAllControls);
QGuiApplication app(argc, argv);

auto handler_message_box = ::uiInterface.ThreadSafeMessageBox_connect(InitErrorMessageBox);

std::unique_ptr<interfaces::Init> init = interfaces::MakeGuiInit(argc, argv);
auto m_handler_message_box = ::uiInterface.ThreadSafeMessageBox_connect(InitErrorMessageBox);

SetupEnvironment();
util::ThreadSetInternalName("main");
Expand All @@ -189,6 +370,10 @@ int QmlGuiMain(int argc, char* argv[])
app.setOrganizationDomain(QAPP_ORG_DOMAIN);
app.setApplicationName(QAPP_APP_NAME_DEFAULT);

QSettings settings;
QString dataDir;
dataDir = settings.value("strDataDir", dataDir).toString();

/// Parse command-line options. We do this after qt in order to show an error if there are problems parsing these.
SetupServerArgs(gArgs);
SetupUIArgs(gArgs);
Expand Down Expand Up @@ -218,16 +403,9 @@ int QmlGuiMain(int argc, char* argv[])
return EXIT_FAILURE;
}

/// Read and parse settings.json file.
std::vector<std::string> errors;
if (!gArgs.ReadSettingsFile(&errors)) {
error = strprintf("Failed loading settings file:\n%s\n", MakeUnorderedList(errors));
InitError(Untranslated(error));
return EXIT_FAILURE;
}

QVariant need_onboarding(true);
if (gArgs.IsArgSet("-datadir") && !gArgs.GetPathArg("-datadir").empty()) {
if ((gArgs.IsArgSet("-datadir") && !gArgs.GetPathArg("-datadir").empty()) || fs::exists(GUIUtil::QStringToPath(dataDir)) ) {
setCustomDataDir(dataDir);
need_onboarding.setValue(false);
} else if (ConfigurationFileExists(gArgs)) {
need_onboarding.setValue(false);
Expand All @@ -240,89 +418,22 @@ int QmlGuiMain(int argc, char* argv[])
// Default printtoconsole to false for the GUI. GUI programs should not
// print to the console unnecessarily.
gArgs.SoftSetBoolArg("-printtoconsole", false);
InitLogging(gArgs);
InitParameterInteraction(gArgs);

GUIUtil::LogQtInfo();

std::unique_ptr<interfaces::Node> node = init->makeNode();
std::unique_ptr<interfaces::Chain> chain = init->makeChain();
if (!node->baseInitialize()) {
// A dialog with detailed error will have been shown by InitError().
return EXIT_FAILURE;
}

handler_message_box.disconnect();

NodeModel node_model{*node};
InitExecutor init_executor{*node};
QObject::connect(&node_model, &NodeModel::requestedInitialize, &init_executor, &InitExecutor::initialize);
QObject::connect(&node_model, &NodeModel::requestedShutdown, &init_executor, &InitExecutor::shutdown);
QObject::connect(&init_executor, &InitExecutor::initializeResult, &node_model, &NodeModel::initializeResult);
QObject::connect(&init_executor, &InitExecutor::shutdownResult, qGuiApp, &QGuiApplication::quit, Qt::QueuedConnection);
// QObject::connect(&init_executor, &InitExecutor::runawayException, &node_model, &NodeModel::handleRunawayException);

NetworkTrafficTower network_traffic_tower{node_model};
#ifdef __ANDROID__
AndroidNotifier android_notifier{node_model};
#endif

ChainModel chain_model{*chain};
chain_model.setCurrentNetworkName(QString::fromStdString(ChainTypeToString(gArgs.GetChainType())));
setupChainQSettings(&app, chain_model.currentNetworkName());

QObject::connect(&node_model, &NodeModel::setTimeRatioList, &chain_model, &ChainModel::setTimeRatioList);
QObject::connect(&node_model, &NodeModel::setTimeRatioListInitial, &chain_model, &ChainModel::setTimeRatioListInitial);

qGuiApp->setQuitOnLastWindowClosed(false);
QObject::connect(qGuiApp, &QGuiApplication::lastWindowClosed, [&] {
node->startShutdown();
});

PeerTableModel peer_model{*node, nullptr};
PeerListSortProxy peer_model_sort_proxy{nullptr};
peer_model_sort_proxy.setSourceModel(&peer_model);

GUIUtil::LoadFont(":/fonts/inter/regular");
GUIUtil::LoadFont(":/fonts/inter/semibold");

QQmlApplicationEngine engine;
m_app = &app;

QScopedPointer<const NetworkStyle> network_style{NetworkStyle::instantiate(Params().GetChainType())};
assert(!network_style.isNull());
engine.addImageProvider(QStringLiteral("images"), new ImageProvider{network_style.data()});

engine.rootContext()->setContextProperty("networkTrafficTower", &network_traffic_tower);
engine.rootContext()->setContextProperty("nodeModel", &node_model);
engine.rootContext()->setContextProperty("chainModel", &chain_model);
engine.rootContext()->setContextProperty("peerTableModel", &peer_model);
engine.rootContext()->setContextProperty("peerListModelProxy", &peer_model_sort_proxy);
QQmlApplicationEngine engine;

OptionsQmlModel options_model(*node, !need_onboarding.toBool());
engine.rootContext()->setContextProperty("optionsModel", &options_model);
engine.rootContext()->setContextProperty("needOnboarding", need_onboarding);

AppMode app_mode = SetupAppMode();

qmlRegisterSingletonInstance<AppMode>("org.bitcoincore.qt", 1, 0, "AppMode", &app_mode);
qmlRegisterType<BlockClockDial>("org.bitcoincore.qt", 1, 0, "BlockClockDial");
qmlRegisterType<LineGraph>("org.bitcoincore.qt", 1, 0, "LineGraph");

engine.load(QUrl(QStringLiteral("qrc:///qml/pages/main.qml")));
if (engine.rootObjects().isEmpty()) {
return EXIT_FAILURE;
}

auto window = qobject_cast<QQuickWindow*>(engine.rootObjects().first());
if (!window) {
return EXIT_FAILURE;
if(need_onboarding.toBool()) {
startOnboarding(*m_app, engine, gArgs);
} else {
startNode(*m_app, engine, argc, argv);
}

// Install qDebug() message handler to route to debug.log
qInstallMessageHandler(DebugMessageHandler);

qInfo() << "Graphics API in use:" << QmlUtil::GraphicsApi(window);

node_model.startShutdownPolling();
return qGuiApp->exec();
return 0;
}

0 comments on commit aebfc89

Please sign in to comment.