1
Fork 0
mirror of https://github.com/redstrate/Astra.git synced 2025-04-21 20:27:45 +00:00

Overhaul initial setup flow, again

This improves the flow drastically, first by porting it from MobileForm
to FormCard. Next, it fixes some of the annoying bugs such as the
profile not switching properly when adding a new profile. Selecting an
existing game path is now possible, and it's less likely you can enter
in invalid account credentials. The overall look and behavior of some
of the pages is improved.
This commit is contained in:
Joshua Goins 2023-10-08 20:01:17 -04:00
parent 3a7dc40826
commit 67dcd90058
16 changed files with 344 additions and 261 deletions

View file

@ -109,6 +109,16 @@ qt_target_qml_sources(astra
ui/Main.qml
)
set_source_files_properties(../zone.xiv.astra.svg PROPERTIES
QT_RESOURCE_ALIAS /zone.xiv.astra.svg
)
qt_target_qml_sources(astra
PREFIX /
RESOURCES
../zone.xiv.astra.svg
)
kconfig_add_kcfg_files(astra GENERATE_MOC config.kcfgc accountconfig.kcfgc profileconfig.kcfgc)
target_include_directories(astra PRIVATE include ${CMAKE_BINARY_DIR})
target_link_libraries(astra PRIVATE

View file

@ -37,6 +37,8 @@ public:
Q_INVOKABLE bool canDelete(Account *account) const;
Q_INVOKABLE void deleteAccount(Account *account);
Q_INVOKABLE bool hasAnyAccounts() const;
private:
void insertAccount(Account *account);

View file

@ -40,6 +40,8 @@ public:
Q_INVOKABLE bool canDelete(Profile *account) const;
Q_INVOKABLE bool hasAnyExistingInstallations() const;
static QString getDefaultGamePath(const QString &uuid);
static QString getDefaultWinePrefixPath(const QString &uuid);

View file

@ -111,3 +111,8 @@ void AccountManager::insertAccount(Account *account)
m_accounts.append(account);
endInsertRows();
}
bool AccountManager::hasAnyAccounts() const
{
return !m_accounts.empty();
}

View file

@ -794,7 +794,7 @@ void LauncherCore::setCurrentProfile(Profile *profile)
{
Q_ASSERT(profile != nullptr);
const int newIndex = m_profileManager->getProfileIndex(profile->name());
const int newIndex = m_profileManager->getProfileIndex(profile->uuid());
if (newIndex != m_currentProfileIndex) {
m_currentProfileIndex = newIndex;
Q_EMIT currentProfileChanged();

View file

@ -510,13 +510,13 @@ QString Profile::bootVersion() const
QString Profile::baseGameVersion() const
{
Q_ASSERT(m_repositories.repositories_count > 1);
Q_ASSERT(m_repositories.repositories_count >= 1);
return m_repositories.repositories[0].version;
}
int Profile::numInstalledExpansions() const
{
Q_ASSERT(m_repositories.repositories_count > 1);
Q_ASSERT(m_repositories.repositories_count >= 1);
return m_repositories.repositories_count - 1;
}

View file

@ -22,7 +22,7 @@ Profile *ProfileManager::getProfile(const int index)
int ProfileManager::getProfileIndex(const QString &name)
{
for (int i = 0; i < m_profiles.size(); i++) {
if (m_profiles[i]->name() == name)
if (m_profiles[i]->uuid() == name)
return i;
}
@ -147,3 +147,14 @@ bool ProfileManager::canDelete(Profile *account) const
Q_UNUSED(account)
return m_profiles.size() != 1;
}
bool ProfileManager::hasAnyExistingInstallations() const
{
for (auto &profile : m_profiles) {
if (profile->isGameInstalled()) {
return true;
}
}
return false;
}

View file

@ -4,9 +4,9 @@
import QtCore
import QtQuick.Dialogs
import org.kde.kirigamiaddons.labs.mobileform as MobileForm
import org.kde.kirigamiaddons.formcard as FormCard
MobileForm.FormButtonDelegate {
FormCard.FormButtonDelegate {
id: control
property string file

View file

@ -88,7 +88,6 @@ FormCard.FormCardPage {
}
}
FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true

View file

@ -5,75 +5,91 @@ import QtQuick
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.labs.mobileform as MobileForm
import org.kde.kirigamiaddons.formcard as FormCard
import zone.xiv.astra
Kirigami.Page {
FormCard.FormCardPage {
id: page
property var profile
title: i18n("Account Setup")
ColumnLayout {
width: parent.width
MobileForm.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true
MobileForm.FormCardHeader {
title: i18n("Accounts")
}
FormCard.FormTextDelegate {
id: helpTextDelegate
MobileForm.FormTextDelegate {
text: i18n("Select an account below to use for profile '%1'.", LauncherCore.currentProfile.name)
}
text: i18n("Select an account to use for '%1'", LauncherCore.currentProfile.name)
}
}
Repeater {
model: LauncherCore.accountManager
FormCard.FormHeader {
title: i18n("Existing Accounts")
visible: LauncherCore.accountManager.hasAnyAccounts()
}
MobileForm.FormButtonDelegate {
required property var account
FormCard.FormCard {
visible: LauncherCore.accountManager.hasAnyAccounts()
text: account.name
Layout.fillWidth: true
onClicked: {
page.profile.account = account
applicationWindow().checkSetup()
}
}
}
}
FormCard.FormTextDelegate {
id: existingHelpDelegate
text: i18n("You can select an existing account.")
}
MobileForm.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
FormCard.FormDelegateSeparator {
above: existingHelpDelegate
}
MobileForm.FormButtonDelegate {
text: i18n("Add Square Enix Account")
icon.name: "list-add-symbolic"
onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "AddSquareEnix"), {
profile: page.profile
})
}
Repeater {
model: LauncherCore.accountManager
MobileForm.FormDelegateSeparator {
}
FormCard.FormButtonDelegate {
required property var account
MobileForm.FormButtonDelegate {
text: i18n("Add Sapphire Account")
icon.name: "list-add-symbolic"
onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "AddSapphire"), {
profile: page.profile
})
text: account.name
onClicked: {
page.profile.account = account
applicationWindow().checkSetup()
}
}
}
}
FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true
FormCard.FormButtonDelegate {
id: addSquareEnixButton
text: i18n("Add Square Enix Account")
icon.name: "list-add-symbolic"
onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "AddSquareEnix"), {
profile: page.profile
})
}
FormCard.FormDelegateSeparator {
above: addSquareEnixButton
below: addSapphireButton
}
FormCard.FormButtonDelegate {
id: addSapphireButton
text: i18n("Add Sapphire Account")
icon.name: "list-add-symbolic"
onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "AddSapphire"), {
profile: page.profile
})
}
}
}

View file

@ -6,60 +6,65 @@ import QtQuick.Layouts
import QtQuick.Window
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.labs.mobileform as MobileForm
import org.kde.kirigamiaddons.formcard as FormCard
import zone.xiv.astra
Kirigami.Page {
FormCard.FormCardPage {
id: page
property var profile
title: i18n("Add Sapphire Account")
ColumnLayout {
width: parent.width
MobileForm.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
readonly property bool isValid: usernameField.text.length !== 0 && lobbyUrlField.text.length !== 0
MobileForm.FormTextDelegate {
description: i18n("Passwords will be entered on the login page. The username will be associated with this profile but can be changed later.")
}
FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true
MobileForm.FormDelegateSeparator {
}
FormCard.FormTextDelegate {
id: helpTextDelegate
description: i18n("The password will be entered on the login page. A username will be associated with this account but can always be changed later.")
}
MobileForm.FormTextFieldDelegate {
id: lobbyUrlField
label: i18n("Lobby URL")
}
FormCard.FormDelegateSeparator {
above: helpTextDelegate
below: usernameField
}
MobileForm.FormDelegateSeparator {
}
FormCard.FormTextFieldDelegate {
id: usernameField
label: i18n("Username")
}
MobileForm.FormTextFieldDelegate {
id: usernameField
label: i18n("Username")
}
FormCard.FormDelegateSeparator {
above: usernameField
below: lobbyUrlField
}
MobileForm.FormDelegateSeparator {
}
FormCard.FormTextFieldDelegate {
id: lobbyUrlField
label: i18n("Lobby URL")
}
MobileForm.FormButtonDelegate {
text: i18n("Add Account")
icon.name: "list-add-symbolic"
onClicked: {
let account = LauncherCore.accountManager.createSapphireAccount(lobbyUrlField.text, usernameField.text)
if (page.profile) {
page.profile.account = account
applicationWindow().checkSetup()
} else {
page.Window.window.pageStack.layers.pop()
}
}
FormCard.FormDelegateSeparator {
above: lobbyUrlField
below: buttonDelegate
}
FormCard.FormButtonDelegate {
id: buttonDelegate
text: i18n("Add Account")
icon.name: "list-add-symbolic"
enabled: page.isValid
onClicked: {
let account = LauncherCore.accountManager.createSapphireAccount(lobbyUrlField.text, usernameField.text)
if (page.profile) {
page.profile.account = account
applicationWindow().checkSetup()
} else {
page.Window.window.pageStack.layers.pop()
}
}
}

View file

@ -6,72 +6,79 @@ import QtQuick.Layouts
import QtQuick.Window
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.labs.mobileform as MobileForm
import org.kde.kirigamiaddons.formcard as FormCard
import zone.xiv.astra
Kirigami.Page {
FormCard.FormCardPage {
id: page
property var profile
title: i18n("Add Square Enix Account")
ColumnLayout {
width: parent.width
MobileForm.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
readonly property bool isValid: usernameField.text.length !== 0
MobileForm.FormTextDelegate {
description: i18n("Passwords will be entered on the login page. The username will be associated with this profile but can be changed later.")
}
FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true
MobileForm.FormDelegateSeparator {
}
FormCard.FormTextDelegate {
id: helpTextDelegate
description: i18n("The password will be entered on the login page. A username will be associated with this account but can always be changed later.")
}
MobileForm.FormTextFieldDelegate {
id: usernameField
label: i18n("Username")
}
FormCard.FormDelegateSeparator {
above: helpTextDelegate
below: usernameField
}
MobileForm.FormDelegateSeparator {
}
FormCard.FormTextFieldDelegate {
id: usernameField
label: i18n("Username")
}
MobileForm.FormComboBoxDelegate {
id: licenseField
text: i18n("License")
description: i18n("If the account holds multiple licenses, choose the preferred one.")
model: ["Windows", "Steam", "macOS"]
currentIndex: 0
}
FormCard.FormDelegateSeparator {
above: usernameField
below: licenseField
}
MobileForm.FormDelegateSeparator {
}
FormCard.FormComboBoxDelegate {
id: licenseField
text: i18n("License")
description: i18n("If the account holds multiple licenses, choose the preferred one.")
model: ["Windows", "Steam", "macOS"]
currentIndex: 0
}
MobileForm.FormCheckDelegate {
id: freeTrialField
text: i18n("Free Trial")
description: i18n("Check if the account is currently on free trial.")
}
FormCard.FormDelegateSeparator {
above: licenseField
below: freeTrialField
}
MobileForm.FormDelegateSeparator {
}
FormCard.FormCheckDelegate {
id: freeTrialField
text: i18n("Free Trial")
description: i18n("Check if the account is currently on free trial.")
}
MobileForm.FormButtonDelegate {
text: i18n("Add Account")
icon.name: "list-add-symbolic"
onClicked: {
let account = LauncherCore.accountManager.createSquareEnixAccount(usernameField.text, licenseField.currentIndex, freeTrialField.checkState === Qt.Checked)
if (page.profile) {
page.profile.account = account
applicationWindow().checkSetup()
} else {
page.Window.window.pageStack.layers.pop()
}
}
FormCard.FormDelegateSeparator {
above: freeTrialField
below: buttonDelegate
}
FormCard.FormButtonDelegate {
id: buttonDelegate
text: i18n("Add Account")
icon.name: "list-add-symbolic"
enabled: page.isValid
onClicked: {
let account = LauncherCore.accountManager.createSquareEnixAccount(usernameField.text, licenseField.currentIndex, freeTrialField.checkState === Qt.Checked)
if (page.profile) {
page.profile.account = account
applicationWindow().checkSetup()
} else {
page.Window.window.pageStack.layers.pop()
}
}
}

View file

@ -5,45 +5,41 @@ import QtQuick
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.labs.mobileform as MobileForm
import org.kde.kirigamiaddons.formcard as FormCard
import zone.xiv.astra
Kirigami.Page {
FormCard.FormCardPage {
id: page
property var profile
title: i18n("Download Game")
ColumnLayout {
width: parent.width
MobileForm.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true
MobileForm.FormCardHeader {
title: i18n("Download Game")
}
FormCard.FormTextDelegate {
id: helpTextDelegate
MobileForm.FormTextDelegate {
text: i18n("Press the button below to download and setup the game.")
description: i18n("This is for the base files required for start-up, only when logged in will Astra begin downloading the full game.")
}
text: i18n("Press the button below to download and setup the game.")
description: i18n("This only installs the base files required for the initial update. Astra can only download patches with a legitimate Square Enix account.")
}
MobileForm.FormDelegateSeparator {
}
FormCard.FormDelegateSeparator {
above: helpTextDelegate
below: buttonDelegate
}
MobileForm.FormButtonDelegate {
text: i18n("Begin installation")
icon.name: "cloud-download"
onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "InstallProgress"), {
gameInstaller: LauncherCore.createInstaller(page.profile)
})
}
}
FormCard.FormButtonDelegate {
id: buttonDelegate
text: i18n("Begin installation")
icon.name: "cloud-download"
onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "InstallProgress"), {
gameInstaller: LauncherCore.createInstaller(page.profile)
})
}
}
}

View file

@ -3,31 +3,52 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Dialogs
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.labs.mobileform as MobileForm
import org.kde.kirigamiaddons.formcard as FormCard
import zone.xiv.astra
Kirigami.Page {
title: i18n("Find Game Installation")
import "../Components"
ColumnLayout {
width: parent.width
MobileForm.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
FormCard.FormCardPage {
id: page
MobileForm.FormCardHeader {
title: i18n("Find an existing installation")
}
property var profile
MobileForm.FormTextDelegate {
text: i18n("Please select the path to your existing installation.")
}
}
title: i18n("Find Existing Installation")
FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true
FormCard.FormTextDelegate {
id: helpTextDelegate
text: i18n("Please select the path to your existing installation.")
}
FormCard.FormDelegateSeparator {
above: helpTextDelegate
below: selectDelegate
}
FormCard.FormButtonDelegate {
id: selectDelegate
text: i18n("Select Existing Path")
icon.name: "document-open-folder"
onClicked: dialog.open()
}
}
data: FolderDialog {
id: dialog
onAccepted: {
page.profile.gamePath = decodeURIComponent(selectedFolder.toString().replace("file://", ""));
applicationWindow().checkSetup();
}
}
}

View file

@ -5,7 +5,6 @@ import QtQuick
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.labs.mobileform as MobileForm
import zone.xiv.astra

View file

@ -5,97 +5,107 @@ import QtQuick
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.labs.mobileform as MobileForm
import org.kde.kirigamiaddons.formcard as FormCard
import zone.xiv.astra
Kirigami.Page {
FormCard.FormCardPage {
id: page
property var profile
readonly property bool isInitialSetup: !LauncherCore.profileManager.hasAnyExistingInstallations()
title: i18n("Game Setup")
title: isInitialSetup ? i18n("Initial Setup") : i18n("Profile Setup")
ColumnLayout {
width: parent.width
MobileForm.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
Image {
source: "qrc:/zone.xiv.astra.svg"
MobileForm.FormCardHeader {
title: i18n("Welcome to Astra")
}
fillMode: Image.PreserveAspectFit
MobileForm.FormTextDelegate {
text: i18n("The profile '%1' must be set up before first.", LauncherCore.currentProfile.name)
}
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: Kirigami.Units.largeSpacing * 3
}
MobileForm.FormTextDelegate {
text: i18n("A copy of the game must be located or installed.")
description: i18n("A valid game account will be required at the end of installation.")
}
}
}
FormCard.FormCard {
Layout.fillWidth: true
MobileForm.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
MobileForm.FormCardHeader {
title: i18n("Existing Installations")
}
MobileForm.FormTextDelegate {
text: i18n("Select an existing installation from another profile below.")
}
Repeater {
model: LauncherCore.profileManager
MobileForm.FormButtonDelegate {
required property var profile
text: profile.name
description: profile.gamePath
onClicked: {
LauncherCore.currentProfile.gamePath = profile.gamePath;
applicationWindow().checkSetup();
}
}
}
}
}
MobileForm.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
MobileForm.FormButtonDelegate {
text: i18n("Find Existing Installation")
icon.name: "edit-find"
onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "ExistingSetup"), {
profile: page.profile
})
}
MobileForm.FormDelegateSeparator {
}
MobileForm.FormButtonDelegate {
text: i18n("Download Game")
icon.name: "cloud-download"
onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "DownloadSetup"), {
profile: page.profile
})
FormCard.FormTextDelegate {
text: {
if (isInitialSetup) {
return i18n("You must have a legitimate installation of the FFXIV to continue.");
} else {
return i18n("You must select a legitimate installation of FFXIV for '%1'", page.profile.name);
}
}
}
}
FormCard.FormHeader {
title: i18n("Existing Installations")
visible: LauncherCore.profileManager.hasAnyExistingInstallations()
}
FormCard.FormCard {
visible: LauncherCore.profileManager.hasAnyExistingInstallations()
Layout.fillWidth: true
FormCard.FormTextDelegate {
id: existingHelpDelegate
text: i18n("You can select an existing installation from another profile.")
}
FormCard.FormDelegateSeparator {
above: existingHelpDelegate
}
Repeater {
model: LauncherCore.profileManager
FormCard.FormButtonDelegate {
required property var profile
text: profile.name
description: profile.gamePath
visible: profile.isGameInstalled
onClicked: {
LauncherCore.currentProfile.gamePath = profile.gamePath;
applicationWindow().checkSetup();
}
}
}
}
FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true
FormCard.FormButtonDelegate {
id: findExistingDelegate
text: i18n("Find Existing Installation")
icon.name: "edit-find"
onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "ExistingSetup"), {
profile: page.profile
})
}
FormCard.FormDelegateSeparator {
above: findExistingDelegate
below: downloadDelegate
}
FormCard.FormButtonDelegate {
id: downloadDelegate
text: i18n("Download Game")
icon.name: "cloud-download"
onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "DownloadSetup"), {
profile: page.profile
})
}
}
}