diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 947cba9..b698747 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -47,6 +47,7 @@ qt_target_qml_sources(astra QML_FILES ui/Components/FormFileDelegate.qml ui/Components/FormFolderDelegate.qml + ui/Pages/AutoLoginPage.qml ui/Pages/BrowserPage.qml ui/Pages/LoginPage.qml ui/Pages/MainPage.qml diff --git a/launcher/config.kcfg b/launcher/config.kcfg index 09969e3..7714bb5 100644 --- a/launcher/config.kcfg +++ b/launcher/config.kcfg @@ -32,5 +32,7 @@ SPDX-License-Identifier: CC0-1.0 square-enix.com + + diff --git a/launcher/include/launchercore.h b/launcher/include/launchercore.h index df565ef..bb023d2 100755 --- a/launcher/include/launchercore.h +++ b/launcher/include/launchercore.h @@ -74,6 +74,7 @@ class LauncherCore : public QObject Q_PROPERTY(QString squareEnixLoginServer READ squareEnixLoginServer WRITE setSquareEnixLoginServer NOTIFY squareEnixLoginServerChanged) Q_PROPERTY(Headline *headline READ headline NOTIFY newsChanged) Q_PROPERTY(Profile *currentProfile READ currentProfile WRITE setCurrentProfile NOTIFY currentProfileChanged) + Q_PROPERTY(Profile *autoLoginProfile READ autoLoginProfile WRITE setAutoLoginProfile NOTIFY autoLoginProfileChanged) public: LauncherCore(); @@ -100,7 +101,7 @@ public: * The launcher will still warn the user about any possible errors, however the call site will need to check the * result to see whether they need to "reset" or show a failed state or not. */ - bool autoLogin(Profile &settings); + Q_INVOKABLE void autoLogin(Profile *profile); /* * Launches the game using the provided authentication. @@ -139,6 +140,10 @@ public: [[nodiscard]] QString squareEnixLoginServer() const; void setSquareEnixLoginServer(const QString &value); + [[nodiscard]] QString autoLoginProfileName() const; + [[nodiscard]] Profile *autoLoginProfile() const; + void setAutoLoginProfile(Profile *value); + Q_INVOKABLE GameInstaller *createInstaller(Profile *profile); Q_INVOKABLE CompatibilityToolInstaller *createCompatInstaller(); @@ -177,6 +182,7 @@ signals: void stageDeterminate(int min, int max, int value); void newsChanged(); void currentProfileChanged(); + void autoLoginProfileChanged(); private: QCoro::Task<> beginLogin(LoginInformation &info); diff --git a/launcher/include/profilemanager.h b/launcher/include/profilemanager.h index 1e83e67..787a167 100644 --- a/launcher/include/profilemanager.h +++ b/launcher/include/profilemanager.h @@ -30,6 +30,7 @@ public: [[nodiscard]] QHash roleNames() const override; Q_INVOKABLE Profile *getProfile(int index); + Profile *getProfileByUUID(const QString &uuid); int getProfileIndex(const QString &name); Q_INVOKABLE Profile *addProfile(); diff --git a/launcher/src/launchercore.cpp b/launcher/src/launchercore.cpp index 93020f7..632a8d0 100755 --- a/launcher/src/launchercore.cpp +++ b/launcher/src/launchercore.cpp @@ -448,12 +448,9 @@ void LauncherCore::login(Profile *profile, const QString &username, const QStrin beginLogin(*loginInformation); } -bool LauncherCore::autoLogin(Profile &profile) +void LauncherCore::autoLogin(Profile *profile) { - // TODO: when login fails, we need some way to propagate this back? or not? - login(&profile, profile.account()->name(), profile.account()->getPassword(), profile.account()->getOTP()); - - return true; + login(profile, profile->account()->name(), profile->account()->getPassword(), profile->account()->useOTP() ? profile->account()->getOTP() : QString()); } GameInstaller *LauncherCore::createInstaller(Profile *profile) @@ -585,6 +582,36 @@ void LauncherCore::setSquareEnixLoginServer(const QString &value) } } +[[nodiscard]] QString LauncherCore::autoLoginProfileName() const +{ + return Config::autoLoginProfile(); +} + +[[nodiscard]] Profile *LauncherCore::autoLoginProfile() const +{ + if (Config::autoLoginProfile().isEmpty()) { + return nullptr; + } + return m_profileManager->getProfileByUUID(Config::autoLoginProfile()); +} + +void LauncherCore::setAutoLoginProfile(Profile *profile) +{ + if (profile == nullptr) { + Config::setAutoLoginProfile({}); + Config::self()->save(); + Q_EMIT autoLoginProfileChanged(); + return; + } + + auto uuid = profile->uuid(); + if (uuid != Config::autoLoginProfile()) { + Config::setAutoLoginProfile(uuid); + Config::self()->save(); + Q_EMIT autoLoginProfileChanged(); + } +} + void LauncherCore::refreshNews() { fetchNews(); diff --git a/launcher/src/profilemanager.cpp b/launcher/src/profilemanager.cpp index 0b338b6..b6be493 100644 --- a/launcher/src/profilemanager.cpp +++ b/launcher/src/profilemanager.cpp @@ -28,6 +28,16 @@ int ProfileManager::getProfileIndex(const QString &name) return -1; } +Profile *ProfileManager::getProfileByUUID(const QString &uuid) +{ + for (auto &m_profile : m_profiles) { + if (m_profile->uuid() == uuid) + return m_profile; + } + + return nullptr; +} + Profile *ProfileManager::addProfile() { auto newProfile = new Profile(m_launcher, QUuid::createUuid().toString(), this); @@ -91,6 +101,7 @@ void ProfileManager::load() auto config = KSharedConfig::openStateConfig(); for (const auto &id : config->groupList()) { if (id.contains(QLatin1String("profile-"))) { + qInfo() << "Adding profile" << id; auto profile = new Profile(m_launcher, QString(id).remove(QLatin1String("profile-")), this); insertProfile(profile); } diff --git a/launcher/src/squarelauncher.cpp b/launcher/src/squarelauncher.cpp index b8c1102..564a47c 100644 --- a/launcher/src/squarelauncher.cpp +++ b/launcher/src/squarelauncher.cpp @@ -104,6 +104,8 @@ QCoro::Task<> SquareLauncher::login(const LoginInformation &info) const auto [stored, referer] = *storedResult; + qInfo() << "Performing oauth..."; + QUrlQuery postData; postData.addQueryItem(QStringLiteral("_STORED_"), stored); postData.addQueryItem(QStringLiteral("sqexid"), info.username); diff --git a/launcher/ui/Main.qml b/launcher/ui/Main.qml index db0a0e8..9ea2910 100644 --- a/launcher/ui/Main.qml +++ b/launcher/ui/Main.qml @@ -46,10 +46,19 @@ Kirigami.ApplicationWindow { profile: LauncherCore.currentProfile }) } else { - pageStack.layers.replace(Qt.createComponent("zone.xiv.astra", "MainPage")) + if (LauncherCore.autoLoginProfile) { + pageStack.layers.replace(Qt.createComponent("zone.xiv.astra", "AutoLoginPage")) + } else { + pageStack.layers.replace(Qt.createComponent("zone.xiv.astra", "MainPage")) + } } } + function cancelAutoLogin() { + pageStack.layers.clear(); + pageStack.layers.replace(Qt.createComponent("zone.xiv.astra", "MainPage")); + } + function pushDialogLayer(url) { if (LauncherCore.isSteamDeck) { pageStack.layers.push(url) diff --git a/launcher/ui/Pages/AutoLoginPage.qml b/launcher/ui/Pages/AutoLoginPage.qml new file mode 100644 index 0000000..942f592 --- /dev/null +++ b/launcher/ui/Pages/AutoLoginPage.qml @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2023 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-or-later + +import QtQuick +import QtQuick.Controls as QQC2 + +import org.kde.kirigami as Kirigami + +import zone.xiv.astra + +Kirigami.Page { + id: root + + title: i18n("Auto Login") + + Kirigami.LoadingPlaceholder { + id: placeholderMessage + + anchors.centerIn: parent + + text: i18n("Logging in...") + + helpfulAction: Kirigami.Action { + icon.name: "Cancel" + text: "Cancel" + enabled: autoLoginTimer.running + onTriggered: { + autoLoginTimer.stop(); + applicationWindow().cancelAutoLogin(); + } + } + } + + Timer { + id: autoLoginTimer + + interval: 5000 + running: true + onTriggered: { + LauncherCore.autoLogin(LauncherCore.autoLoginProfile); + pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "StatusPage")); + } + } +} diff --git a/launcher/ui/Settings/GeneralSettings.qml b/launcher/ui/Settings/GeneralSettings.qml index fd10104..92aa6a5 100644 --- a/launcher/ui/Settings/GeneralSettings.qml +++ b/launcher/ui/Settings/GeneralSettings.qml @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: 2023 Joshua Goins // SPDX-License-Identifier: GPL-3.0-or-later +import QtQuick +import QtQuick.Controls as QQC2 + import org.kde.kirigamiaddons.formcard as FormCard import zone.xiv.astra @@ -13,6 +16,43 @@ FormCard.FormCardPage { } FormCard.FormCard { + FormCard.FormButtonDelegate { + text: i18n("Auto-login Profile") + description: LauncherCore.autoLoginProfile ? LauncherCore.autoLoginProfile.name : i18n("Disabled") + + QQC2.Menu { + id: profileMenu + + QQC2.MenuItem { + text: "Disabled" + + onClicked: { + LauncherCore.autoLoginProfile = null; + profileMenu.close(); + } + } + + Repeater { + model: LauncherCore.profileManager + + QQC2.MenuItem { + required property var profile + + text: profile.name + + onClicked: { + LauncherCore.autoLoginProfile = profile; + profileMenu.close(); + } + } + } + } + + onClicked: profileMenu.popup() + } + + FormCard.FormDelegateSeparator {} + FormCard.FormCheckDelegate { id: closeAstraDelegate