Improve initial active connection handling

If the last active connection is not reachable (server down, keychain problems,
token revoked, etc.), NeoChat currently fails to load at all, with the only fix
being to delete a line from the config file. This is surprisingly hard to fix with
a nice UX as long as we stick to the principle of loading the user's last active
connection automatically.

This patch thus drops that principle; instead, the user is always asked to choose
the connection to continue with.
This commit is contained in:
Tobias Fella
2023-11-03 15:27:52 +01:00
parent c4f6abee9d
commit 7f9e709559
9 changed files with 83 additions and 32 deletions

View File

@@ -154,7 +154,6 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
qml/SpaceListContextMenu.qml qml/SpaceListContextMenu.qml
qml/UserInfo.qml qml/UserInfo.qml
qml/UserInfoDesktop.qml qml/UserInfoDesktop.qml
qml/LoadingPage.qml
qml/RoomPage.qml qml/RoomPage.qml
qml/RoomWindow.qml qml/RoomWindow.qml
qml/JoinRoomPage.qml qml/JoinRoomPage.qml

View File

@@ -167,6 +167,8 @@ void Controller::invokeLogin()
QString id = NeoChatConfig::self()->activeConnection(); QString id = NeoChatConfig::self()->activeConnection();
for (const auto &accountId : accounts) { for (const auto &accountId : accounts) {
AccountSettings account{accountId}; AccountSettings account{accountId};
m_accountsLoading += accountId;
Q_EMIT accountsLoadingChanged();
if (id.isEmpty()) { if (id.isEmpty()) {
// handle case where the account config is empty // handle case where the account config is empty
id = accountId; id = accountId;
@@ -186,6 +188,8 @@ void Controller::invokeLogin()
connect(connection, &NeoChatConnection::connected, this, [this, connection, id] { connect(connection, &NeoChatConnection::connected, this, [this, connection, id] {
connection->loadState(); connection->loadState();
addConnection(connection); addConnection(connection);
m_accountsLoading.removeAll(connection->userId());
Q_EMIT accountsLoadingChanged();
if (connection->userId() == id) { if (connection->userId() == id) {
setActiveConnection(connection); setActiveConnection(connection);
connectSingleShot(connection, &NeoChatConnection::syncDone, this, &Controller::initiated); connectSingleShot(connection, &NeoChatConnection::syncDone, this, &Controller::initiated);

View File

@@ -63,6 +63,8 @@ class Controller : public QObject
*/ */
Q_PROPERTY(bool isFlatpak READ isFlatpak CONSTANT) Q_PROPERTY(bool isFlatpak READ isFlatpak CONSTANT)
Q_PROPERTY(QStringList accountsLoading MEMBER m_accountsLoading NOTIFY accountsLoadingChanged)
public: public:
/** /**
* @brief Defines the status after an attempt to change the password on an account. * @brief Defines the status after an attempt to change the password on an account.
@@ -140,6 +142,7 @@ private:
QMap<Quotient::Room *, int> m_notificationCounts; QMap<Quotient::Room *, int> m_notificationCounts;
Quotient::AccountRegistry m_accountRegistry; Quotient::AccountRegistry m_accountRegistry;
QStringList m_accountsLoading;
private Q_SLOTS: private Q_SLOTS:
void invokeLogin(); void invokeLogin();
@@ -162,6 +165,7 @@ Q_SIGNALS:
void passwordStatus(Controller::PasswordStatus status); void passwordStatus(Controller::PasswordStatus status);
void userConsentRequired(QUrl url); void userConsentRequired(QUrl url);
void isOnlineChanged(bool isOnline); void isOnlineChanged(bool isOnline);
void accountsLoadingChanged();
public Q_SLOTS: public Q_SLOTS:
void saveWindowGeometry(); void saveWindowGeometry();

View File

@@ -100,7 +100,8 @@ void LoginHelper::init()
Q_EMIT Controller::instance().globalErrorOccured(i18n("Network Error"), std::move(error)); Q_EMIT Controller::instance().globalErrorOccured(i18n("Network Error"), std::move(error));
}); });
connectSingleShot(m_connection, &Connection::syncDone, this, []() { connectSingleShot(m_connection, &Connection::syncDone, this, [this]() {
Q_EMIT loaded();
Q_EMIT Controller::instance().initiated(); Q_EMIT Controller::instance().initiated();
}); });
} }

View File

@@ -79,7 +79,17 @@ class LoginHelper : public QObject
Q_PROPERTY(bool isInvalidPassword READ isInvalidPassword NOTIFY isInvalidPasswordChanged) Q_PROPERTY(bool isInvalidPassword READ isInvalidPassword NOTIFY isInvalidPasswordChanged)
public: public:
explicit LoginHelper(QObject *parent = nullptr); static LoginHelper &instance()
{
static LoginHelper _instance;
return _instance;
}
static LoginHelper *create(QQmlEngine *engine, QJSEngine *)
{
engine->setObjectOwnership(&instance(), QQmlEngine::CppOwnership);
return &instance();
}
Q_INVOKABLE void init(); Q_INVOKABLE void init();
@@ -125,6 +135,7 @@ Q_SIGNALS:
void isLoggingInChanged(); void isLoggingInChanged();
void isLoggedInChanged(); void isLoggedInChanged();
void isInvalidPasswordChanged(); void isInvalidPasswordChanged();
void loaded();
private: private:
void setHomeserverReachable(bool reachable); void setHomeserverReachable(bool reachable);
@@ -141,4 +152,5 @@ private:
bool m_isLoggingIn = false; bool m_isLoggingIn = false;
bool m_isLoggedIn = false; bool m_isLoggedIn = false;
bool m_invalidPassword = false; bool m_invalidPassword = false;
explicit LoginHelper(QObject *parent = nullptr);
}; };

View File

@@ -1,13 +0,0 @@
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
Kirigami.Page {
title: i18n("Loading…")
Kirigami.LoadingPlaceholder {
id: loadingIndicator
anchors.centerIn: parent
}
}

View File

@@ -14,7 +14,12 @@ import org.kde.neochat.accounts
FormCard.FormCardPage { FormCard.FormCardPage {
id: root id: root
property bool showExisting: false
property bool _showExisting: showExisting && module.source == root.initialStep
property alias currentStep: module.item property alias currentStep: module.item
property string initialStep: "qrc:/org/kde/neochat/qml/LoginRegister.qml"
signal connectionChosen
title: i18n("Welcome") title: i18n("Welcome")
@@ -42,17 +47,47 @@ FormCard.FormCardPage {
FormCard.FormTextDelegate { FormCard.FormTextDelegate {
id: welcomeMessage id: welcomeMessage
text: AccountRegistry.accountCount > 0 ? i18n("Log in to a different account or create a new account.") : i18n("Welcome to NeoChat! Continue by logging in or creating a new account.") text: i18n("Welcome to NeoChat")
} }
}
FormCard.FormDelegateSeparator { FormCard.FormHeader {
above: welcomeMessage id: existingAccountsHeader
title: i18nc("@title", "Continue with an existing account")
visible: (loadedAccounts.count > 0 || loadingAccounts.count > 0) && root._showExisting
}
FormCard.FormCard {
visible: existingAccountsHeader.visible
Repeater {
id: loadedAccounts
model: AccountRegistry
delegate: FormCard.FormButtonDelegate {
text: model.userId
onClicked: {
Controller.activeConnection = model.connection
root.connectionChosen()
}
}
} }
Repeater {
id: loadingAccounts
model: Controller.accountsLoading
delegate: FormCard.FormButtonDelegate {
text: i18nc("As in 'this account is still loading'", "%1 (loading)", modelData)
enabled: false
}
}
}
FormCard.FormHeader {
title: i18nc("@title", "Log in or Create a New Account")
}
FormCard.FormCard {
Loader { Loader {
id: module id: module
Layout.fillWidth: true Layout.fillWidth: true
source: "qrc:/org/kde/neochat/qml/LoginRegister.qml" source: root.initialStep
Connections { Connections {
id: stepConnections id: stepConnections

View File

@@ -31,7 +31,15 @@ Kirigami.ApplicationWindow {
wideScreen: width > columnWidth * 5 wideScreen: width > columnWidth * 5
pageStack { pageStack {
initialPage: LoadingPage {} initialPage: WelcomePage {
showExisting: true
onConnectionChosen: {
pageStack.replace(roomListComponent);
roomListLoaded = true;
roomListPage = pageStack.currentItem
RoomManager.loadInitialRoom();
}
}
globalToolBar.canContainHandles: true globalToolBar.canContainHandles: true
defaultColumnWidth: roomListPage ? roomListPage.currentWidth : 0 defaultColumnWidth: roomListPage ? roomListPage.currentWidth : 0
globalToolBar { globalToolBar {
@@ -47,6 +55,16 @@ Kirigami.ApplicationWindow {
SpaceHierarchyCache.connection = root.connection SpaceHierarchyCache.connection = root.connection
} }
Connections {
target: LoginHelper
function onLoaded() {
pageStack.replace(roomListComponent);
roomListLoaded = true;
roomListPage = pageStack.currentItem
RoomManager.loadInitialRoom();
}
}
Connections { Connections {
target: root.quitAction target: root.quitAction
function onTriggered() { function onTriggered() {
@@ -279,17 +297,6 @@ Kirigami.ApplicationWindow {
Connections { Connections {
target: Controller target: Controller
function onInitiated() {
if (AccountRegistry.accountCount === 0) {
pageStack.replace("qrc:/org/kde/neochat/qml/WelcomePage.qml", {});
} else if (!roomListLoaded) {
pageStack.replace(roomListComponent);
roomListLoaded = true;
roomListPage = pageStack.currentItem
RoomManager.loadInitialRoom();
}
}
function onGlobalErrorOccured(error, detail) { function onGlobalErrorOccured(error, detail) {
showPassiveNotification(i18n("%1: %2", error, detail)); showPassiveNotification(i18n("%1: %2", error, detail));
} }

View File

@@ -12,6 +12,7 @@
#include <Quotient/settings.h> #include <Quotient/settings.h>
#include "controller.h" #include "controller.h"
#include "login.h"
#include <KLocalizedString> #include <KLocalizedString>
@@ -113,6 +114,7 @@ void Registration::registerAccount()
Controller::instance().setActiveConnection(connection); Controller::instance().setActiveConnection(connection);
connectSingleShot(connection, &Connection::syncDone, this, []() { connectSingleShot(connection, &Connection::syncDone, this, []() {
Q_EMIT Controller::instance().initiated(); Q_EMIT Controller::instance().initiated();
Q_EMIT LoginHelper::instance().loaded();
}); });
m_connection = nullptr; m_connection = nullptr;
}); });