1
Fork 0
mirror of https://github.com/redstrate/Astra.git synced 2025-06-08 06:57:46 +00:00

Use KConfig's own class instead of "proxying" it in Account

This will make it trivial to add more account settings in the future.
This commit is contained in:
Joshua Goins 2025-03-17 18:27:17 -04:00
parent 799b718818
commit da3084266c
12 changed files with 107 additions and 279 deletions

View file

@ -90,10 +90,10 @@ target_link_libraries(astra_static PUBLIC
QCoro::Core QCoro::Core
QCoro::Network QCoro::Network
QCoro::Qml) QCoro::Qml)
kconfig_target_kcfg_file(astra_static FILE config.kcfg CLASS_NAME Config MUTATORS GENERATE_PROPERTIES GENERATE_MOC DEFAULT_VALUE_GETTERS PARENT_IN_CONSTRUCTOR QML_REGISTRATION QML_UNCREATABLE) kconfig_target_kcfg_file(astra_static FILE config.kcfg CLASS_NAME Config MUTATORS GENERATE_PROPERTIES GENERATE_MOC DEFAULT_VALUE_GETTERS PARENT_IN_CONSTRUCTOR QML_REGISTRATION QML_UNCREATABLE USE_ENUM_TYPES)
kconfig_target_kcfg_file(astra_static FILE accountconfig.kcfg CLASS_NAME AccountConfig MUTATORS GENERATE_PROPERTIES GENERATE_MOC DEFAULT_VALUE_GETTERS PARENT_IN_CONSTRUCTOR QML_REGISTRATION QML_UNCREATABLE) kconfig_target_kcfg_file(astra_static FILE accountconfig.kcfg CLASS_NAME AccountConfig MUTATORS GENERATE_PROPERTIES GENERATE_MOC DEFAULT_VALUE_GETTERS PARENT_IN_CONSTRUCTOR QML_REGISTRATION QML_UNCREATABLE USE_ENUM_TYPES)
kconfig_target_kcfg_file(astra_static FILE profileconfig.kcfg CLASS_NAME ProfileConfig MUTATORS GENERATE_PROPERTIES GENERATE_MOC DEFAULT_VALUE_GETTERS PARENT_IN_CONSTRUCTOR QML_REGISTRATION QML_UNCREATABLE) kconfig_target_kcfg_file(astra_static FILE profileconfig.kcfg CLASS_NAME ProfileConfig MUTATORS GENERATE_PROPERTIES GENERATE_MOC DEFAULT_VALUE_GETTERS PARENT_IN_CONSTRUCTOR QML_REGISTRATION QML_UNCREATABLE)
target_include_directories(astra_static PUBLIC include ${CMAKE_BINARY_DIR}) target_include_directories(astra_static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_BINARY_DIR})
if (NOT MSVC) if (NOT MSVC)
target_compile_options(astra_static PUBLIC -fexceptions) target_compile_options(astra_static PUBLIC -fexceptions)

View file

@ -11,6 +11,7 @@ SPDX-License-Identifier: CC0-1.0
<kcfgfile name="astrastaterc" stateConfig="true"> <kcfgfile name="astrastaterc" stateConfig="true">
<parameter name="uuid"/> <parameter name="uuid"/>
</kcfgfile> </kcfgfile>
<include>account.h</include>
<group name="account-$(uuid)"> <group name="account-$(uuid)">
<entry key="Name" type="string"> <entry key="Name" type="string">
</entry> </entry>
@ -50,7 +51,7 @@ SPDX-License-Identifier: CC0-1.0
<default>false</default> <default>false</default>
</entry> </entry>
<entry name="License" type="Enum"> <entry name="License" type="Enum">
<choices> <choices name="Account::GameLicense">
<choice name="WindowsStandalone"> <choice name="WindowsStandalone">
</choice> </choice>
<choice name="WindowsSteam"> <choice name="WindowsSteam">

View file

@ -6,7 +6,7 @@
#include <QtQml> #include <QtQml>
#include <qcorotask.h> #include <qcorotask.h>
#include "accountconfig.h" class AccountConfig;
class Account : public QObject class Account : public QObject
{ {
@ -14,59 +14,24 @@ class Account : public QObject
QML_ELEMENT QML_ELEMENT
QML_UNCREATABLE("Use from AccountManager") QML_UNCREATABLE("Use from AccountManager")
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(AccountConfig *config READ config CONSTANT)
Q_PROPERTY(int language READ language WRITE setLanguage NOTIFY languageChanged)
Q_PROPERTY(QString lodestoneId READ lodestoneId WRITE setLodestoneId NOTIFY lodestoneIdChanged)
Q_PROPERTY(QString avatarUrl READ avatarUrl NOTIFY avatarUrlChanged) Q_PROPERTY(QString avatarUrl READ avatarUrl NOTIFY avatarUrlChanged)
Q_PROPERTY(bool isSapphire READ isSapphire WRITE setIsSapphire NOTIFY isSapphireChanged)
Q_PROPERTY(QString lobbyUrl READ lobbyUrl WRITE setLobbyUrl NOTIFY lobbyUrlChanged)
Q_PROPERTY(bool rememberPassword READ rememberPassword WRITE setRememberPassword NOTIFY rememberPasswordChanged)
Q_PROPERTY(bool rememberOTP READ rememberOTP WRITE setRememberOTP NOTIFY rememberOTPChanged)
Q_PROPERTY(bool useOTP READ useOTP WRITE setUseOTP NOTIFY useOTPChanged)
Q_PROPERTY(GameLicense license READ license WRITE setLicense NOTIFY licenseChanged)
Q_PROPERTY(bool isFreeTrial READ isFreeTrial WRITE setIsFreeTrial NOTIFY isFreeTrialChanged)
Q_PROPERTY(bool needsPassword READ needsPassword NOTIFY needsPasswordChanged) Q_PROPERTY(bool needsPassword READ needsPassword NOTIFY needsPasswordChanged)
public: public:
explicit Account(const QString &key, QObject *parent = nullptr); explicit Account(const QString &key, QObject *parent = nullptr);
enum class GameLicense { WindowsStandalone, WindowsSteam, macOS }; enum GameLicense {
WindowsStandalone,
WindowsSteam,
macOS
};
Q_ENUM(GameLicense) Q_ENUM(GameLicense)
[[nodiscard]] QString uuid() const; [[nodiscard]] QString uuid() const;
[[nodiscard]] QString name() const;
void setName(const QString &name);
[[nodiscard]] int language() const;
void setLanguage(int value);
[[nodiscard]] QString lodestoneId() const;
void setLodestoneId(const QString &id);
[[nodiscard]] QString avatarUrl() const; [[nodiscard]] QString avatarUrl() const;
[[nodiscard]] bool isSapphire() const;
void setIsSapphire(bool value);
[[nodiscard]] QString lobbyUrl() const;
void setLobbyUrl(const QString &url);
[[nodiscard]] bool rememberPassword() const;
void setRememberPassword(bool value);
[[nodiscard]] bool rememberOTP() const;
void setRememberOTP(bool value);
[[nodiscard]] bool useOTP() const;
void setUseOTP(bool value);
[[nodiscard]] GameLicense license() const;
void setLicense(GameLicense license);
[[nodiscard]] bool isFreeTrial() const;
void setIsFreeTrial(bool value);
Q_INVOKABLE QString getPassword(); Q_INVOKABLE QString getPassword();
void setPassword(const QString &password); void setPassword(const QString &password);
@ -81,18 +46,10 @@ public:
[[nodiscard]] bool needsPassword() const; [[nodiscard]] bool needsPassword() const;
AccountConfig *config() const;
Q_SIGNALS: Q_SIGNALS:
void nameChanged();
void languageChanged();
void lodestoneIdChanged();
void avatarUrlChanged(); void avatarUrlChanged();
void isSapphireChanged();
void lobbyUrlChanged();
void rememberPasswordChanged();
void rememberOTPChanged();
void useOTPChanged();
void licenseChanged();
void isFreeTrialChanged();
bool needsPasswordChanged(); bool needsPasswordChanged();
private: private:
@ -108,7 +65,7 @@ private:
*/ */
QCoro::Task<QString> getKeychainValue(const QString &key); QCoro::Task<QString> getKeychainValue(const QString &key);
AccountConfig m_config; AccountConfig *m_config;
QString m_key; QString m_key;
QString m_avatarUrl; QString m_avatarUrl;
bool m_needsPassword = false; bool m_needsPassword = false;

View file

@ -7,6 +7,7 @@
#include <qcorocore.h> #include <qcorocore.h>
#include <qt6keychain/keychain.h> #include <qt6keychain/keychain.h>
#include "accountconfig.h"
#include "astra_log.h" #include "astra_log.h"
#include "utility.h" #include "utility.h"
@ -14,7 +15,7 @@ using namespace Qt::StringLiterals;
Account::Account(const QString &key, QObject *parent) Account::Account(const QString &key, QObject *parent)
: QObject(parent) : QObject(parent)
, m_config(key) , m_config(new AccountConfig(key))
, m_key(key) , m_key(key)
{ {
fetchPassword(); fetchPassword();
@ -25,151 +26,11 @@ QString Account::uuid() const
return m_key; return m_key;
} }
QString Account::name() const
{
return m_config.name();
}
void Account::setName(const QString &name)
{
if (m_config.name() != name) {
m_config.setName(name);
m_config.save();
Q_EMIT nameChanged();
}
}
int Account::language() const
{
return m_config.language();
}
void Account::setLanguage(const int value)
{
if (m_config.language() != value) {
m_config.setLanguage(value);
m_config.save();
Q_EMIT languageChanged();
}
}
QString Account::lodestoneId() const
{
return m_config.lodestoneId();
}
void Account::setLodestoneId(const QString &id)
{
if (m_config.lodestoneId() != id) {
m_config.setLodestoneId(id);
m_config.save();
Q_EMIT lodestoneIdChanged();
}
}
QString Account::avatarUrl() const QString Account::avatarUrl() const
{ {
return m_avatarUrl; return m_avatarUrl;
} }
bool Account::isSapphire() const
{
return m_config.isSapphire();
}
void Account::setIsSapphire(const bool value)
{
if (m_config.isSapphire() != value) {
m_config.setIsSapphire(value);
m_config.save();
Q_EMIT isSapphireChanged();
}
}
QString Account::lobbyUrl() const
{
return m_config.lobbyUrl();
}
void Account::setLobbyUrl(const QString &url)
{
if (m_config.lobbyUrl() != url) {
m_config.setLobbyUrl(url);
m_config.save();
Q_EMIT lobbyUrlChanged();
}
}
bool Account::rememberPassword() const
{
return m_config.rememberPassword();
}
void Account::setRememberPassword(const bool value)
{
if (m_config.rememberPassword() != value) {
m_config.setRememberPassword(value);
m_config.save();
Q_EMIT rememberPasswordChanged();
}
}
bool Account::rememberOTP() const
{
return m_config.rememberOTP();
}
void Account::setRememberOTP(const bool value)
{
if (m_config.rememberOTP() != value) {
m_config.setRememberOTP(value);
m_config.save();
Q_EMIT rememberOTPChanged();
}
}
bool Account::useOTP() const
{
return m_config.useOTP();
}
void Account::setUseOTP(const bool value)
{
if (m_config.useOTP() != value) {
m_config.setUseOTP(value);
m_config.save();
Q_EMIT useOTPChanged();
}
}
Account::GameLicense Account::license() const
{
return static_cast<GameLicense>(m_config.license());
}
void Account::setLicense(const GameLicense license)
{
if (static_cast<GameLicense>(m_config.license()) != license) {
m_config.setLicense(static_cast<int>(license));
m_config.save();
Q_EMIT licenseChanged();
}
}
bool Account::isFreeTrial() const
{
return m_config.isFreeTrial();
}
void Account::setIsFreeTrial(const bool value)
{
if (m_config.isFreeTrial() != value) {
m_config.setIsFreeTrial(value);
m_config.save();
Q_EMIT isFreeTrialChanged();
}
}
QString Account::getPassword() QString Account::getPassword()
{ {
return QCoro::waitFor(getKeychainValue(QStringLiteral("password"))); return QCoro::waitFor(getKeychainValue(QStringLiteral("password")));
@ -291,4 +152,9 @@ QCoro::Task<> Account::fetchPassword()
co_return; co_return;
} }
AccountConfig *Account::config() const
{
return m_config;
}
#include "moc_account.cpp" #include "moc_account.cpp"

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "accountmanager.h" #include "accountmanager.h"
#include "accountconfig.h"
#include "astra_log.h" #include "astra_log.h"
#include <KSharedConfig> #include <KSharedConfig>
@ -57,10 +58,10 @@ QHash<int, QByteArray> AccountManager::roleNames() const
Account *AccountManager::createSquareEnixAccount(const QString &username, const int licenseType, const bool isFreeTrial) Account *AccountManager::createSquareEnixAccount(const QString &username, const int licenseType, const bool isFreeTrial)
{ {
const auto account = new Account(QUuid::createUuid().toString(), this); const auto account = new Account(QUuid::createUuid().toString(), this);
account->setIsSapphire(false); account->config()->setIsSapphire(false);
account->setLicense(static_cast<Account::GameLicense>(licenseType)); account->config()->setLicense(static_cast<Account::GameLicense>(licenseType));
account->setIsFreeTrial(isFreeTrial); account->config()->setIsFreeTrial(isFreeTrial);
account->setName(username); account->config()->setName(username);
insertAccount(account); insertAccount(account);
@ -70,9 +71,9 @@ Account *AccountManager::createSquareEnixAccount(const QString &username, const
Account *AccountManager::createSapphireAccount(const QString &lobbyUrl, const QString &username) Account *AccountManager::createSapphireAccount(const QString &lobbyUrl, const QString &username)
{ {
const auto account = new Account(QUuid::createUuid().toString(), this); const auto account = new Account(QUuid::createUuid().toString(), this);
account->setIsSapphire(true); account->config()->setIsSapphire(true);
account->setName(username); account->config()->setName(username);
account->setLobbyUrl(lobbyUrl); account->config()->setLobbyUrl(lobbyUrl);
insertAccount(account); insertAccount(account);

View file

@ -7,6 +7,7 @@
#include <gamemode_client.h> #include <gamemode_client.h>
#endif #endif
#include "accountconfig.h"
#include "astra_log.h" #include "astra_log.h"
#include "encryptedarg.h" #include "encryptedarg.h"
#include "launchercore.h" #include "launchercore.h"
@ -156,7 +157,7 @@ void GameRunner::beginDalamudGame(const QString &gameExecutablePath, Profile &pr
QStringLiteral("--dalamud-configuration-path=") + Utility::toWindowsPath(dalamudConfigPath), QStringLiteral("--dalamud-configuration-path=") + Utility::toWindowsPath(dalamudConfigPath),
QStringLiteral("--dalamud-plugin-directory=") + Utility::toWindowsPath(dalamudPluginDir), QStringLiteral("--dalamud-plugin-directory=") + Utility::toWindowsPath(dalamudPluginDir),
QStringLiteral("--dalamud-asset-directory=") + Utility::toWindowsPath(dalamudAssetDir), QStringLiteral("--dalamud-asset-directory=") + Utility::toWindowsPath(dalamudAssetDir),
QStringLiteral("--dalamud-client-language=") + QString::number(profile.account()->language()), QStringLiteral("--dalamud-client-language=") + QString::number(profile.account()->config()->language()),
QStringLiteral("--dalamud-delay-initialize=") + QString::number(profile.dalamudInjectDelay()), QStringLiteral("--dalamud-delay-initialize=") + QString::number(profile.dalamudInjectDelay()),
QStringLiteral("--logpath=") + Utility::toWindowsPath(logDir), QStringLiteral("--logpath=") + Utility::toWindowsPath(logDir),
QStringLiteral("--"), QStringLiteral("--"),
@ -206,7 +207,7 @@ QString GameRunner::getGameArgs(const Profile &profile, const std::optional<Logi
gameArgs.push_back({QStringLiteral("DEV.UseSqPack"), QString::number(1)}); gameArgs.push_back({QStringLiteral("DEV.UseSqPack"), QString::number(1)});
gameArgs.push_back({QStringLiteral("ver"), profile.baseGameVersion()}); gameArgs.push_back({QStringLiteral("ver"), profile.baseGameVersion()});
gameArgs.push_back({QStringLiteral("resetconfig"), QStringLiteral("0")}); gameArgs.push_back({QStringLiteral("resetconfig"), QStringLiteral("0")});
gameArgs.push_back({QStringLiteral("language"), QString::number(profile.account()->language())}); gameArgs.push_back({QStringLiteral("language"), QString::number(profile.account()->config()->language())});
gameArgs.push_back({QStringLiteral("UserPath"), Utility::toWindowsPath(profile.account()->getConfigDir().absolutePath())}); gameArgs.push_back({QStringLiteral("UserPath"), Utility::toWindowsPath(profile.account()->getConfigDir().absolutePath())});
Utility::createPathIfNeeded(profile.account()->getConfigDir().absolutePath()); Utility::createPathIfNeeded(profile.account()->getConfigDir().absolutePath());
@ -236,7 +237,7 @@ QString GameRunner::getGameArgs(const Profile &profile, const std::optional<Logi
} }
} }
if (profile.account()->license() == Account::GameLicense::WindowsSteam) { if (profile.account()->config()->license() == Account::GameLicense::WindowsSteam) {
gameArgs.push_back({QStringLiteral("IsSteam"), QString::number(1)}); gameArgs.push_back({QStringLiteral("IsSteam"), QString::number(1)});
} }
} }
@ -260,7 +261,7 @@ void GameRunner::launchExecutable(const Profile &profile, QProcess *process, con
#if defined(Q_OS_LINUX) || defined(Q_OS_MAC) #if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
// FFXIV detects this as a "macOS" build by checking if Wine shows up // FFXIV detects this as a "macOS" build by checking if Wine shows up
if (!profile.isBenchmark()) { if (!profile.isBenchmark()) {
const int value = profile.account()->license() == Account::GameLicense::macOS ? 0 : 1; const int value = profile.account()->config()->license() == Account::GameLicense::macOS ? 0 : 1;
addRegistryKey(profile, QStringLiteral("HKEY_CURRENT_USER\\Software\\Wine"), QStringLiteral("HideWineExports"), QString::number(value)); addRegistryKey(profile, QStringLiteral("HKEY_CURRENT_USER\\Software\\Wine"), QStringLiteral("HideWineExports"), QString::number(value));
} }
@ -348,7 +349,7 @@ void GameRunner::launchExecutable(const Profile &profile, QProcess *process, con
arguments.push_back(profile.winePath()); arguments.push_back(profile.winePath());
#endif #endif
if (!profile.isBenchmark() && profile.account()->license() == Account::GameLicense::WindowsSteam) { if (!profile.isBenchmark() && profile.account()->config()->license() == Account::GameLicense::WindowsSteam) {
env.insert(QStringLiteral("IS_FFXIV_LAUNCH_FROM_STEAM"), QStringLiteral("1")); env.insert(QStringLiteral("IS_FFXIV_LAUNCH_FROM_STEAM"), QStringLiteral("1"));
} }

View file

@ -12,6 +12,7 @@
#include <qcoronetworkreply.h> #include <qcoronetworkreply.h>
#include "account.h" #include "account.h"
#include "accountconfig.h"
#include "assetupdater.h" #include "assetupdater.h"
#include "astra_log.h" #include "astra_log.h"
#include "benchmarkinstaller.h" #include "benchmarkinstaller.h"
@ -100,7 +101,7 @@ void LauncherCore::login(Profile *profile, const QString &username, const QStrin
loginInformation->password = password; loginInformation->password = password;
loginInformation->oneTimePassword = oneTimePassword; loginInformation->oneTimePassword = oneTimePassword;
if (profile->account()->rememberPassword()) { if (profile->account()->config()->rememberPassword()) {
profile->account()->setPassword(password); profile->account()->setPassword(password);
} }
} }
@ -113,8 +114,8 @@ bool LauncherCore::autoLogin(Profile *profile)
Q_ASSERT(profile != nullptr); Q_ASSERT(profile != nullptr);
QString otp; QString otp;
if (profile->account()->useOTP()) { if (profile->account()->config()->useOTP()) {
if (!profile->account()->rememberOTP()) { if (!profile->account()->config()->rememberOTP()) {
Q_EMIT loginError(i18n("This account does not have an OTP secret set, but requires it for login.")); Q_EMIT loginError(i18n("This account does not have an OTP secret set, but requires it for login."));
return false; return false;
} }
@ -126,7 +127,7 @@ bool LauncherCore::autoLogin(Profile *profile)
} }
} }
login(profile, profile->account()->name(), profile->account()->getPassword(), otp); login(profile, profile->account()->config()->name(), profile->account()->getPassword(), otp);
return true; return true;
} }
@ -172,21 +173,21 @@ BenchmarkInstaller *LauncherCore::createBenchmarkInstallerFromExisting(Profile *
void LauncherCore::fetchAvatar(Account *account) void LauncherCore::fetchAvatar(Account *account)
{ {
if (account->lodestoneId().isEmpty()) { if (account->config()->lodestoneId().isEmpty()) {
return; return;
} }
const QString cacheLocation = QStandardPaths::standardLocations(QStandardPaths::CacheLocation)[0] + QStringLiteral("/avatars"); const QString cacheLocation = QStandardPaths::standardLocations(QStandardPaths::CacheLocation)[0] + QStringLiteral("/avatars");
Utility::createPathIfNeeded(cacheLocation); Utility::createPathIfNeeded(cacheLocation);
const QString filename = QStringLiteral("%1/%2.jpg").arg(cacheLocation, account->lodestoneId()); const QString filename = QStringLiteral("%1/%2.jpg").arg(cacheLocation, account->config()->lodestoneId());
if (!QFile(filename).exists()) { if (!QFile(filename).exists()) {
qDebug(ASTRA_LOG) << "Did not find lodestone character " << account->lodestoneId() << " in cache, fetching from Lodestone."; qDebug(ASTRA_LOG) << "Did not find lodestone character " << account->config()->lodestoneId() << " in cache, fetching from Lodestone.";
QUrl url; QUrl url;
url.setScheme(config()->preferredProtocol()); url.setScheme(config()->preferredProtocol());
url.setHost(QStringLiteral("na.%1").arg(config()->mainServer())); // TODO: NA isnt the only thing in the world... url.setHost(QStringLiteral("na.%1").arg(config()->mainServer())); // TODO: NA isnt the only thing in the world...
url.setPath(QStringLiteral("/lodestone/character/%1").arg(account->lodestoneId())); url.setPath(QStringLiteral("/lodestone/character/%1").arg(account->config()->lodestoneId()));
const QNetworkRequest request(url); const QNetworkRequest request(url);
Utility::printRequest(QStringLiteral("GET"), request); Utility::printRequest(QStringLiteral("GET"), request);
@ -347,7 +348,7 @@ void LauncherCore::buildRequest(const Profile &settings, QNetworkRequest &reques
{ {
Utility::setSSL(request); Utility::setSSL(request);
if (settings.account()->license() == Account::GameLicense::macOS) { if (settings.account()->config()->license() == Account::GameLicense::macOS) {
request.setHeader(QNetworkRequest::UserAgentHeader, QByteArrayLiteral("macSQEXAuthor/2.0.0(MacOSX; ja-jp)")); request.setHeader(QNetworkRequest::UserAgentHeader, QByteArrayLiteral("macSQEXAuthor/2.0.0(MacOSX; ja-jp)"));
} else { } else {
request.setHeader(QNetworkRequest::UserAgentHeader, request.setHeader(QNetworkRequest::UserAgentHeader,
@ -468,8 +469,8 @@ QCoro::Task<> LauncherCore::beginLogin(LoginInformation &info)
std::optional<LoginAuth> auth; std::optional<LoginAuth> auth;
if (!info.profile->isBenchmark()) { if (!info.profile->isBenchmark()) {
if (info.profile->account()->isSapphire()) { if (info.profile->account()->config()->isSapphire()) {
auth = co_await m_sapphireLogin->login(info.profile->account()->lobbyUrl(), info); auth = co_await m_sapphireLogin->login(info.profile->account()->config()->lobbyUrl(), info);
} else { } else {
auth = co_await m_squareEnixLogin->login(&info); auth = co_await m_squareEnixLogin->login(&info);
} }

View file

@ -14,6 +14,7 @@
#include <qcoronetworkreply.h> #include <qcoronetworkreply.h>
#include "account.h" #include "account.h"
#include "accountconfig.h"
#include "astra_log.h" #include "astra_log.h"
#include "launchercore.h" #include "launchercore.h"
#include "utility.h" #include "utility.h"
@ -176,7 +177,7 @@ QCoro::Task<bool> SquareEnixLogin::checkBootUpdates()
url.setQuery(query); url.setQuery(query);
auto request = QNetworkRequest(url); auto request = QNetworkRequest(url);
if (m_info->profile->account()->license() == Account::GameLicense::macOS) { if (m_info->profile->account()->config()->license() == Account::GameLicense::macOS) {
request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, macosPatchUserAgent); request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, macosPatchUserAgent);
} else { } else {
request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, patchUserAgent); request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, patchUserAgent);
@ -225,12 +226,12 @@ QCoro::Task<std::optional<SquareEnixLogin::StoredInfo>> SquareEnixLogin::getStor
query.addQueryItem(QStringLiteral("lng"), QStringLiteral("en")); query.addQueryItem(QStringLiteral("lng"), QStringLiteral("en"));
// for some reason, we always use region 3. the actual region is acquired later // for some reason, we always use region 3. the actual region is acquired later
query.addQueryItem(QStringLiteral("rgn"), QString::number(3)); query.addQueryItem(QStringLiteral("rgn"), QString::number(3));
query.addQueryItem(QStringLiteral("isft"), QString::number(m_info->profile->account()->isFreeTrial() ? 1 : 0)); query.addQueryItem(QStringLiteral("isft"), QString::number(m_info->profile->account()->config()->isFreeTrial() ? 1 : 0));
query.addQueryItem(QStringLiteral("cssmode"), QString::number(1)); query.addQueryItem(QStringLiteral("cssmode"), QString::number(1));
query.addQueryItem(QStringLiteral("isnew"), QString::number(1)); query.addQueryItem(QStringLiteral("isnew"), QString::number(1));
query.addQueryItem(QStringLiteral("launchver"), QString::number(3)); query.addQueryItem(QStringLiteral("launchver"), QString::number(3));
if (m_info->profile->account()->license() == Account::GameLicense::WindowsSteam) { if (m_info->profile->account()->config()->license() == Account::GameLicense::WindowsSteam) {
query.addQueryItem(QStringLiteral("issteam"), QString::number(1)); query.addQueryItem(QStringLiteral("issteam"), QString::number(1));
// TODO: get steam ticket information from steam api // TODO: get steam ticket information from steam api
@ -255,7 +256,7 @@ QCoro::Task<std::optional<SquareEnixLogin::StoredInfo>> SquareEnixLogin::getStor
const QString str = QString::fromUtf8(reply->readAll()); const QString str = QString::fromUtf8(reply->readAll());
// fetches Steam username // fetches Steam username
if (m_info->profile->account()->license() == Account::GameLicense::WindowsSteam) { if (m_info->profile->account()->config()->license() == Account::GameLicense::WindowsSteam) {
const static QRegularExpression re(QStringLiteral(R"lit(<input name=""sqexid"" type=""hidden"" value=""(?<sqexid>.*)""\/>)lit")); const static QRegularExpression re(QStringLiteral(R"lit(<input name=""sqexid"" type=""hidden"" value=""(?<sqexid>.*)""\/>)lit"));
const QRegularExpressionMatch match = re.match(str); const QRegularExpressionMatch match = re.match(str);

View file

@ -31,7 +31,7 @@ QQC2.Control {
return i18n("Password is required."); return i18n("Password is required.");
} }
if (LauncherCore.currentProfile.account.useOTP && !LauncherCore.currentProfile.account.rememberOTP && otpField.text.length === 0) { if (LauncherCore.currentProfile.account.config.useOTP && !LauncherCore.currentProfile.account.config.rememberOTP && otpField.text.length === 0) {
return i18n("OTP is required."); return i18n("OTP is required.");
} }
@ -55,7 +55,7 @@ QQC2.Control {
return false; return false;
} }
if (LauncherCore.currentProfile.account.useOTP && !LauncherCore.currentProfile.account.rememberOTP && otpField.text.length === 0) { if (LauncherCore.currentProfile.account.config.useOTP && !LauncherCore.currentProfile.account.config.rememberOTP && otpField.text.length === 0) {
return false; return false;
} }
@ -63,9 +63,9 @@ QQC2.Control {
} }
function updateFields(): void { function updateFields(): void {
usernameField.text = LauncherCore.currentProfile.account.name; usernameField.text = LauncherCore.currentProfile.account.config.name;
passwordField.text = !LauncherCore.currentProfile.account.needsPassword && LauncherCore.currentProfile.account.rememberPassword ? LauncherCore.currentProfile.account.getPassword() : ""; passwordField.text = !LauncherCore.currentProfile.account.needsPassword && LauncherCore.currentProfile.account.config.rememberPassword ? LauncherCore.currentProfile.account.getPassword() : "";
if (LauncherCore.currentProfile.account.rememberOTP) { if (LauncherCore.currentProfile.account.config.rememberOTP) {
otpField.text = "Auto-generated"; otpField.text = "Auto-generated";
} else { } else {
otpField.text = ""; otpField.text = "";
@ -83,7 +83,7 @@ QQC2.Control {
return; return;
} }
if (LauncherCore.currentProfile.account.useOTP) { if (LauncherCore.currentProfile.account.config.useOTP) {
otpField.forceActiveFocus(); otpField.forceActiveFocus();
return; return;
} }
@ -165,13 +165,13 @@ QQC2.Control {
id: currentAccountDelegate id: currentAccountDelegate
enabled: LauncherCore.accountManager.numAccounts > 1 enabled: LauncherCore.accountManager.numAccounts > 1
text: LauncherCore.currentProfile.account.name text: LauncherCore.currentProfile.account.config.name
leading: Components.Avatar { leading: Components.Avatar {
implicitWidth: Kirigami.Units.iconSizes.medium implicitWidth: Kirigami.Units.iconSizes.medium
implicitHeight: Kirigami.Units.iconSizes.medium implicitHeight: Kirigami.Units.iconSizes.medium
name: LauncherCore.currentProfile.account.name name: LauncherCore.currentProfile.account.config.name
source: LauncherCore.currentProfile.account.avatarUrl source: LauncherCore.currentProfile.account.avatarUrl
} }
@ -191,7 +191,7 @@ QQC2.Control {
required property var account required property var account
QQC2.MenuItem { QQC2.MenuItem {
text: menuItem.account.name text: menuItem.account.config.name
icon.name: menuItem.account.avatarUrl.length === 0 ? "actor" : "" icon.name: menuItem.account.avatarUrl.length === 0 ? "actor" : ""
icon.source: menuItem.account.avatarUrl icon.source: menuItem.account.avatarUrl
@ -214,8 +214,8 @@ QQC2.Control {
FormCard.FormTextFieldDelegate { FormCard.FormTextFieldDelegate {
id: usernameField id: usernameField
label: LauncherCore.currentProfile.account.isSapphire ? i18n("Username") : i18n("Square Enix ID") label: LauncherCore.currentProfile.account.config.isSapphire ? i18n("Username") : i18n("Square Enix ID")
text: LauncherCore.currentProfile.account.name text: LauncherCore.currentProfile.account.config.name
enabled: false enabled: false
QQC2.ToolTip.text: i18n("The username can only be changed under account settings.") QQC2.ToolTip.text: i18n("The username can only be changed under account settings.")
@ -230,7 +230,7 @@ QQC2.Control {
FormCard.FormPasswordFieldDelegate { FormCard.FormPasswordFieldDelegate {
id: passwordField id: passwordField
label: LauncherCore.currentProfile.account.isSapphire ? i18n("Password") : i18n("Square Enix Password") label: LauncherCore.currentProfile.account.config.isSapphire ? i18n("Password") : i18n("Square Enix Password")
focus: true focus: true
onAccepted: { onAccepted: {
if (otpField.visible) { if (otpField.visible) {
@ -243,15 +243,15 @@ QQC2.Control {
FormCard.FormDelegateSeparator { FormCard.FormDelegateSeparator {
above: passwordField above: passwordField
below: LauncherCore.currentProfile.account.useOTP ? otpField : loginButton below: LauncherCore.currentProfile.account.config.useOTP ? otpField : loginButton
} }
FormCard.FormTextFieldDelegate { FormCard.FormTextFieldDelegate {
id: otpField id: otpField
enabled: !LauncherCore.currentProfile.account.rememberOTP enabled: !LauncherCore.currentProfile.account.config.rememberOTP
label: i18n("One-time Password") label: i18n("One-time Password")
visible: LauncherCore.currentProfile.account.useOTP visible: LauncherCore.currentProfile.account.config.useOTP
onAccepted: { onAccepted: {
if (page.isLoginValid) { if (page.isLoginValid) {
loginButton.clicked() loginButton.clicked()
@ -260,9 +260,9 @@ QQC2.Control {
} }
FormCard.FormDelegateSeparator { FormCard.FormDelegateSeparator {
above: LauncherCore.currentProfile.account.useOTP ? otpField : passwordField above: LauncherCore.currentProfile.account.config.useOTP ? otpField : passwordField
below: loginButton below: loginButton
visible: LauncherCore.currentProfile.account.useOTP visible: LauncherCore.currentProfile.account.config.useOTP
} }
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
@ -281,7 +281,7 @@ QQC2.Control {
FormCard.FormDelegateSeparator { FormCard.FormDelegateSeparator {
above: loginButton above: loginButton
below: forgotPasswordButton below: forgotPasswordButton
visible: !LauncherCore.currentProfile.account.isSapphire visible: !LauncherCore.currentProfile.account.config.isSapphire
} }
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
@ -289,7 +289,7 @@ QQC2.Control {
text: i18n("Forgot ID or Password") text: i18n("Forgot ID or Password")
icon.name: "question-symbolic" icon.name: "question-symbolic"
visible: !LauncherCore.currentProfile.account.isSapphire visible: !LauncherCore.currentProfile.account.config.isSapphire
onClicked: applicationWindow().openUrl('https://secure.square-enix.com/account/app/svc/reminder') onClicked: applicationWindow().openUrl('https://secure.square-enix.com/account/app/svc/reminder')
} }
} }
@ -325,7 +325,7 @@ QQC2.Control {
FormCard.FormLinkDelegate { FormCard.FormLinkDelegate {
text: i18nc("@action:button", "The Lodestone") text: i18nc("@action:button", "The Lodestone")
icon.name: "internet-services-symbolic" icon.name: "internet-services-symbolic"
visible: !LauncherCore.currentProfile.account.isSapphire visible: !LauncherCore.currentProfile.account.config.isSapphire
// TODO: how do we link to a "worldwide" lodestone, if that even exists? // TODO: how do we link to a "worldwide" lodestone, if that even exists?
url: 'https://na.finalfantasyxiv.com/lodestone/' url: 'https://na.finalfantasyxiv.com/lodestone/'
onClicked: applicationWindow().openUrl(url) onClicked: applicationWindow().openUrl(url)
@ -336,7 +336,7 @@ QQC2.Control {
FormCard.FormLinkDelegate { FormCard.FormLinkDelegate {
text: i18nc("@action:button", "Mog Station") text: i18nc("@action:button", "Mog Station")
icon.name: "internet-services-symbolic" icon.name: "internet-services-symbolic"
visible: !LauncherCore.currentProfile.account.isSapphire visible: !LauncherCore.currentProfile.account.config.isSapphire
url: 'https://secure.square-enix.com/account/app/svc/mogstation/' url: 'https://secure.square-enix.com/account/app/svc/mogstation/'
onClicked: applicationWindow().openUrl(url) onClicked: applicationWindow().openUrl(url)
} }

View file

@ -67,8 +67,8 @@ FormCard.FormCardPage {
id: usernameDelegate id: usernameDelegate
label: i18n("Username") label: i18n("Username")
text: page.account.name text: page.account.config.name
onTextChanged: page.account.name = text onTextChanged: page.account.config.name = text
} }
FormCard.FormDelegateSeparator { FormCard.FormDelegateSeparator {
@ -81,8 +81,8 @@ FormCard.FormCardPage {
text: i18n("Account type") text: i18n("Account type")
model: ["Square Enix", "Sapphire"] model: ["Square Enix", "Sapphire"]
currentIndex: page.account.isSapphire ? 1 : 0 currentIndex: page.account.config.isSapphire ? 1 : 0
onCurrentIndexChanged: page.account.isSapphire = (currentIndex === 1) onCurrentIndexChanged: page.account.config.isSapphire = (currentIndex === 1)
} }
FormCard.FormDelegateSeparator { FormCard.FormDelegateSeparator {
@ -96,13 +96,13 @@ FormCard.FormCardPage {
text: i18n("Language") text: i18n("Language")
description: i18n("The language used in the game client.") description: i18n("The language used in the game client.")
model: ["Japanese", "English", "German", "French"] model: ["Japanese", "English", "German", "French"]
currentIndex: page.account.language currentIndex: page.account.config.language
onCurrentIndexChanged: page.account.language = currentIndex onCurrentIndexChanged: page.account.config.language = currentIndex
} }
} }
FormCard.FormCard { FormCard.FormCard {
visible: accountAction.checked && !page.account.isSapphire visible: accountAction.checked && !page.account.config.isSapphire
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: Kirigami.Units.largeSpacing * 4 Layout.topMargin: Kirigami.Units.largeSpacing * 4
@ -113,8 +113,8 @@ FormCard.FormCardPage {
text: i18n("License") text: i18n("License")
description: i18n("If the account holds multiple licenses, choose the preferred one.") description: i18n("If the account holds multiple licenses, choose the preferred one.")
model: ["Windows", "Steam", "macOS"] model: ["Windows", "Steam", "macOS"]
currentIndex: page.account.license currentIndex: page.account.config.license
onCurrentIndexChanged: page.account.license = currentIndex onCurrentIndexChanged: page.account.config.license = currentIndex
} }
FormCard.FormDelegateSeparator { FormCard.FormDelegateSeparator {
@ -126,8 +126,8 @@ FormCard.FormCardPage {
id: freeTrialField id: freeTrialField
text: i18n("Free trial") text: i18n("Free trial")
description: i18n("If the account has a free trial license.") description: i18n("If the account has a free trial license.")
checked: page.account.isFreeTrial checked: page.account.config.isFreeTrial
onCheckedChanged: page.account.isFreeTrial = checked onCheckedChanged: page.account.config.isFreeTrial = checked
} }
FormCard.FormDelegateSeparator { FormCard.FormDelegateSeparator {
@ -140,8 +140,8 @@ FormCard.FormCardPage {
text: i18n("Needs a one-time password") text: i18n("Needs a one-time password")
description: i18n("Prompt for the one-time password when logging in.") description: i18n("Prompt for the one-time password when logging in.")
checked: page.account.useOTP checked: page.account.config.useOTP
onCheckedChanged: page.account.useOTP = checked onCheckedChanged: page.account.config.useOTP = checked
} }
FormCard.FormDelegateSeparator { FormCard.FormDelegateSeparator {
@ -161,11 +161,11 @@ FormCard.FormCardPage {
standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel
onAccepted: page.account.lodestoneId = lodestoneIdField.text onAccepted: page.account.config.lodestoneId = lodestoneIdField.text
QQC2.TextField { QQC2.TextField {
id: lodestoneIdField id: lodestoneIdField
text: page.account.lodestoneId text: page.account.config.lodestoneId
placeholderText: qsTr("123456...") placeholderText: qsTr("123456...")
} }
} }
@ -175,7 +175,7 @@ FormCard.FormCardPage {
} }
FormCard.FormCard { FormCard.FormCard {
visible: accountAction.checked && page.account.isSapphire visible: accountAction.checked && page.account.config.isSapphire
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: Kirigami.Units.largeSpacing * 4 Layout.topMargin: Kirigami.Units.largeSpacing * 4
@ -184,9 +184,9 @@ FormCard.FormCardPage {
id: lobbyURLDelegate id: lobbyURLDelegate
label: i18n("Lobby URL") label: i18n("Lobby URL")
text: page.account.lobbyUrl text: page.account.config.lobbyUrl
onTextChanged: page.account.lobbyUrl = text onTextChanged: page.account.config.lobbyUrl = text
visible: page.account.isSapphire visible: page.account.config.isSapphire
placeholderText: "neolobby0X.ffxiv.com" placeholderText: "neolobby0X.ffxiv.com"
} }
} }
@ -202,8 +202,8 @@ FormCard.FormCardPage {
text: i18n("Remember password") text: i18n("Remember password")
description: i18n("Stores the password on the device, using it's existing secure credential storage.") description: i18n("Stores the password on the device, using it's existing secure credential storage.")
checked: page.account.rememberPassword checked: page.account.config.rememberPassword
onCheckedChanged: page.account.rememberPassword = checked onCheckedChanged: page.account.config.rememberPassword = checked
} }
FormCard.FormDelegateSeparator { FormCard.FormDelegateSeparator {
@ -217,10 +217,10 @@ FormCard.FormCardPage {
text: i18n("Automatically generate one-time passwords") text: i18n("Automatically generate one-time passwords")
description: i18n("Stores the one-time password secret on this device, making it inherently insecure. Only use this feature if you understand the risks.") description: i18n("Stores the one-time password secret on this device, making it inherently insecure. Only use this feature if you understand the risks.")
checked: page.account.rememberOTP checked: page.account.config.rememberOTP
onCheckedChanged: page.account.rememberOTP = checked onCheckedChanged: page.account.config.rememberOTP = checked
enabled: page.account.useOTP enabled: page.account.config.useOTP
visible: !page.account.isSapphire visible: !page.account.config.isSapphire
} }
FormCard.FormDelegateSeparator { FormCard.FormDelegateSeparator {
@ -235,7 +235,7 @@ FormCard.FormCardPage {
text: i18n("Enter OTP Secret") text: i18n("Enter OTP Secret")
icon.name: "list-add-symbolic" icon.name: "list-add-symbolic"
enabled: page.account.rememberOTP enabled: page.account.config.rememberOTP
visible: generateOTPField.visible visible: generateOTPField.visible
Kirigami.PromptDialog { Kirigami.PromptDialog {
id: otpDialog id: otpDialog

View file

@ -52,12 +52,12 @@ FormCard.FormCardPage {
spacing: 0 spacing: 0
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
text: layout.account.name text: layout.account.config.name
description: layout.account.isSapphire ? i18n("Sapphire") : i18n("Square Enix") description: layout.account.config.isSapphire ? i18n("Sapphire") : i18n("Square Enix")
leading: Components.Avatar leading: Components.Avatar
{ {
name: layout.account.name name: layout.account.config.name
source: layout.account.avatarUrl source: layout.account.avatarUrl
} }

View file

@ -45,7 +45,7 @@ FormCard.FormCardPage {
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
required property var account required property var account
text: account.name text: account.config.name
onClicked: { onClicked: {
page.profile.account = account page.profile.account = account