diff --git a/imports/NeoChat/Page/RoomListPage.qml b/imports/NeoChat/Page/RoomListPage.qml index 28f3a26d4..2a84f06e0 100644 --- a/imports/NeoChat/Page/RoomListPage.qml +++ b/imports/NeoChat/Page/RoomListPage.qml @@ -303,16 +303,14 @@ Kirigami.ScrollablePage { spacing: 0 Repeater { id: accountList - model: AccountListModel { - id: accountListModel - } + model: AccountRegistry delegate: Kirigami.BasicListItem { checkable: true - checked: Controller.activeConnection && Controller.activeConnection.localUser.id === model.user.id + checked: Controller.activeConnection && Controller.activeConnection.localUserId === model.connection.localUserId onClicked: Controller.activeConnection = model.connection Layout.fillWidth: true Layout.fillHeight: true - text: model.user.id + text: model.connection.localUserId } } } diff --git a/imports/NeoChat/Settings/AccountsPage.qml b/imports/NeoChat/Settings/AccountsPage.qml index 6981cf644..4209f9524 100644 --- a/imports/NeoChat/Settings/AccountsPage.qml +++ b/imports/NeoChat/Settings/AccountsPage.qml @@ -42,7 +42,7 @@ Kirigami.Page { Controls.ScrollBar.horizontal.policy: Controls.ScrollBar.AlwaysOff ListView { clip: true - model: AccountListModel { } + model: AccountRegistry delegate: Kirigami.SwipeListItem { leftPadding: 0 rightPadding: 0 @@ -50,10 +50,10 @@ Kirigami.Page { anchors.top: parent.top anchors.bottom: parent.bottom - text: model.user.displayName + text: model.connection.localUser.displayName labelItem.textFormat: Text.PlainText - subtitle: model.user.id - icon: model.user.avatarMediaId ? ("image://mxc/" + model.user.avatarMediaId) : "im-user" + subtitle: model.connection.localUserId + icon: model.connection.localUser.avatarMediaId ? ("image://mxc/" + model.connection.localUser.avatarMediaId) : "im-user" onClicked: { Controller.activeConnection = model.connection diff --git a/qml/main.qml b/qml/main.qml index 36d8f0544..672572b88 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -309,6 +309,18 @@ Kirigami.ApplicationWindow { } } + Connections { + target: AccountRegistry + function onRowsRemoved() { + if (AccountRegistry.rowCount() === 0) { + RoomManager.reset(); + pageStack.clear(); + roomListLoaded = false; + pageStack.push("qrc:/imports/NeoChat/Page/WelcomePage.qml"); + } + } + } + Connections { target: Controller @@ -324,15 +336,6 @@ Kirigami.ApplicationWindow { } } - function onConnectionDropped() { - if (Controller.accountCount === 0) { - RoomManager.reset(); - pageStack.clear(); - roomListLoaded = false; - pageStack.replace("qrc:/imports/NeoChat/Page/WelcomePage.qml"); - } - } - function onGlobalErrorOccured(error, detail) { showPassiveNotification(i18nc("%1: %2", error, detail)); } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d5f1b34ec..88e5c940e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,7 +4,6 @@ # SPDX-License-Identifier: BSD-2-Clause add_executable(neochat - accountlistmodel.cpp controller.cpp actionshandler.cpp emojimodel.cpp @@ -42,6 +41,8 @@ add_executable(neochat if(Quotient_VERSION_MINOR GREATER 6) target_compile_definitions(neochat PRIVATE QUOTIENT_07) +else() + target_sources(neochat PRIVATE accountregistry.cpp) endif() ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png) diff --git a/src/accountlistmodel.cpp b/src/accountlistmodel.cpp deleted file mode 100644 index 9f1c46b3a..000000000 --- a/src/accountlistmodel.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-FileCopyrightText: 2018-2019 Black Hat -// SPDX-License-Identifier: GPL-3.0-only - -#include "accountlistmodel.h" - -#include "room.h" - -AccountListModel::AccountListModel(QObject *parent) - : QAbstractListModel(parent) -{ - connect(&Controller::instance(), &Controller::connectionAdded, this, [=]() { - beginResetModel(); - endResetModel(); - }); - connect(&Controller::instance(), &Controller::connectionDropped, this, [=]() { - beginResetModel(); - endResetModel(); - }); -} - -QVariant AccountListModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) { - return {}; - } - - if (index.row() >= Controller::instance().connections().count()) { - return {}; - } - - auto connection = Controller::instance().connections().at(index.row()); - - if (role == UserRole) { - return QVariant::fromValue(connection->user()); - } - if (role == ConnectionRole) { - return QVariant::fromValue(connection); - } - - return {}; -} - -int AccountListModel::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid()) { - return 0; - } - - return Controller::instance().connections().count(); -} - -QHash AccountListModel::roleNames() const -{ - QHash roles; - - roles[UserRole] = "user"; - roles[ConnectionRole] = "connection"; - - return roles; -} diff --git a/src/accountlistmodel.h b/src/accountlistmodel.h deleted file mode 100644 index 3583bd5d5..000000000 --- a/src/accountlistmodel.h +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-FileCopyrightText: 2018 Black Hat -// SPDX-License-Identifier: GPL-3.0-only - -#pragma once - -#include "controller.h" - -#include -#include - -class AccountListModel : public QAbstractListModel -{ - Q_OBJECT -public: - enum EventRoles { - UserRole = Qt::UserRole + 1, - ConnectionRole, - }; - - AccountListModel(QObject *parent = nullptr); - - [[nodiscard]] QVariant data(const QModelIndex &index, int role = UserRole) const override; - [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override; - - [[nodiscard]] QHash roleNames() const override; -}; diff --git a/src/accountregistry.cpp b/src/accountregistry.cpp new file mode 100644 index 000000000..b87587700 --- /dev/null +++ b/src/accountregistry.cpp @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: Kitsune Ral +// SPDX-FileCopyrightText: Tobias Fella +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "accountregistry.h" + +#include "connection.h" + +using namespace Quotient; + +void AccountRegistry::add(Connection *c) +{ + if (m_accounts.contains(c)) + return; + beginInsertRows(QModelIndex(), m_accounts.size(), m_accounts.size()); + m_accounts += c; + endInsertRows(); +} + +void AccountRegistry::drop(Connection *c) +{ + beginRemoveRows(QModelIndex(), m_accounts.indexOf(c), m_accounts.indexOf(c)); + m_accounts.removeOne(c); + endRemoveRows(); + Q_ASSERT(!m_accounts.contains(c)); +} + +bool AccountRegistry::isLoggedIn(const QString &userId) const +{ + return std::any_of(m_accounts.cbegin(), m_accounts.cend(), [&userId](Connection *a) { + return a->userId() == userId; + }); +} + +bool AccountRegistry::contains(Connection *c) const +{ + return m_accounts.contains(c); +} + +AccountRegistry::AccountRegistry() = default; + +QVariant AccountRegistry::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + return {}; + } + + if (index.row() >= m_accounts.count()) { + return {}; + } + + const auto account = m_accounts[index.row()]; + + if (role == ConnectionRole) { + return QVariant::fromValue(account); + } + + return {}; +} + +int AccountRegistry::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) { + return 0; + } + + return m_accounts.count(); +} + +QHash AccountRegistry::roleNames() const +{ + return {{ConnectionRole, "connection"}}; +} + +bool AccountRegistry::isEmpty() const +{ + return m_accounts.isEmpty(); +} + +int AccountRegistry::count() const +{ + return m_accounts.count(); +} + +const QVector AccountRegistry::accounts() const +{ + return m_accounts; +} + +Connection *AccountRegistry::get(const QString &userId) +{ + for (const auto &connection : m_accounts) { + if (connection->userId() == userId) { + return connection; + } + } + return nullptr; +} diff --git a/src/accountregistry.h b/src/accountregistry.h new file mode 100644 index 000000000..ecb1dbdd3 --- /dev/null +++ b/src/accountregistry.h @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2020 Kitsune Ral +// SPDX-FileCopyrightText: Tobias Fella +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include +#include + +namespace Quotient +{ +class Connection; + +class AccountRegistry : public QAbstractListModel +{ + Q_OBJECT +public: + enum EventRoles { + ConnectionRole = Qt::UserRole + 1, + }; + + static AccountRegistry &instance() + { + static AccountRegistry _instance; + return _instance; + } + + const QVector accounts() const; + void add(Connection *a); + void drop(Connection *a); + bool isLoggedIn(const QString &userId) const; + bool isEmpty() const; + int count() const; + bool contains(Connection *) const; + Connection *get(const QString &userId); + + [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + [[nodiscard]] QHash roleNames() const override; + +private: + AccountRegistry(); + + QVector m_accounts; +}; +} diff --git a/src/controller.cpp b/src/controller.cpp index 2def31b7f..67d32307a 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -34,6 +34,7 @@ #include +#include "accountregistry.h" #include "csapi/account-data.h" #include "csapi/content-repo.h" #include "csapi/joining.h" @@ -45,6 +46,8 @@ #include "neochatuser.h" #include "roommanager.h" #include "settings.h" +#include "utils.h" + #include #if defined(Q_OS_WIN) || defined(Q_OS_MAC) @@ -118,7 +121,7 @@ Controller::Controller(QObject *parent) Controller::~Controller() { - for (auto c : qAsConst(m_connections)) { + for (auto c : AccountRegistry::instance().accounts()) { c->saveState(); } } @@ -186,28 +189,24 @@ void Controller::logout(Connection *conn, bool serverSideLogout) job.start(); loop.exec(); - conn->stopSync(); - Q_EMIT conn->stateChanged(); - Q_EMIT conn->loggedOut(); - if (conn == activeConnection() && !m_connections.isEmpty()) { - setActiveConnection(m_connections[0]); + if (conn == activeConnection() && AccountRegistry::instance().count() > 1) { + setActiveConnection(AccountRegistry::instance().accounts()[0]); } else { setActiveConnection(nullptr); } if (!serverSideLogout) { return; } - auto logoutJob = conn->callApi(); - connect(logoutJob, &LogoutJob::failure, this, [=] { - Q_EMIT errorOccured(i18n("Server-side Logout Failed: %1", logoutJob->errorString())); - }); + conn->logout(); } void Controller::addConnection(Connection *c) { Q_ASSERT_X(c, __FUNCTION__, "Attempt to add a null connection"); - m_connections += c; +#ifndef QUOTIENT_07 + AccountRegistry::instance().add(c); +#endif c->setLazyLoading(true); @@ -240,11 +239,16 @@ void Controller::addConnection(Connection *c) void Controller::dropConnection(Connection *c) { Q_ASSERT_X(c, __FUNCTION__, "Attempt to drop a null connection"); - m_connections.removeOne(c); + +#ifndef QUOTIENT_07 + AccountRegistry::instance().drop(c); +#endif Q_EMIT connectionDropped(c); Q_EMIT accountCountChanged(); +#ifndef QUOTIENT_07 c->deleteLater(); +#endif } void Controller::invokeLogin() @@ -491,14 +495,9 @@ NeochatChangePasswordJob::NeochatChangePasswordJob(const QString &newPassword, b setRequestData(_data); } -QVector Controller::connections() const -{ - return m_connections; -} - int Controller::accountCount() const { - return m_connections.count(); + return AccountRegistry::instance().count(); } bool Controller::quitOnLastWindowClosed() diff --git a/src/controller.h b/src/controller.h index 67077ad80..228193e99 100644 --- a/src/controller.h +++ b/src/controller.h @@ -40,8 +40,6 @@ class Controller : public QObject public: static Controller &instance(); - [[nodiscard]] QVector connections() const; - void setActiveConnection(Connection *connection); [[nodiscard]] Connection *activeConnection() const; @@ -97,7 +95,6 @@ private: explicit Controller(QObject *parent = nullptr); ~Controller() override; - QVector m_connections; QPointer m_connection; bool m_busy = false; diff --git a/src/main.cpp b/src/main.cpp index f42b5ed33..23aedb020 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,7 +30,7 @@ #include "neochat-version.h" -#include "accountlistmodel.h" +#include "accountregistry.h" #include "actionshandler.h" #include "blurhashimageprovider.h" #include "chatboxhelper.h" @@ -180,7 +180,7 @@ int main(int argc, char *argv[]) qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "ChatBoxHelper", &chatBoxHelper); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "EmojiModel", new EmojiModel(&app)); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "CommandModel", new CommandModel(&app)); - qmlRegisterType("org.kde.neochat", 1, 0, "AccountListModel"); + qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "AccountRegistry", &Quotient::AccountRegistry::instance()); qmlRegisterType("org.kde.neochat", 1, 0, "ActionsHandler"); qmlRegisterType("org.kde.neochat", 1, 0, "ChatDocumentHandler"); qmlRegisterType("org.kde.neochat", 1, 0, "SpellcheckHighlighter");