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 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) kconfig_add_kcfg_files(astra GENERATE_MOC config.kcfgc accountconfig.kcfgc profileconfig.kcfgc)
target_include_directories(astra PRIVATE include ${CMAKE_BINARY_DIR}) target_include_directories(astra PRIVATE include ${CMAKE_BINARY_DIR})
target_link_libraries(astra PRIVATE target_link_libraries(astra PRIVATE

View file

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

View file

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

View file

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

View file

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

View file

@ -510,13 +510,13 @@ QString Profile::bootVersion() const
QString Profile::baseGameVersion() 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; return m_repositories.repositories[0].version;
} }
int Profile::numInstalledExpansions() const int Profile::numInstalledExpansions() const
{ {
Q_ASSERT(m_repositories.repositories_count > 1); Q_ASSERT(m_repositories.repositories_count >= 1);
return 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) int ProfileManager::getProfileIndex(const QString &name)
{ {
for (int i = 0; i < m_profiles.size(); i++) { for (int i = 0; i < m_profiles.size(); i++) {
if (m_profiles[i]->name() == name) if (m_profiles[i]->uuid() == name)
return i; return i;
} }
@ -147,3 +147,14 @@ bool ProfileManager::canDelete(Profile *account) const
Q_UNUSED(account) Q_UNUSED(account)
return m_profiles.size() != 1; 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 QtCore
import QtQuick.Dialogs import QtQuick.Dialogs
import org.kde.kirigamiaddons.labs.mobileform as MobileForm import org.kde.kirigamiaddons.formcard as FormCard
MobileForm.FormButtonDelegate { FormCard.FormButtonDelegate {
id: control id: control
property string file property string file

View file

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

View file

@ -5,37 +5,52 @@ import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import org.kde.kirigami as Kirigami 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 import zone.xiv.astra
Kirigami.Page { FormCard.FormCardPage {
id: page id: page
property var profile property var profile
title: i18n("Account Setup") title: i18n("Account Setup")
ColumnLayout { FormCard.FormCard {
width: parent.width
MobileForm.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
MobileForm.FormCardHeader { FormCard.FormTextDelegate {
title: i18n("Accounts") id: helpTextDelegate
text: i18n("Select an account to use for '%1'", LauncherCore.currentProfile.name)
}
} }
MobileForm.FormTextDelegate { FormCard.FormHeader {
text: i18n("Select an account below to use for profile '%1'.", LauncherCore.currentProfile.name) title: i18n("Existing Accounts")
visible: LauncherCore.accountManager.hasAnyAccounts()
}
FormCard.FormCard {
visible: LauncherCore.accountManager.hasAnyAccounts()
Layout.fillWidth: true
FormCard.FormTextDelegate {
id: existingHelpDelegate
text: i18n("You can select an existing account.")
}
FormCard.FormDelegateSeparator {
above: existingHelpDelegate
} }
Repeater { Repeater {
model: LauncherCore.accountManager model: LauncherCore.accountManager
MobileForm.FormButtonDelegate { FormCard.FormButtonDelegate {
required property var account required property var account
text: account.name text: account.name
@ -47,15 +62,14 @@ Kirigami.Page {
} }
} }
} }
}
MobileForm.FormCard { FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
MobileForm.FormButtonDelegate { FormCard.FormButtonDelegate {
id: addSquareEnixButton
text: i18n("Add Square Enix Account") text: i18n("Add Square Enix Account")
icon.name: "list-add-symbolic" icon.name: "list-add-symbolic"
onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "AddSquareEnix"), { onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "AddSquareEnix"), {
@ -63,10 +77,14 @@ Kirigami.Page {
}) })
} }
MobileForm.FormDelegateSeparator { FormCard.FormDelegateSeparator {
above: addSquareEnixButton
below: addSapphireButton
} }
MobileForm.FormButtonDelegate { FormCard.FormButtonDelegate {
id: addSapphireButton
text: i18n("Add Sapphire Account") text: i18n("Add Sapphire Account")
icon.name: "list-add-symbolic" icon.name: "list-add-symbolic"
onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "AddSapphire"), { onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "AddSapphire"), {
@ -75,5 +93,3 @@ Kirigami.Page {
} }
} }
} }
}
}

View file

@ -6,51 +6,58 @@ import QtQuick.Layouts
import QtQuick.Window import QtQuick.Window
import org.kde.kirigami as Kirigami 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 import zone.xiv.astra
Kirigami.Page { FormCard.FormCardPage {
id: page id: page
property var profile property var profile
title: i18n("Add Sapphire Account") title: i18n("Add Sapphire Account")
ColumnLayout { readonly property bool isValid: usernameField.text.length !== 0 && lobbyUrlField.text.length !== 0
width: parent.width
MobileForm.FormCard { FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
MobileForm.FormTextDelegate { FormCard.FormTextDelegate {
description: i18n("Passwords will be entered on the login page. The username will be associated with this profile but can be changed later.") 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.FormDelegateSeparator { FormCard.FormDelegateSeparator {
above: helpTextDelegate
below: usernameField
} }
MobileForm.FormTextFieldDelegate { FormCard.FormTextFieldDelegate {
id: lobbyUrlField
label: i18n("Lobby URL")
}
MobileForm.FormDelegateSeparator {
}
MobileForm.FormTextFieldDelegate {
id: usernameField id: usernameField
label: i18n("Username") label: i18n("Username")
} }
MobileForm.FormDelegateSeparator { FormCard.FormDelegateSeparator {
above: usernameField
below: lobbyUrlField
} }
MobileForm.FormButtonDelegate { FormCard.FormTextFieldDelegate {
id: lobbyUrlField
label: i18n("Lobby URL")
}
FormCard.FormDelegateSeparator {
above: lobbyUrlField
below: buttonDelegate
}
FormCard.FormButtonDelegate {
id: buttonDelegate
text: i18n("Add Account") text: i18n("Add Account")
icon.name: "list-add-symbolic" icon.name: "list-add-symbolic"
enabled: page.isValid
onClicked: { onClicked: {
let account = LauncherCore.accountManager.createSapphireAccount(lobbyUrlField.text, usernameField.text) let account = LauncherCore.accountManager.createSapphireAccount(lobbyUrlField.text, usernameField.text)
if (page.profile) { if (page.profile) {
@ -63,5 +70,3 @@ Kirigami.Page {
} }
} }
} }
}
}

View file

@ -6,41 +6,44 @@ import QtQuick.Layouts
import QtQuick.Window import QtQuick.Window
import org.kde.kirigami as Kirigami 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 import zone.xiv.astra
Kirigami.Page { FormCard.FormCardPage {
id: page id: page
property var profile property var profile
title: i18n("Add Square Enix Account") title: i18n("Add Square Enix Account")
ColumnLayout { readonly property bool isValid: usernameField.text.length !== 0
width: parent.width
MobileForm.FormCard { FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
MobileForm.FormTextDelegate { FormCard.FormTextDelegate {
description: i18n("Passwords will be entered on the login page. The username will be associated with this profile but can be changed later.") 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.FormDelegateSeparator { FormCard.FormDelegateSeparator {
above: helpTextDelegate
below: usernameField
} }
MobileForm.FormTextFieldDelegate { FormCard.FormTextFieldDelegate {
id: usernameField id: usernameField
label: i18n("Username") label: i18n("Username")
} }
MobileForm.FormDelegateSeparator { FormCard.FormDelegateSeparator {
above: usernameField
below: licenseField
} }
MobileForm.FormComboBoxDelegate { FormCard.FormComboBoxDelegate {
id: licenseField id: licenseField
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.")
@ -48,21 +51,27 @@ Kirigami.Page {
currentIndex: 0 currentIndex: 0
} }
MobileForm.FormDelegateSeparator { FormCard.FormDelegateSeparator {
above: licenseField
below: freeTrialField
} }
MobileForm.FormCheckDelegate { FormCard.FormCheckDelegate {
id: freeTrialField id: freeTrialField
text: i18n("Free Trial") text: i18n("Free Trial")
description: i18n("Check if the account is currently on free trial.") description: i18n("Check if the account is currently on free trial.")
} }
MobileForm.FormDelegateSeparator { FormCard.FormDelegateSeparator {
above: freeTrialField
below: buttonDelegate
} }
MobileForm.FormButtonDelegate { FormCard.FormButtonDelegate {
id: buttonDelegate
text: i18n("Add Account") text: i18n("Add Account")
icon.name: "list-add-symbolic" icon.name: "list-add-symbolic"
enabled: page.isValid
onClicked: { onClicked: {
let account = LauncherCore.accountManager.createSquareEnixAccount(usernameField.text, licenseField.currentIndex, freeTrialField.checkState === Qt.Checked) let account = LauncherCore.accountManager.createSquareEnixAccount(usernameField.text, licenseField.currentIndex, freeTrialField.checkState === Qt.Checked)
if (page.profile) { if (page.profile) {
@ -75,5 +84,3 @@ Kirigami.Page {
} }
} }
} }
}
}

View file

@ -5,38 +5,36 @@ import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import org.kde.kirigami as Kirigami 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 import zone.xiv.astra
Kirigami.Page { FormCard.FormCardPage {
id: page id: page
property var profile property var profile
title: i18n("Download Game") title: i18n("Download Game")
ColumnLayout { FormCard.FormCard {
width: parent.width
MobileForm.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
MobileForm.FormCardHeader { FormCard.FormTextDelegate {
title: i18n("Download Game") id: helpTextDelegate
}
MobileForm.FormTextDelegate {
text: i18n("Press the button below to download and setup the game.") 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.") 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 { FormCard.FormButtonDelegate {
id: buttonDelegate
text: i18n("Begin installation") text: i18n("Begin installation")
icon.name: "cloud-download" icon.name: "cloud-download"
onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "InstallProgress"), { onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "InstallProgress"), {
@ -45,5 +43,3 @@ Kirigami.Page {
} }
} }
} }
}
}

View file

@ -3,31 +3,52 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Dialogs
import org.kde.kirigami as Kirigami 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 import zone.xiv.astra
Kirigami.Page { import "../Components"
title: i18n("Find Game Installation")
ColumnLayout { FormCard.FormCardPage {
width: parent.width id: page
MobileForm.FormCard {
property var profile
title: i18n("Find Existing Installation")
FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
MobileForm.FormCardHeader { FormCard.FormTextDelegate {
title: i18n("Find an existing installation") id: helpTextDelegate
}
MobileForm.FormTextDelegate {
text: i18n("Please select the path to your existing installation.") 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 QtQuick.Layouts
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.labs.mobileform as MobileForm
import zone.xiv.astra import zone.xiv.astra

View file

@ -5,62 +5,71 @@ import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import org.kde.kirigami as Kirigami 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 import zone.xiv.astra
Kirigami.Page { FormCard.FormCardPage {
id: page id: page
property var profile property var profile
readonly property bool isInitialSetup: !LauncherCore.profileManager.hasAnyExistingInstallations()
title: i18n("Game Setup") title: isInitialSetup ? i18n("Initial Setup") : i18n("Profile Setup")
Image {
source: "qrc:/zone.xiv.astra.svg"
fillMode: Image.PreserveAspectFit
ColumnLayout {
width: parent.width
MobileForm.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true Layout.fillWidth: true
contentItem: ColumnLayout { Layout.fillHeight: true
spacing: 0 Layout.margins: Kirigami.Units.largeSpacing * 3
MobileForm.FormCardHeader {
title: i18n("Welcome to Astra")
} }
MobileForm.FormTextDelegate { FormCard.FormCard {
text: i18n("The profile '%1' must be set up before first.", LauncherCore.currentProfile.name)
}
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.")
}
}
}
MobileForm.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
MobileForm.FormCardHeader { 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") title: i18n("Existing Installations")
visible: LauncherCore.profileManager.hasAnyExistingInstallations()
} }
MobileForm.FormTextDelegate { FormCard.FormCard {
text: i18n("Select an existing installation from another profile below.") 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 { Repeater {
model: LauncherCore.profileManager model: LauncherCore.profileManager
MobileForm.FormButtonDelegate { FormCard.FormButtonDelegate {
required property var profile required property var profile
text: profile.name text: profile.name
description: profile.gamePath description: profile.gamePath
visible: profile.isGameInstalled
onClicked: { onClicked: {
LauncherCore.currentProfile.gamePath = profile.gamePath; LauncherCore.currentProfile.gamePath = profile.gamePath;
@ -69,15 +78,14 @@ Kirigami.Page {
} }
} }
} }
}
MobileForm.FormCard { FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing Layout.topMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
MobileForm.FormButtonDelegate { FormCard.FormButtonDelegate {
id: findExistingDelegate
text: i18n("Find Existing Installation") text: i18n("Find Existing Installation")
icon.name: "edit-find" icon.name: "edit-find"
onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "ExistingSetup"), { onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "ExistingSetup"), {
@ -85,10 +93,14 @@ Kirigami.Page {
}) })
} }
MobileForm.FormDelegateSeparator { FormCard.FormDelegateSeparator {
above: findExistingDelegate
below: downloadDelegate
} }
MobileForm.FormButtonDelegate { FormCard.FormButtonDelegate {
id: downloadDelegate
text: i18n("Download Game") text: i18n("Download Game")
icon.name: "cloud-download" icon.name: "cloud-download"
onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "DownloadSetup"), { onClicked: pageStack.layers.push(Qt.createComponent("zone.xiv.astra", "DownloadSetup"), {
@ -97,5 +109,3 @@ Kirigami.Page {
} }
} }
} }
}
}