From 7f9e709559b9a2dc6f03d1836be35658ca8e488c Mon Sep 17 00:00:00 2001 From: Tobias Fella Date: Fri, 3 Nov 2023 15:27:52 +0100 Subject: [PATCH] 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. --- src/CMakeLists.txt | 1 - src/controller.cpp | 4 ++++ src/controller.h | 4 ++++ src/login.cpp | 3 ++- src/login.h | 14 +++++++++++++- src/qml/LoadingPage.qml | 13 ------------- src/qml/WelcomePage.qml | 43 +++++++++++++++++++++++++++++++++++++---- src/qml/main.qml | 31 +++++++++++++++++------------ src/registration.cpp | 2 ++ 9 files changed, 83 insertions(+), 32 deletions(-) delete mode 100644 src/qml/LoadingPage.qml diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ee7b67822..05fb27785 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -154,7 +154,6 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN qml/SpaceListContextMenu.qml qml/UserInfo.qml qml/UserInfoDesktop.qml - qml/LoadingPage.qml qml/RoomPage.qml qml/RoomWindow.qml qml/JoinRoomPage.qml diff --git a/src/controller.cpp b/src/controller.cpp index ab6d6676e..9b10bab33 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -167,6 +167,8 @@ void Controller::invokeLogin() QString id = NeoChatConfig::self()->activeConnection(); for (const auto &accountId : accounts) { AccountSettings account{accountId}; + m_accountsLoading += accountId; + Q_EMIT accountsLoadingChanged(); if (id.isEmpty()) { // handle case where the account config is empty id = accountId; @@ -186,6 +188,8 @@ void Controller::invokeLogin() connect(connection, &NeoChatConnection::connected, this, [this, connection, id] { connection->loadState(); addConnection(connection); + m_accountsLoading.removeAll(connection->userId()); + Q_EMIT accountsLoadingChanged(); if (connection->userId() == id) { setActiveConnection(connection); connectSingleShot(connection, &NeoChatConnection::syncDone, this, &Controller::initiated); diff --git a/src/controller.h b/src/controller.h index 63ee1a6c6..9572b8bb0 100644 --- a/src/controller.h +++ b/src/controller.h @@ -63,6 +63,8 @@ class Controller : public QObject */ Q_PROPERTY(bool isFlatpak READ isFlatpak CONSTANT) + Q_PROPERTY(QStringList accountsLoading MEMBER m_accountsLoading NOTIFY accountsLoadingChanged) + public: /** * @brief Defines the status after an attempt to change the password on an account. @@ -140,6 +142,7 @@ private: QMap m_notificationCounts; Quotient::AccountRegistry m_accountRegistry; + QStringList m_accountsLoading; private Q_SLOTS: void invokeLogin(); @@ -162,6 +165,7 @@ Q_SIGNALS: void passwordStatus(Controller::PasswordStatus status); void userConsentRequired(QUrl url); void isOnlineChanged(bool isOnline); + void accountsLoadingChanged(); public Q_SLOTS: void saveWindowGeometry(); diff --git a/src/login.cpp b/src/login.cpp index 96678678a..853fb53cd 100644 --- a/src/login.cpp +++ b/src/login.cpp @@ -100,7 +100,8 @@ void LoginHelper::init() 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(); }); } diff --git a/src/login.h b/src/login.h index 8155fea62..fd8f3fee6 100644 --- a/src/login.h +++ b/src/login.h @@ -79,7 +79,17 @@ class LoginHelper : public QObject Q_PROPERTY(bool isInvalidPassword READ isInvalidPassword NOTIFY isInvalidPasswordChanged) 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(); @@ -125,6 +135,7 @@ Q_SIGNALS: void isLoggingInChanged(); void isLoggedInChanged(); void isInvalidPasswordChanged(); + void loaded(); private: void setHomeserverReachable(bool reachable); @@ -141,4 +152,5 @@ private: bool m_isLoggingIn = false; bool m_isLoggedIn = false; bool m_invalidPassword = false; + explicit LoginHelper(QObject *parent = nullptr); }; diff --git a/src/qml/LoadingPage.qml b/src/qml/LoadingPage.qml deleted file mode 100644 index b54ab4c53..000000000 --- a/src/qml/LoadingPage.qml +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-FileCopyrightText: 2020 Carl Schwan -// 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 - } -} diff --git a/src/qml/WelcomePage.qml b/src/qml/WelcomePage.qml index 230b24feb..915e66245 100644 --- a/src/qml/WelcomePage.qml +++ b/src/qml/WelcomePage.qml @@ -14,7 +14,12 @@ import org.kde.neochat.accounts FormCard.FormCardPage { id: root + property bool showExisting: false + property bool _showExisting: showExisting && module.source == root.initialStep property alias currentStep: module.item + property string initialStep: "qrc:/org/kde/neochat/qml/LoginRegister.qml" + + signal connectionChosen title: i18n("Welcome") @@ -42,17 +47,47 @@ FormCard.FormCardPage { FormCard.FormTextDelegate { 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 { - above: welcomeMessage + FormCard.FormHeader { + 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 { id: module Layout.fillWidth: true - source: "qrc:/org/kde/neochat/qml/LoginRegister.qml" + source: root.initialStep Connections { id: stepConnections diff --git a/src/qml/main.qml b/src/qml/main.qml index c1e5303f9..cddddd5cb 100644 --- a/src/qml/main.qml +++ b/src/qml/main.qml @@ -31,7 +31,15 @@ Kirigami.ApplicationWindow { wideScreen: width > columnWidth * 5 pageStack { - initialPage: LoadingPage {} + initialPage: WelcomePage { + showExisting: true + onConnectionChosen: { + pageStack.replace(roomListComponent); + roomListLoaded = true; + roomListPage = pageStack.currentItem + RoomManager.loadInitialRoom(); + } + } globalToolBar.canContainHandles: true defaultColumnWidth: roomListPage ? roomListPage.currentWidth : 0 globalToolBar { @@ -47,6 +55,16 @@ Kirigami.ApplicationWindow { SpaceHierarchyCache.connection = root.connection } + Connections { + target: LoginHelper + function onLoaded() { + pageStack.replace(roomListComponent); + roomListLoaded = true; + roomListPage = pageStack.currentItem + RoomManager.loadInitialRoom(); + } + } + Connections { target: root.quitAction function onTriggered() { @@ -279,17 +297,6 @@ Kirigami.ApplicationWindow { Connections { 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) { showPassiveNotification(i18n("%1: %2", error, detail)); } diff --git a/src/registration.cpp b/src/registration.cpp index 5fe7fa50d..4fba1a86b 100644 --- a/src/registration.cpp +++ b/src/registration.cpp @@ -12,6 +12,7 @@ #include #include "controller.h" +#include "login.h" #include @@ -113,6 +114,7 @@ void Registration::registerAccount() Controller::instance().setActiveConnection(connection); connectSingleShot(connection, &Connection::syncDone, this, []() { Q_EMIT Controller::instance().initiated(); + Q_EMIT LoginHelper::instance().loaded(); }); m_connection = nullptr; });