From 310e9b7ba34c4a3aef1e80e320788c79f64c4d42 Mon Sep 17 00:00:00 2001 From: Tobias Fella Date: Tue, 31 May 2022 22:19:38 +0200 Subject: [PATCH] Remove compatibility with libQuotient 0.6 --- CMakeLists.txt | 14 +- autotests/texthandlertest.cpp | 4 - src/CMakeLists.txt | 14 +- src/controller.cpp | 345 +++--------------- src/controller.h | 26 +- src/filetransferpseudojob.cpp | 1 + src/joinrulesevent.h | 13 - src/login.cpp | 16 +- src/main.cpp | 30 +- src/models/actionsmodel.cpp | 16 +- src/models/customemojimodel.cpp | 21 +- src/models/devicesmodel.cpp | 2 +- src/models/messageeventmodel.cpp | 58 +-- src/models/publicroomlistmodel.cpp | 8 - src/models/roomlistmodel.cpp | 72 +--- src/models/roomlistmodel.h | 3 - src/models/searchmodel.cpp | 36 +- src/models/searchmodel.h | 4 - src/models/serverlistmodel.cpp | 4 - src/models/statemodel.cpp | 8 +- src/models/userdirectorylistmodel.cpp | 4 - src/models/userlistmodel.cpp | 21 +- src/neochataccountregistry.cpp | 105 ------ src/neochataccountregistry.h | 54 --- src/neochatroom.cpp | 225 +++--------- src/neochatroom.h | 4 - src/notificationsmanager.cpp | 8 - src/pollhandler.cpp | 4 +- src/qml/Dialog/ConfirmLogout.qml | 2 +- src/qml/Menu/GlobalMenu.qml | 4 +- src/qml/Page/RoomList/UserInfo.qml | 2 +- src/qml/Page/RoomListPage.qml | 502 ++++++++++++++++++++++++++ src/qml/Settings/AccountsPage.qml | 2 +- src/qml/main.qml | 18 +- src/roommanager.cpp | 9 - src/roommanager.h | 3 - src/spacehierarchycache.cpp | 8 +- src/stickerevent.cpp | 35 -- src/stickerevent.h | 43 --- 39 files changed, 678 insertions(+), 1070 deletions(-) delete mode 100644 src/neochataccountregistry.cpp delete mode 100644 src/neochataccountregistry.h create mode 100644 src/qml/Page/RoomListPage.qml delete mode 100644 src/stickerevent.cpp delete mode 100644 src/stickerevent.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f8b5d373..04290cdc8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,12 +68,6 @@ set_package_properties(KF${QT_MAJOR_VERSION}Kirigami2 PROPERTIES ) find_package(KF${QT_MAJOR_VERSION}KirigamiAddons 0.7.2 REQUIRED) -find_package(Qt${QT_MAJOR_VERSION}Keychain) -set_package_properties(Qt${QT_MAJOR_VERSION}Keychain PROPERTIES - TYPE REQUIRED - PURPOSE "Secure storage of account secrets" -) - if(ANDROID) find_package(OpenSSL) set_package_properties(OpenSSL PROPERTIES @@ -93,7 +87,7 @@ if (NOT ANDROID AND NOT WIN32 AND NOT APPLE) find_package(KF${QT_MAJOR_VERSION}DBusAddons ${KF_MIN_VERSION} REQUIRED) endif() -find_package(Quotient 0.6) +find_package(Quotient 0.7) set_package_properties(Quotient PROPERTIES TYPE REQUIRED DESCRIPTION "Qt wrapper around Matrix API" @@ -131,9 +125,7 @@ set_package_properties(KF${QT_MAJOR_VERSION}DocTools PROPERTIES DESCRIPTION TYPE OPTIONAL ) -if(NOT Quotient_VERSION_MINOR GREATER 6) - cmake_policy(SET CMP0063 OLD) -endif() +find_package(Sqlite3) if(ANDROID) find_package(Sqlite3) @@ -150,7 +142,7 @@ install(FILES org.kde.neochat.tray.svg DESTINATION ${KDE_INSTALL_FULL_ICONDIR}/h add_definitions(-DQT_NO_FOREACH) add_subdirectory(src) -if (BUILD_TESTING AND Quotient_VERSION_MINOR GREATER 6) +if (BUILD_TESTING) find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test) add_subdirectory(autotests) endif() diff --git a/autotests/texthandlertest.cpp b/autotests/texthandlertest.cpp index a7de31615..01e29e9d7 100644 --- a/autotests/texthandlertest.cpp +++ b/autotests/texthandlertest.cpp @@ -65,7 +65,6 @@ private Q_SLOTS: void receiveRichEdited(); }; -#ifdef QUOTIENT_07 void TextHandlerTest::initTestCase() { connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org")); @@ -198,7 +197,6 @@ void TextHandlerTest::initTestCase() SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, json.object()); room->update(std::move(roomData)); } -#endif void TextHandlerTest::allowedAttributes() { @@ -473,7 +471,6 @@ void TextHandlerTest::receiveRichtextIn() QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString); } -#ifdef QUOTIENT_07 void TextHandlerTest::receiveRichMxcUrl() { const QString testInputString = QStringLiteral( @@ -491,7 +488,6 @@ void TextHandlerTest::receiveRichMxcUrl() QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText, room, room->messageEvents().at(0).get()), testOutputString); } -#endif /** * For when your rich input string has a plain text url left in. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a2db43d12..471528f67 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,7 +30,6 @@ add_library(neochat STATIC models/devicesmodel.cpp filetypesingleton.cpp login.cpp - stickerevent.cpp models/webshortcutmodel.cpp blurhash.cpp blurhashimageprovider.cpp @@ -47,8 +46,12 @@ add_library(neochat STATIC filetransferpseudojob.cpp models/searchmodel.cpp texthandler.cpp + pollevent.cpp + pollhandler.cpp ) +target_compile_definitions(neochat PUBLIC "-DQT_NO_KEYWORDS") + add_executable(neochat-app main.cpp res.qrc @@ -60,14 +63,6 @@ target_link_libraries(neochat-app PRIVATE neochat ) -if(Quotient_VERSION_MINOR GREATER 6) - target_compile_definitions(neochat PUBLIC QUOTIENT_07) - target_sources(neochat PRIVATE pollevent.cpp pollhandler.cpp) -else() - target_compile_definitions(neochat PUBLIC QUOTIENT_VERSION=\"${Quotient_VERSION}\") - target_sources(neochat PRIVATE neochataccountregistry.cpp) -endif() - ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png) target_sources(neochat-app PRIVATE ${NEOCHAT_ICON}) @@ -95,6 +90,7 @@ endif() target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR}) target_link_libraries(neochat PUBLIC Qt::Core Qt::Quick Qt::Qml Qt::Gui Qt::Multimedia Qt::Network Qt::QuickControls2 KF${QT_MAJOR_VERSION}::I18n KF${QT_MAJOR_VERSION}::Kirigami2 KF${QT_MAJOR_VERSION}::Notifications KF${QT_MAJOR_VERSION}::ConfigCore KF${QT_MAJOR_VERSION}::ConfigGui KF${QT_MAJOR_VERSION}::CoreAddons KF${QT_MAJOR_VERSION}::SonnetCore KF${QT_MAJOR_VERSION}::ItemModels Quotient cmark::cmark ${QTKEYCHAIN_LIBRARIES} QCoro::Core) + kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc) if(NEOCHAT_FLATPAK) diff --git a/src/controller.cpp b/src/controller.cpp index b71953fd7..641febb50 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -31,11 +31,7 @@ #include -#ifdef QUOTIENT_07 -#include "accountregistry.h" -#else -#include "neochataccountregistry.h" -#endif +#include #include #include @@ -43,11 +39,8 @@ #include #include #include - -#ifdef QUOTIENT_07 #include #include -#endif #include "neochatconfig.h" #include "neochatroom.h" @@ -56,6 +49,8 @@ #include "roommanager.h" #include "windowcontroller.h" +#include + #if defined(Q_OS_WIN) || defined(Q_OS_MAC) #include "trayicon.h" #elif !defined(Q_OS_ANDROID) @@ -93,10 +88,28 @@ Controller::Controller(QObject *parent) }); #endif - QTimer::singleShot(0, this, [this] { - invokeLogin(); + connectUntil(&Accounts, &AccountRegistry::rowsInserted, this, [this]() { + if (auto *connection = Accounts.get(NeoChatConfig::self()->activeConnection())) { + connectSingleShot(connection, &Connection::loadedRoomState, this, [this, connection]() { + setActiveConnection(connection); + }); + return true; + } + return false; }); + connect(&Accounts, &AccountRegistry::rowsRemoved, this, [this]() { + if (!Accounts.isLoggedIn(NeoChatConfig::self()->activeConnection())) { + if (Accounts.size() > 0) { + setActiveConnection(Accounts.accounts().at(0)); + } else { + setActiveConnection(nullptr); + } + } + }); + + QMetaObject::invokeMethod(&Accounts, &AccountRegistry::invokeLogin); + QObject::connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit, QGuiApplication::instance(), [] { NeoChatConfig::self()->save(); }); @@ -125,23 +138,29 @@ Controller::Controller(QObject *parent) } #endif - connect(&AccountRegistry::instance(), &AccountRegistry::accountCountChanged, this, &Controller::activeConnectionIndexChanged); + connect(&Accounts, &AccountRegistry::accountCountChanged, this, &Controller::activeConnectionIndexChanged); -#ifdef QUOTIENT_07 static int oldAccountCount = 0; - connect(&AccountRegistry::instance(), &AccountRegistry::accountCountChanged, this, [=]() { - if (AccountRegistry::instance().size() > oldAccountCount) { - auto connection = AccountRegistry::instance().accounts()[AccountRegistry::instance().size() - 1]; - connect(connection, &Connection::syncDone, this, [=]() { - handleNotifications(connection); + connect(&Accounts, &AccountRegistry::accountCountChanged, this, [this]() { + if (Accounts.size() > oldAccountCount) { + auto connection = Accounts.accounts()[Accounts.size() - 1]; + connect(connection, &Connection::syncDone, this, [this, connection]() { + bool changes = false; + for (const auto &room : connection->allRooms()) { + if (m_notificationCounts[room] != room->unreadStats().notableCount) { + m_notificationCounts[room] = room->unreadStats().notableCount; + changes = true; + } + } + if (changes) { + handleNotifications(connection); + } }); } - oldAccountCount = AccountRegistry::instance().size(); + oldAccountCount = Accounts.size(); }); -#endif } -#ifdef QUOTIENT_07 void Controller::handleNotifications(QPointer connection) { static QStringList initial; @@ -215,7 +234,6 @@ void Controller::handleNotifications(QPointer connection) } }); } -#endif Controller &Controller::instance() { @@ -228,239 +246,10 @@ void Controller::showWindow() WindowController::instance().showAndRaiseWindow(QString()); } -void Controller::loginWithAccessToken(const QString &serverAddr, const QString &user, const QString &token, const QString &deviceName) -{ - if (user.isEmpty() || token.isEmpty()) { - return; - } - - QUrl serverUrl(serverAddr); - - auto conn = new Connection(); - if (serverUrl.isValid()) { - conn->setHomeserver(serverUrl); - } - - connect(conn, &Connection::connected, this, [this, conn, deviceName] { - AccountSettings account(conn->userId()); - account.setKeepLoggedIn(true); - account.setHomeserver(conn->homeserver()); - account.setDeviceId(conn->deviceId()); - account.setDeviceName(deviceName); - if (!saveAccessTokenToKeyChain(account, conn->accessToken())) { - qWarning() << "Couldn't save access token"; - } - account.sync(); - addConnection(conn); - setActiveConnection(conn); - }); - connect(conn, &Connection::networkError, this, [this](QString error, const QString &, int, int) { - Q_EMIT errorOccured(i18n("Network Error: %1", error)); - }); - conn->assumeIdentity(user, token, deviceName); -} - -void Controller::logout(Connection *conn, bool serverSideLogout) -{ - if (!conn) { - qCritical() << "Attempt to logout null connection"; - return; - } - - SettingsGroup("Accounts").remove(conn->userId()); - - QKeychain::DeletePasswordJob job(qAppName()); - job.setAutoDelete(true); - job.setKey(conn->userId()); - QEventLoop loop; - QKeychain::DeletePasswordJob::connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit); - job.start(); - loop.exec(); - - if (AccountRegistry::instance().count() > 1) { - // Only set the connection if the the account being logged out is currently active - if (conn == activeConnection()) { - setActiveConnection(AccountRegistry::instance().accounts()[0]); - } - } else { - setActiveConnection(nullptr); - } - if (!serverSideLogout) { - return; - } - conn->logout(); -} - -void Controller::addConnection(Connection *c) -{ - Q_ASSERT_X(c, __FUNCTION__, "Attempt to add a null connection"); - -#ifndef QUOTIENT_07 - AccountRegistry::instance().add(c); -#endif - - c->setLazyLoading(true); - - connect(c, &Connection::syncDone, this, [this, c] { - setBusy(false); - - Q_EMIT syncDone(); - - c->sync(30000); - c->saveState(); - }); - connect(c, &Connection::loggedOut, this, [this, c] { - dropConnection(c); - }); - - connect(c, &Connection::requestFailed, this, [this](BaseJob *job) { - if (job->error() == BaseJob::UserConsentRequiredError) { - Q_EMIT userConsentRequired(job->errorUrl()); - } - }); - - setBusy(true); - - c->sync(); - - Q_EMIT connectionAdded(c); - Q_EMIT accountCountChanged(); -} - -void Controller::dropConnection(Connection *c) -{ - Q_ASSERT_X(c, __FUNCTION__, "Attempt to drop a null connection"); - -#ifndef QUOTIENT_07 - AccountRegistry::instance().drop(c); -#endif - - Q_EMIT connectionDropped(c); - Q_EMIT accountCountChanged(); -#ifndef QUOTIENT_07 - c->deleteLater(); -#endif -} - -void Controller::invokeLogin() -{ - const auto accounts = SettingsGroup("Accounts").childGroups(); - QString id = NeoChatConfig::self()->activeConnection(); - for (const auto &accountId : accounts) { - AccountSettings account{accountId}; - if (id.isEmpty()) { - // handle case where the account config is empty - id = accountId; - } - if (!account.homeserver().isEmpty()) { - auto accessTokenLoadingJob = loadAccessTokenFromKeyChain(account); - connect(accessTokenLoadingJob, &QKeychain::Job::finished, this, [accountId, id, this, accessTokenLoadingJob](QKeychain::Job *) { - AccountSettings account{accountId}; - QString accessToken; - if (accessTokenLoadingJob->error() == QKeychain::Error::NoError) { - accessToken = accessTokenLoadingJob->binaryData(); - } else { - return; - } - - auto connection = new Connection(account.homeserver()); - connect(connection, &Connection::connected, this, [this, connection, id] { - connection->loadState(); - addConnection(connection); - if (connection->userId() == id) { - setActiveConnection(connection); - connectSingleShot(connection, &Connection::syncDone, this, &Controller::initiated); - } - }); - connect(connection, &Connection::loginError, this, [this, connection](const QString &error, const QString &) { - if (error == "Unrecognised access token") { - Q_EMIT errorOccured(i18n("Login Failed: Access Token invalid or revoked")); - logout(connection, false); - } else if (error == "Connection closed") { - Q_EMIT errorOccured(i18n("Login Failed: %1", error)); - // Failed due to network connection issue. This might happen when the homeserver is - // temporary down, or the user trying to re-launch NeoChat in a network that cannot - // connect to the homeserver. In this case, we don't want to do logout(). - } else { - Q_EMIT errorOccured(i18n("Login Failed: %1", error)); - logout(connection, true); - } - Q_EMIT initiated(); - }); - connect(connection, &Connection::networkError, this, [this](const QString &error, const QString &, int, int) { - Q_EMIT errorOccured(i18n("Network Error: %1", error)); - }); - connection->assumeIdentity(account.userId(), accessToken, account.deviceId()); - }); - } - } - if (accounts.isEmpty()) { - Q_EMIT initiated(); - } -} - -QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const AccountSettings &account) -{ - qDebug() << "Reading access token from the keychain for" << account.userId(); - auto job = new QKeychain::ReadPasswordJob(qAppName(), this); - job->setKey(account.userId()); - - // Handling of errors - connect(job, &QKeychain::Job::finished, this, [this, &account, job]() { - if (job->error() == QKeychain::Error::NoError) { - return; - } - - switch (job->error()) { - case QKeychain::EntryNotFound: - Q_EMIT globalErrorOccured(i18n("Access token wasn't found"), i18n("Maybe it was deleted?")); - break; - case QKeychain::AccessDeniedByUser: - case QKeychain::AccessDenied: - Q_EMIT globalErrorOccured(i18n("Access to keychain was denied."), i18n("Please allow NeoChat to read the access token")); - break; - case QKeychain::NoBackendAvailable: - Q_EMIT globalErrorOccured(i18n("No keychain available."), i18n("Please install a keychain, e.g. KWallet or GNOME keyring on Linux")); - break; - case QKeychain::OtherError: - Q_EMIT globalErrorOccured(i18n("Unable to read access token"), job->errorString()); - break; - default: - break; - } - }); - job->start(); - - return job; -} - -bool Controller::saveAccessTokenToKeyChain(const AccountSettings &account, const QByteArray &accessToken) -{ - qDebug() << "Save the access token to the keychain for " << account.userId(); - QKeychain::WritePasswordJob job(qAppName()); - job.setAutoDelete(false); - job.setKey(account.userId()); - job.setBinaryData(accessToken); - QEventLoop loop; - QKeychain::WritePasswordJob::connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit); - job.start(); - loop.exec(); - - if (job.error()) { - qWarning() << "Could not save access token to the keychain: " << qPrintable(job.errorString()); - return false; - } - return true; -} - void Controller::changeAvatar(Connection *conn, const QUrl &localFile) { auto job = conn->uploadFile(localFile.toLocalFile()); -#ifdef QUOTIENT_07 if (isJobPending(job)) { -#else - if (isJobRunning(job)) { -#endif connect(job, &BaseJob::success, this, [conn, job] { conn->callApi(conn->userId(), job->contentUri()); }); @@ -487,7 +276,7 @@ bool Controller::supportSystemTray() const void Controller::changePassword(Connection *connection, const QString ¤tPassword, const QString &newPassword) { - NeochatChangePasswordJob *job = connection->callApi(newPassword, false); + auto *job = connection->callApi(newPassword, false); connect(job, &BaseJob::result, this, [this, job, currentPassword, newPassword, connection] { if (job->error() == 103) { QJsonObject replyData = job->jsonData(); @@ -498,7 +287,7 @@ void Controller::changePassword(Connection *connection, const QString ¤tPa authData["user"] = connection->user()->id(); QJsonObject identifier = {{"type", "m.id.user"}, {"user", connection->user()->id()}}; authData["identifier"] = identifier; - NeochatChangePasswordJob *innerJob = connection->callApi(newPassword, false, authData); + auto *innerJob = connection->callApi(newPassword, false, authData); connect(innerJob, &BaseJob::success, this, [this]() { Q_EMIT passwordStatus(PasswordStatus::Success); }); @@ -518,11 +307,7 @@ bool Controller::setAvatar(Connection *connection, const QUrl &avatarSource) User *localUser = connection->user(); QString decoded = avatarSource.path(); if (decoded.isEmpty()) { -#ifdef QUOTIENT_07 connection->callApi(localUser->id(), avatarSource); -#else - connection->callApi(localUser->id(), QString()); -#endif return true; } if (QImageReader(decoded).read().isNull()) { @@ -533,11 +318,7 @@ bool Controller::setAvatar(Connection *connection, const QUrl &avatarSource) } NeochatChangePasswordJob::NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const Omittable &auth) -#ifdef QUOTIENT_07 : BaseJob(HttpVerb::Post, QStringLiteral("ChangePasswordJob"), "/_matrix/client/r0/account/password") -#else - : BaseJob(HttpVerb::Post, QStringLiteral("ChangePasswordJob"), QStringLiteral("/_matrix/client/r0/account/password")) -#endif { QJsonObject _data; addParam<>(_data, QStringLiteral("new_password"), newPassword); @@ -546,9 +327,12 @@ NeochatChangePasswordJob::NeochatChangePasswordJob(const QString &newPassword, b setRequestData(_data); } -int Controller::accountCount() const +NeochatDeleteDeviceJob::NeochatDeleteDeviceJob(const QString &deviceId, const Omittable &auth) + : Quotient::BaseJob(HttpVerb::Delete, QStringLiteral("DeleteDeviceJob"), QStringLiteral("/matrix/client/r0/devices/%1").arg(deviceId).toLatin1()) { - return AccountRegistry::instance().count(); + QJsonObject _data; + addParam(_data, QStringLiteral("auth"), auth); + setRequestData(std::move(_data)); } bool Controller::quitOnLastWindowClosed() @@ -629,18 +413,6 @@ void Controller::saveWindowGeometry() WindowController::instance().saveGeometry(); } -NeochatDeleteDeviceJob::NeochatDeleteDeviceJob(const QString &deviceId, const Omittable &auth) -#ifdef QUOTIENT_07 - : Quotient::BaseJob(HttpVerb::Delete, QStringLiteral("DeleteDeviceJob"), QStringLiteral("/_matrix/client/r0/devices/%1").arg(deviceId).toLatin1()) -#else - : Quotient::BaseJob(HttpVerb::Delete, QStringLiteral("DeleteDeviceJob"), QStringLiteral("/_matrix/client/r0/devices/%1").arg(deviceId)) -#endif -{ - QJsonObject _data; - addParam(_data, QStringLiteral("auth"), auth); - setRequestData(std::move(_data)); -} - void Controller::createRoom(const QString &name, const QString &topic) { auto createRoomJob = m_connection->createRoom(Connection::PublishRoom, "", name, topic, QStringList()); @@ -651,7 +423,7 @@ void Controller::createRoom(const QString &name, const QString &topic) this, &Controller::roomAdded, this, - [this](NeoChatRoom *room) { + [](NeoChatRoom *room) { RoomManager::instance().enterRoom(room); }, Qt::QueuedConnection); @@ -731,11 +503,7 @@ QString Controller::plainText(QQuickTextDocument *document) const bool Controller::encryptionSupported() const { -#ifdef QUOTIENT_07 return Quotient::encryptionSupported(); -#else - return false; -#endif } void Controller::forceRefreshTextDocument(QQuickTextDocument *textDocument, QQuickItem *item) @@ -776,29 +544,10 @@ void Controller::setApplicationProxy() int Controller::activeConnectionIndex() const { -#ifdef QUOTIENT_07 auto result = std::find_if(Accounts.accounts().begin(), Accounts.accounts().end(), [this](const auto &it) { return it == m_connection; }); return result - Accounts.accounts().begin(); -#else - for (int i = 0; i < AccountRegistry::instance().rowCount(); i++) { - if (AccountRegistry::instance().data(AccountRegistry::instance().index(i, 0), AccountRegistry::UserIdRole).toString() == m_connection->userId()) { - return i; - } - } - return 0; -#endif -} - -int Controller::quotientMinorVersion() const -{ -// TODO libQuotient 0.7: Replace with version function from libQuotient -#ifdef QUOTIENT_07 - return 7; -#else - return 6; -#endif } bool Controller::isFlatpak() const diff --git a/src/controller.h b/src/controller.h index 90806d374..7abeff7c0 100644 --- a/src/controller.h +++ b/src/controller.h @@ -23,15 +23,9 @@ class Connection; class Room; } -namespace QKeychain -{ -class ReadPasswordJob; -} - class Controller : public QObject { Q_OBJECT - Q_PROPERTY(int accountCount READ accountCount NOTIFY accountCountChanged) Q_PROPERTY(bool quitOnLastWindowClosed READ quitOnLastWindowClosed WRITE setQuitOnLastWindowClosed NOTIFY quitOnLastWindowClosedChanged) Q_PROPERTY(Quotient::Connection *activeConnection READ activeConnection WRITE setActiveConnection NOTIFY activeConnectionChanged) Q_PROPERTY(bool busy READ busy WRITE setBusy NOTIFY busyChanged) @@ -40,7 +34,6 @@ class Controller : public QObject Q_PROPERTY(bool isOnline READ isOnline NOTIFY isOnlineChanged) Q_PROPERTY(bool encryptionSupported READ encryptionSupported CONSTANT) Q_PROPERTY(int activeConnectionIndex READ activeConnectionIndex NOTIFY activeConnectionIndexChanged) - Q_PROPERTY(int quotientMinorVersion READ quotientMinorVersion CONSTANT) Q_PROPERTY(bool isFlatpak READ isFlatpak CONSTANT) public: @@ -49,17 +42,10 @@ public: void setActiveConnection(Quotient::Connection *connection); [[nodiscard]] Quotient::Connection *activeConnection() const; - void addConnection(Quotient::Connection *c); - void dropConnection(Quotient::Connection *c); - - Q_INVOKABLE void loginWithAccessToken(const QString &, const QString &, const QString &, const QString &); - Q_INVOKABLE void changePassword(Quotient::Connection *connection, const QString ¤tPassword, const QString &newPassword); Q_INVOKABLE bool setAvatar(Quotient::Connection *connection, const QUrl &avatarSource); - [[nodiscard]] int accountCount() const; - [[nodiscard]] static bool quitOnLastWindowClosed(); void setQuitOnLastWindowClosed(bool value); @@ -68,8 +54,6 @@ public: [[nodiscard]] bool supportSystemTray() const; - bool saveAccessTokenToKeyChain(const Quotient::AccountSettings &account, const QByteArray &accessToken); - int activeConnectionIndex() const; enum PasswordStatus { @@ -100,7 +84,6 @@ public: Q_INVOKABLE void setApplicationProxy(); - int quotientMinorVersion() const; bool isFlatpak() const; private: @@ -110,20 +93,15 @@ private: bool m_busy = false; TrayIcon *m_trayIcon = nullptr; - QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const Quotient::AccountSettings &account); - void loadSettings(); void saveSettings() const; bool m_isOnline = true; QMap m_notificationCounts; bool hasWindowSystem() const; -#ifdef QUOTIENT_07 void handleNotifications(QPointer connection); -#endif private Q_SLOTS: - void invokeLogin(); void showWindow(); Q_SIGNALS: @@ -136,7 +114,6 @@ Q_SIGNALS: void syncDone(); void connectionAdded(Quotient::Connection *_t1); void connectionDropped(Quotient::Connection *_t1); - void accountCountChanged(); void initiated(); void notificationClicked(const QString &_t1, const QString &_t2); void quitOnLastWindowClosedChanged(); @@ -154,13 +131,12 @@ Q_SIGNALS: void roomAdded(NeoChatRoom *room); public Q_SLOTS: - void logout(Quotient::Connection *conn, bool serverSideLogout); void changeAvatar(Quotient::Connection *conn, const QUrl &localFile); static void markAllMessagesAsRead(Quotient::Connection *conn); void saveWindowGeometry(); }; -// TODO libQuotient 0.7: Drop +// TODO libQuotient 0.?: Drop class NeochatChangePasswordJob : public Quotient::BaseJob { public: diff --git a/src/filetransferpseudojob.cpp b/src/filetransferpseudojob.cpp index 66b75ffd7..15ea161ca 100644 --- a/src/filetransferpseudojob.cpp +++ b/src/filetransferpseudojob.cpp @@ -25,6 +25,7 @@ void FileTransferPseudoJob::fileTransferProgress(QString id, qint64 progress, qi void FileTransferPseudoJob::fileTransferCompleted(QString id, QUrl localFile) { + Q_UNUSED(localFile); if (id != m_eventId) { return; } diff --git a/src/joinrulesevent.h b/src/joinrulesevent.h index 4ffedb19d..ca2cc3137 100644 --- a/src/joinrulesevent.h +++ b/src/joinrulesevent.h @@ -7,30 +7,17 @@ namespace Quotient { -#ifdef QUOTIENT_07 class JoinRulesEvent : public StateEvent -#else -class JoinRulesEvent : public StateEventBase -#endif { public: -#ifdef QUOTIENT_07 QUO_EVENT(JoinRulesEvent, "m.room.join_rules") -#else - DEFINE_EVENT_TYPEID("m.room.join_rules", JoinRulesEvent) -#endif explicit JoinRulesEvent(const QJsonObject &obj) -#ifdef QUOTIENT_07 : StateEvent(obj) -#else - : StateEventBase(typeId(), obj) -#endif { } QString joinRule() const; QJsonArray allow() const; }; -REGISTER_EVENT_TYPE(JoinRulesEvent) } diff --git a/src/login.cpp b/src/login.cpp index 1121875f9..ed07535f8 100644 --- a/src/login.cpp +++ b/src/login.cpp @@ -3,16 +3,13 @@ #include "login.h" -#ifdef QUOTIENT_07 #include -#else -#include "neochataccountregistry.h" -#endif #include #include #include "controller.h" +#include "neochatroom.h" #include @@ -43,7 +40,7 @@ void Login::init() return; } - m_isLoggedIn = AccountRegistry::instance().isLoggedIn(m_matrixId); + m_isLoggedIn = Accounts.isLoggedIn(m_matrixId); Q_EMIT isLoggedInChanged(); if (m_isLoggedIn) { return; @@ -74,11 +71,7 @@ void Login::init() account.setHomeserver(m_connection->homeserver()); account.setDeviceId(m_connection->deviceId()); account.setDeviceName(m_deviceName); - if (!Controller::instance().saveAccessTokenToKeyChain(account, m_connection->accessToken())) { - qWarning() << "Couldn't save access token"; - } account.sync(); - Controller::instance().addConnection(m_connection); Controller::instance().setActiveConnection(m_connection); m_connection = nullptr; }); @@ -97,8 +90,9 @@ void Login::init() Q_EMIT Controller::instance().globalErrorOccured(i18n("Network Error"), std::move(error)); }); - connectSingleShot(m_connection, &Connection::syncDone, this, [this]() { - Q_EMIT Controller::instance().initiated(); + connectSingleShot(m_connection, &Connection::loadedRoomState, this, [this]() { + Controller::instance().setActiveConnection(m_connection); + // TODO close settings window }); } diff --git a/src/main.cpp b/src/main.cpp index e6d53ed46..59089d527 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,15 +28,14 @@ #include "neochat-version.h" -#ifdef QUOTIENT_07 #include -#else -#include "neochataccountregistry.h" -#endif - #include #include #include +#include +#include +#include +#include #include "actionshandler.h" #include "blurhashimageprovider.h" @@ -68,16 +67,11 @@ #include "neochatroom.h" #include "neochatuser.h" #include "notificationsmanager.h" -#ifdef QUOTIENT_07 #include "pollhandler.h" -#endif #include "roommanager.h" #include "spacehierarchycache.h" #include "urlhelper.h" #include "windowcontroller.h" -#ifdef QUOTIENT_07 -#include -#endif #ifdef HAVE_COLORSCHEME #include "colorschemer.h" #endif @@ -85,6 +79,7 @@ #include "models/statemodel.h" #include "neochatuser.h" + #ifdef HAVE_RUNNER #include "runner.h" #include @@ -165,14 +160,10 @@ int main(int argc, char *argv[]) about.addComponent(QStringLiteral("libQuotient"), i18n("A Qt5 library to write cross-platform clients for Matrix"), -#ifdef QUOTIENT_07 i18nc(" (built against )", "%1 (built against %2)", Quotient::versionString(), QStringLiteral(Quotient_VERSION_STRING)), -#else - QStringLiteral(QUOTIENT_VERSION), -#endif QStringLiteral("https://github.com/quotient-im/libquotient"), KAboutLicense::LGPL_V2_1); @@ -209,11 +200,8 @@ int main(int argc, char *argv[]) qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "LoginHelper", login); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "UrlHelper", &urlHelper); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "EmojiModel", &EmojiModel::instance()); -#ifdef QUOTIENT_07 - qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "AccountRegistry", &Quotient::Accounts); -#else - qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "AccountRegistry", &Quotient::AccountRegistry::instance()); -#endif + qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Accounts", &Quotient::Accounts); + qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Accounts", &Quotient::Accounts); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "SpaceHierarchyCache", &SpaceHierarchyCache::instance()); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "CustomEmojiModel", &CustomEmojiModel::instance()); qmlRegisterType("org.kde.neochat", 1, 0, "ActionsHandler"); @@ -235,9 +223,7 @@ int main(int argc, char *argv[]) qmlRegisterType("org.kde.neochat", 1, 0, "CompletionModel"); qmlRegisterType("org.kde.neochat", 1, 0, "StateModel"); qmlRegisterType("org.kde.neochat", 1, 0, "SearchModel"); -#ifdef QUOTIENT_07 qmlRegisterType("org.kde.neochat", 1, 0, "PollHandler"); -#endif qmlRegisterType("org.kde.neochat", 1, 0, "KeywordNotificationRuleModel"); qmlRegisterUncreatableType("org.kde.neochat", 1, 0, "RoomMessageEvent", "ENUM"); qmlRegisterUncreatableType("org.kde.neochat", 1, 0, "PushNotificationState", "ENUM"); @@ -256,12 +242,10 @@ int main(int argc, char *argv[]) qRegisterMetaType("NeoChatUser*"); qRegisterMetaType("GetRoomEventsJob*"); qRegisterMetaType("QMimeType"); -#ifdef QUOTIENT_07 #ifdef Quotient_E2EE_ENABLED qRegisterMetaType("KeyVerificationSession*"); qmlRegisterUncreatableType("org.kde.neochat", 1, 0, "KeyVerificationSession", {}); qRegisterMetaType>("QVector"); -#endif #endif qmlRegisterSingletonType("org.kde.neochat", 1, 0, "About", [](QQmlEngine *engine, QJSEngine *) -> QJSValue { return engine->toScriptValue(KAboutData::applicationData()); diff --git a/src/models/actionsmodel.cpp b/src/models/actionsmodel.cpp index 6fb1d0982..07cee7ee8 100644 --- a/src/models/actionsmodel.cpp +++ b/src/models/actionsmodel.cpp @@ -192,7 +192,6 @@ QVector actions{ Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'' does not look like a matrix id.", "'%1' does not look like a matrix id.", text)); return QString(); } -#ifdef QUOTIENT_07 const RoomMemberEvent *roomMemberEvent = room->currentState().get(text); if (roomMemberEvent && roomMemberEvent->membership() == Membership::Invite) { Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc(" is already invited to this room.", "%1 is already invited to this room.", text)); @@ -202,7 +201,6 @@ QVector actions{ Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc(" is banned from this room.", "%1 is banned from this room.", text)); return QString(); } -#endif if (room->localUser()->id() == text) { Q_EMIT room->showMessage(NeoChatRoom::Positive, i18n("You are already in this room.")); return QString(); @@ -244,7 +242,6 @@ QVector actions{ kli18n(""), kli18n("Joins the given room"), }, -#ifdef QUOTIENT_07 Action{ QStringLiteral("knock"), [](const QString &text, NeoChatRoom *room) { @@ -277,7 +274,6 @@ QVector actions{ kli18n(" []"), kli18n("Requests to join the given room"), }, -#endif Action{ QStringLiteral("j"), [](const QString &text, NeoChatRoom *room) { @@ -436,14 +432,12 @@ QVector actions{ Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'' does not look like a matrix id.", "'%1' does not look like a matrix id.", text)); return QString(); } -#ifdef QUOTIENT_07 auto state = room->currentState().get(parts[0]); if (state && state->membership() == Membership::Ban) { Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc(" is already banned from this room.", "%1 is already banned from this room.", text)); return QString(); } -#endif - auto plEvent = room->getCurrentState(); + auto plEvent = room->currentState().get(); if (plEvent->ban() > plEvent->powerLevelForUser(room->localUser()->id())) { Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You are not allowed to ban users from this room.")); return QString(); @@ -473,18 +467,16 @@ QVector actions{ Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'' does not look like a matrix id.", "'%1' does not look like a matrix id.", text)); return QString(); } - auto plEvent = room->getCurrentState(); + auto plEvent = room->currentState().get(); if (plEvent->ban() > plEvent->powerLevelForUser(room->localUser()->id())) { Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You are not allowed to unban users from this room.")); return QString(); } -#ifdef QUOTIENT_07 auto state = room->currentState().get(text); if (state && state->membership() != Membership::Ban) { Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc(" is not banned from this room.", "%1 is not banned from this room.", text)); return QString(); } -#endif room->unban(text); Q_EMIT room->showMessage(NeoChatRoom::Positive, i18nc(" was unbanned from this room.", "%1 was unbanned from this room.", text)); @@ -511,13 +503,11 @@ QVector actions{ Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You cannot kick yourself from the room.")); return QString(); } -#ifdef QUOTIENT_07 if (!room->isMember(parts[0])) { Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc(" is not in this room", "%1 is not in this room.", parts[0])); return QString(); } -#endif - auto plEvent = room->getCurrentState(); + auto plEvent = room->currentState().get(); auto kick = plEvent->kick(); if (plEvent->powerLevelForUser(room->localUser()->id()) < kick) { Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You are not allowed to kick users from this room.")); diff --git a/src/models/customemojimodel.cpp b/src/models/customemojimodel.cpp index 0dcce275f..55952f6c9 100644 --- a/src/models/customemojimodel.cpp +++ b/src/models/customemojimodel.cpp @@ -11,12 +11,6 @@ using namespace Quotient; -#ifdef QUOTIENT_07 -#define running isJobPending -#else -#define running isJobRunning -#endif - void CustomEmojiModel::fetchEmojis() { if (!Controller::instance().activeConnection()) { @@ -57,18 +51,12 @@ void CustomEmojiModel::addEmoji(const QString &name, const QUrl &location) auto job = Controller::instance().activeConnection()->uploadFile(location.toLocalFile()); - if (running(job)) { - connect(job, &BaseJob::success, this, [this, name, job] { + if (isJobPending(job)) { + connect(job, &BaseJob::success, this, [name, job] { const auto &data = Controller::instance().activeConnection()->accountData("im.ponies.user_emotes"); auto json = data != nullptr ? data->contentJson() : QJsonObject(); auto emojiData = json["images"].toObject(); - emojiData[QStringLiteral("%1").arg(name)] = QJsonObject({ -#ifdef QUOTIENT_07 - {QStringLiteral("url"), job->contentUri().toString()} -#else - {QStringLiteral("url"), job->contentUri()} -#endif - }); + emojiData[QStringLiteral("%1").arg(name)] = QJsonObject({{QStringLiteral("url"), job->contentUri().toString()}}); json["images"] = emojiData; Controller::instance().activeConnection()->setAccountData("im.ponies.user_emotes", json); }); @@ -141,8 +129,9 @@ QVariant CustomEmojiModel::data(const QModelIndex &idx, int role) const return QUrl(QStringLiteral("image://mxc/") + data.url.mid(6)); case Roles::MxcUrl: return data.url.mid(6); + default: + return {}; } - return QVariant(); } diff --git a/src/models/devicesmodel.cpp b/src/models/devicesmodel.cpp index 0482aa637..9cd7bd048 100644 --- a/src/models/devicesmodel.cpp +++ b/src/models/devicesmodel.cpp @@ -67,7 +67,7 @@ QHash DevicesModel::roleNames() const void DevicesModel::logout(int index, const QString &password) { - auto job = Controller::instance().activeConnection()->callApi(m_devices[index].deviceId); + auto job = Controller::instance().activeConnection()->callApi(m_devices[index].deviceId); connect(job, &BaseJob::result, this, [this, job, password, index] { auto onSuccess = [this, index]() { diff --git a/src/models/messageeventmodel.cpp b/src/models/messageeventmodel.cpp index d23b0af0c..4e7ed864e 100644 --- a/src/models/messageeventmodel.cpp +++ b/src/models/messageeventmodel.cpp @@ -11,12 +11,10 @@ #include #include #include +#include #include -#ifdef QUOTIENT_07 #include "pollevent.h" -#endif -#include "stickerevent.h" #include #include @@ -109,11 +107,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room) if (m_currentRoom->timelineSize() < 10 && !room->allHistoryLoaded()) { room->getPreviousContent(50); } -#ifdef QUOTIENT_07 lastReadEventId = room->lastFullyReadEventId(); -#else - lastReadEventId = room->readMarkerEventId(); -#endif using namespace Quotient; connect(m_currentRoom, &Room::aboutToAddNewMessages, this, [this](RoomEventsRange events) { @@ -162,11 +156,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room) endInsertRows(); if (!m_lastReadEventIndex.isValid()) { // no read marker, so see if we need to create one. -#ifdef QUOTIENT_07 moveReadMarker(m_currentRoom->lastFullyReadEventId()); -#else - moveReadMarker(m_currentRoom->readMarkerEventId()); -#endif } if (biggest < m_currentRoom->maxTimelineIndex()) { auto rowBelowInserted = m_currentRoom->maxTimelineIndex() - biggest + timelineBaseIndex() - 1; @@ -207,7 +197,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room) beginRemoveRows({}, i, i); }); connect(m_currentRoom, &Room::pendingEventDiscarded, this, &MessageEventModel::endRemoveRows); - connect(m_currentRoom, &Room::readMarkerMoved, this, [this](const QString &fromEventId, const QString &toEventId) { + connect(m_currentRoom, &Room::fullyReadMarkerMoved, this, [this](const QString &fromEventId, const QString &toEventId) { Q_UNUSED(fromEventId); moveReadMarker(toEventId); }); @@ -230,9 +220,6 @@ void MessageEventModel::setRoom(NeoChatRoom *room) connect(m_currentRoom, &Room::fileTransferProgress, this, &MessageEventModel::refreshEvent); connect(m_currentRoom, &Room::fileTransferCompleted, this, &MessageEventModel::refreshEvent); connect(m_currentRoom, &Room::fileTransferFailed, this, &MessageEventModel::refreshEvent); -#ifndef QUOTIENT_07 - connect(m_currentRoom, &Room::fileTransferCancelled, this, &MessageEventModel::refreshEvent); -#endif connect(m_currentRoom->connection(), &Connection::ignoredUsersListChanged, this, [this] { beginResetModel(); endResetModel(); @@ -498,7 +485,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const } if (role == SourceRole) { - return evt.originalJson(); + return QJsonDocument(evt.fullJson()).toJson(); } if (role == DelegateTypeRole) { @@ -534,20 +521,18 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const if (is(evt)) { return DelegateType::Encrypted; } -#ifdef QUOTIENT_07 if (is(evt)) { if (evt.isRedacted()) { return DelegateType::Message; } return DelegateType::Poll; } -#endif return DelegateType::Other; } if (role == EventResolvedTypeRole) { - return EventTypeRegistry::getMatrixType(evt.type()); + return evt.type(); } if (role == AuthorRole) { @@ -646,7 +631,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const return EventStatus::Hidden; } - if (evt.isStateEvent() && static_cast(evt).repeatsState()) { + if (evt.isStateEvent() && static_cast(evt).repeatsState()) { return EventStatus::Hidden; } @@ -820,22 +805,13 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const } if (role == ReadMarkersRole) { -#ifdef QUOTIENT_07 auto userIds = room()->userIdsAtEvent(evt.id()); userIds.remove(m_currentRoom->localUser()->id()); -#else - auto userIds = room()->usersAtEventId(evt.id()); - userIds.removeAll(m_currentRoom->localUser()); -#endif QVariantList users; users.reserve(userIds.size()); for (const auto &userId : userIds) { -#ifdef QUOTIENT_07 auto user = static_cast(m_currentRoom->user(userId)); -#else - auto user = static_cast(userId); -#endif users += userAtEvent(user, m_currentRoom, evt); } @@ -843,24 +819,15 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const } if (role == ReadMarkersStringRole) { -#ifdef QUOTIENT_07 auto userIds = room()->userIdsAtEvent(evt.id()); userIds.remove(m_currentRoom->localUser()->id()); -#else - auto userIds = room()->usersAtEventId(evt.id()); - userIds.removeAll(m_currentRoom->localUser()); -#endif /** * The string ends up in the form * "x users: user1DisplayName, user2DisplayName, etc." */ QString readMarkersString = i18np("1 user: ", "%1 users: ", userIds.size()); for (const auto &userId : userIds) { -#ifdef QUOTIENT_07 auto user = static_cast(m_currentRoom->user(userId)); -#else - auto user = static_cast(userId); -#endif readMarkersString += user->displayname(m_currentRoom) + i18nc("list separator", ", "); } readMarkersString.chop(2); @@ -868,18 +835,13 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const } if (role == ShowReadMarkersRole) { -#ifdef QUOTIENT_07 auto userIds = room()->userIdsAtEvent(evt.id()); userIds.remove(m_currentRoom->localUser()->id()); -#else - auto userIds = room()->usersAtEventId(evt.id()); - userIds.removeAll(m_currentRoom->localUser()); -#endif return userIds.size() > 0; } if (role == ReactionRole) { - const auto &annotations = m_currentRoom->relatedEvents(evt, EventRelation::Annotation()); + const auto &annotations = m_currentRoom->relatedEvents(evt, EventRelation::AnnotationType); if (annotations.isEmpty()) { return {}; }; @@ -889,7 +851,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const continue; } if (auto e = eventCast(a)) { - reactions[e->relation().key].append(static_cast(m_currentRoom->user(e->senderId()))); + reactions[e->eventId()].append(static_cast(m_currentRoom->user(e->senderId()))); } } @@ -916,7 +878,6 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const } if (role == MediaUrlRole) { -#ifdef QUOTIENT_07 if (auto e = eventCast(&evt)) { if (!e->hasFileContent()) { return QVariant(); @@ -933,7 +894,6 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const // if (auto e = eventCast(&evt)) { // return m_currentRoom->makeMediaUrl(e->id(), e->url()); // } -#endif // Construct link in the same form as urlToDownload as that function doesn't work for stickers if (auto e = eventCast(&evt)) { @@ -948,14 +908,12 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const } if (role == VerifiedRole) { -#ifdef QUOTIENT_07 #ifdef Quotient_E2EE_ENABLED if (evt.originalEvent()) { auto encrypted = dynamic_cast(evt.originalEvent()); Q_ASSERT(encrypted); return m_currentRoom->connection()->isVerifiedSession(encrypted->sessionId().toLatin1()); } -#endif #endif return false; } @@ -983,7 +941,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const } if (role == IsPendingRole) { - return row < m_currentRoom->pendingEvents().size(); + return row < static_cast(m_currentRoom->pendingEvents().size()); } return {}; diff --git a/src/models/publicroomlistmodel.cpp b/src/models/publicroomlistmodel.cpp index 4d4ca98bc..dd3873f43 100644 --- a/src/models/publicroomlistmodel.cpp +++ b/src/models/publicroomlistmodel.cpp @@ -117,11 +117,7 @@ void PublicRoomListModel::next(int count) return; } -#ifdef QUOTIENT_07 job = m_connection->callApi(m_server, count, nextBatch, QueryPublicRoomsJob::Filter{m_keyword, {}}); -#else - job = m_connection->callApi(m_server, count, nextBatch, QueryPublicRoomsJob::Filter{m_keyword}); -#endif connect(job, &BaseJob::finished, this, [this] { attempted = true; @@ -177,11 +173,7 @@ QVariant PublicRoomListModel::data(const QModelIndex &index, int role) const if (avatarUrl.isEmpty()) { return ""; } -#ifdef QUOTIENT_07 return avatarUrl.url().remove(0, 6); -#else - return avatarUrl.remove(0, 6); -#endif } if (role == TopicRole) { return room.topic; diff --git a/src/models/roomlistmodel.cpp b/src/models/roomlistmodel.cpp index dfbed4a7a..ef86e9ccc 100644 --- a/src/models/roomlistmodel.cpp +++ b/src/models/roomlistmodel.cpp @@ -8,6 +8,7 @@ #include "neochatroom.h" #include "roommanager.h" #include "user.h" +#include #include #if QT_VERSION < QT_VERSION_CHECK(6, 6, 0) @@ -22,11 +23,6 @@ #include #include -#ifndef QUOTIENT_07 -#include "notificationsmanager.h" -#include -#endif - using namespace Quotient; Q_DECLARE_METATYPE(Quotient::JoinState) @@ -151,7 +147,7 @@ void RoomListModel::connectRoomSignals(NeoChatRoom *room) connect(room, &Room::displaynameChanged, this, [this, room] { refresh(room, {DisplayNameRole, NameRole}); }); - connect(room, &Room::unreadMessagesChanged, this, [this, room] { + connect(room, &Room::unreadStatsChanged, this, [this, room] { refresh(room, {UnreadCountRole, NotificationCountRole, HighlightCountRole}); }); connect(room, &Room::notificationCountChanged, this, [this, room] { @@ -172,71 +168,9 @@ void RoomListModel::connectRoomSignals(NeoChatRoom *room) connect(room, &Room::pendingEventMerged, this, [this, room] { refresh(room, {LastEventRole, SubtitleTextRole}); }); -#ifndef QUOTIENT_07 - connect(room, &Room::notificationCountChanged, this, &RoomListModel::handleNotifications); -#endif - -#ifndef QUOTIENT_07 - connect(room, &Room::notificationCountChanged, this, &RoomListModel::refreshNotificationCount); -#else connect(room, &Room::unreadStatsChanged, this, &RoomListModel::refreshNotificationCount); -#endif } -#ifndef QUOTIENT_07 -void RoomListModel::handleNotifications() -{ - static bool initial = true; - static QStringList oldNotifications; - auto job = m_connection->callApi(); - - connect(job, &BaseJob::success, this, [this, job]() { - const auto notifications = job->jsonData()["notifications"].toArray(); - if (initial) { - initial = false; - for (const auto &n : notifications) { - oldNotifications += n.toObject()["event"].toObject()["event_id"].toString(); - } - return; - } - for (const auto &n : notifications) { - const auto notification = n.toObject(); - if (notification["read"].toBool()) { - oldNotifications.removeOne(notification["event"].toObject()["event_id"].toString()); - continue; - } - if (oldNotifications.contains(notification["event"].toObject()["event_id"].toString())) { - continue; - } - oldNotifications += notification["event"].toObject()["event_id"].toString(); - - auto room = m_connection->room(notification["room_id"].toString()); - auto currentRoom = RoomManager::instance().currentRoom(); - bool roomIsActive = currentRoom && room->id() == currentRoom->id(); - - // If room exists, room is NOT active OR the application is NOT active, show notification - if (room && !(roomIsActive && QGuiApplication::applicationState() == Qt::ApplicationActive)) { - // The room might have been deleted (for example rejected invitation). - auto sender = room->user(notification["event"].toObject()["sender"].toString()); - - QImage avatar_image; - if (!sender->avatarUrl(room).isEmpty()) { - avatar_image = sender->avatar(128, room); - } else { - avatar_image = room->avatar(128); - } - NotificationsManager::instance().postNotification(dynamic_cast(room), - sender->displayname(room), - notification["event"].toObject()["content"].toObject()["body"].toString(), - avatar_image, - notification["event"].toObject()["event_id"].toString(), - true); - } - } - }); -} -#endif - void RoomListModel::refreshNotificationCount() { int count = 0; @@ -361,7 +295,7 @@ QVariant RoomListModel::data(const QModelIndex &index, int role) const return NeoChatRoomType::Normal; } if (role == UnreadCountRole) { - return room->unreadCount(); + return room->unreadStats().notableCount; } if (role == NotificationCountRole) { return room->notificationCount(); diff --git a/src/models/roomlistmodel.h b/src/models/roomlistmodel.h index 2b65ae25a..bcba256fd 100644 --- a/src/models/roomlistmodel.h +++ b/src/models/roomlistmodel.h @@ -107,9 +107,6 @@ private: QString m_activeSpaceId = ""; void connectRoomSignals(NeoChatRoom *room); -#ifndef QUOTIENT_07 - void handleNotifications(); -#endif Q_SIGNALS: void connectionChanged(); diff --git a/src/models/searchmodel.cpp b/src/models/searchmodel.cpp index f38527bd8..27b6270e4 100644 --- a/src/models/searchmodel.cpp +++ b/src/models/searchmodel.cpp @@ -8,9 +8,7 @@ #include #include -#ifdef QUOTIENT_07 #include -#endif using namespace Quotient; @@ -34,7 +32,6 @@ void SearchModel::setSearchText(const QString &searchText) void SearchModel::search() { -#ifdef QUOTIENT_07 Q_ASSERT(m_connection); setSearching(true); if (m_job) { @@ -43,20 +40,26 @@ void SearchModel::search() } SearchJob::RoomEventsCriteria criteria{ - m_searchText, - {}, - RoomEventFilter{ - .rooms = {m_room->id()}, - }, - "recent", - SearchJob::IncludeEventContext{3, 3, true}, - false, - none, + .searchTerm = m_searchText, + .keys = {}, + .filter = + RoomEventFilter{ + .unreadThreadNotifications = none, + .lazyLoadMembers = true, + .includeRedundantMembers = false, + .notRooms = {}, + .rooms = {m_room->id()}, + .containsUrl = false, + }, + .orderBy = "recent", + .eventContext = SearchJob::IncludeEventContext{3, 3, true}, + .includeState = false, + .groupings = none, }; auto job = m_connection->callApi(SearchJob::Categories{criteria}); m_job = job; - connect(job, &BaseJob::finished, this, [=] { + connect(job, &BaseJob::finished, this, [this, job] { beginResetModel(); m_result = job->searchCategories().roomEvents; endResetModel(); @@ -64,7 +67,6 @@ void SearchModel::search() m_job = nullptr; // TODO error handling }); -#endif } Connection *SearchModel::connection() const @@ -80,7 +82,6 @@ void SearchModel::setConnection(Connection *connection) QVariant SearchModel::data(const QModelIndex &index, int role) const { -#ifdef QUOTIENT_07 auto row = index.row(); const auto &event = *m_result->results[row].result; switch (role) { @@ -110,17 +111,14 @@ QVariant SearchModel::data(const QModelIndex &index, int role) const return event.originTimestamp(); } return MessageEventModel::DelegateType::Message; -#endif - return {}; } int SearchModel::rowCount(const QModelIndex &parent) const { -#ifdef QUOTIENT_07 + Q_UNUSED(parent); if (m_result.has_value()) { return m_result->results.size(); } -#endif return 0; } diff --git a/src/models/searchmodel.h b/src/models/searchmodel.h index a3244131e..a84c23e44 100644 --- a/src/models/searchmodel.h +++ b/src/models/searchmodel.h @@ -6,9 +6,7 @@ #include #include -#ifdef QUOTIENT_07 #include -#endif namespace Quotient { @@ -68,10 +66,8 @@ private: QString m_searchText; Quotient::Connection *m_connection = nullptr; NeoChatRoom *m_room = nullptr; -#ifdef QUOTIENT_07 Quotient::Omittable m_result = Quotient::none; Quotient::SearchJob *m_job = nullptr; -#endif bool m_searching = false; }; diff --git a/src/models/serverlistmodel.cpp b/src/models/serverlistmodel.cpp index e20398f4d..b947b1416 100644 --- a/src/models/serverlistmodel.cpp +++ b/src/models/serverlistmodel.cpp @@ -95,11 +95,7 @@ void ServerListModel::checkServer(const QString &url) KConfigGroup serverGroup(&dataResource, "Servers"); if (!serverGroup.hasKey(url)) { -#ifdef QUOTIENT_07 if (Quotient::isJobPending(m_checkServerJob)) { -#else - if (Quotient::isJobRunning(m_checkServerJob)) { -#endif m_checkServerJob->abandon(); } diff --git a/src/models/statemodel.cpp b/src/models/statemodel.cpp index 016e2bea0..7d0cbc32b 100644 --- a/src/models/statemodel.cpp +++ b/src/models/statemodel.cpp @@ -14,7 +14,6 @@ QHash StateModel::roleNames() const } QVariant StateModel::data(const QModelIndex &index, int role) const { -#ifdef QUOTIENT_07 auto row = index.row(); switch (role) { case TypeRole: @@ -24,18 +23,13 @@ QVariant StateModel::data(const QModelIndex &index, int role) const case SourceRole: return QJsonDocument(m_room->currentState().events()[m_room->currentState().events().keys()[row]]->fullJson()).toJson(); } -#endif return {}; } int StateModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); -#ifdef QUOTIENT_07 return m_room->currentState().events().size(); -#else - return 0; -#endif } NeoChatRoom *StateModel::room() const @@ -49,7 +43,7 @@ void StateModel::setRoom(NeoChatRoom *room) Q_EMIT roomChanged(); beginResetModel(); endResetModel(); - connect(room, &NeoChatRoom::changed, this, [=] { + connect(room, &NeoChatRoom::changed, this, [this] { beginResetModel(); endResetModel(); }); diff --git a/src/models/userdirectorylistmodel.cpp b/src/models/userdirectorylistmodel.cpp index 209c5e126..484f41001 100644 --- a/src/models/userdirectorylistmodel.cpp +++ b/src/models/userdirectorylistmodel.cpp @@ -131,11 +131,7 @@ QVariant UserDirectoryListModel::data(const QModelIndex &index, int role) const if (avatarUrl.isEmpty()) { return ""; } -#ifdef QUOTIENT_07 return avatarUrl.url().remove(0, 6); -#else - return avatarUrl.remove(0, 6); -#endif } if (role == UserIDRole) { return user.userId; diff --git a/src/models/userlistmodel.cpp b/src/models/userlistmodel.cpp index 3a0f891e7..f900ce2a9 100644 --- a/src/models/userlistmodel.cpp +++ b/src/models/userlistmodel.cpp @@ -44,13 +44,9 @@ void UserListModel::setRoom(NeoChatRoom *room) std::sort(m_users.begin(), m_users.end(), room->memberSorter()); } for (User *user : std::as_const(m_users)) { -#ifdef QUOTIENT_07 connect(user, &User::defaultAvatarChanged, this, [this, user]() { avatarChanged(user, m_currentRoom); }); -#else - connect(user, &User::avatarChanged, this, &UserListModel::avatarChanged); -#endif } connect(m_currentRoom->connection(), &Connection::loggedOut, this, [this]() { setRoom(nullptr); @@ -96,15 +92,14 @@ QVariant UserListModel::data(const QModelIndex &index, int role) const return QVariant::fromValue(user); } if (role == PowerLevelRole) { - auto pl = m_currentRoom->getCurrentState(); + auto pl = m_currentRoom->currentState().get(); + if (!pl) { + return 0; + } return pl->powerLevelForUser(user->id()); } if (role == PowerLevelStringRole) { -#ifdef QUOTIENT_07 auto pl = m_currentRoom->currentState().get(); -#else - auto pl = m_currentRoom->getCurrentState(); -#endif // User might not in the room yet, in this case pl can be nullptr. // e.g. When invited but user not accepted or denied the invitation. if (!pl) { @@ -143,13 +138,9 @@ void UserListModel::userAdded(Quotient::User *user) beginInsertRows(QModelIndex(), pos, pos); m_users.insert(pos, user); endInsertRows(); -#ifdef QUOTIENT_07 connect(user, &User::defaultAvatarChanged, this, [this, user]() { avatarChanged(user, m_currentRoom); }); -#else - connect(user, &Quotient::User::avatarChanged, this, &UserListModel::avatarChanged); -#endif } void UserListModel::userRemoved(Quotient::User *user) @@ -188,13 +179,9 @@ void UserListModel::refreshAll() std::sort(m_users.begin(), m_users.end(), m_currentRoom->memberSorter()); } for (User *user : std::as_const(m_users)) { -#ifdef QUOTIENT_07 connect(user, &User::defaultAvatarChanged, this, [this, user]() { avatarChanged(user, m_currentRoom); }); -#else - connect(user, &User::avatarChanged, this, &UserListModel::avatarChanged); -#endif } connect(m_currentRoom->connection(), &Connection::loggedOut, this, [this]() { setRoom(nullptr); diff --git a/src/neochataccountregistry.cpp b/src/neochataccountregistry.cpp deleted file mode 100644 index c25f01009..000000000 --- a/src/neochataccountregistry.cpp +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-FileCopyrightText: Kitsune Ral -// SPDX-FileCopyrightText: Tobias Fella -// SPDX-License-Identifier: LGPL-2.1-or-later - -#include "neochataccountregistry.h" - -#include - -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(); - emit accountCountChanged(); -} - -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)); - emit accountCountChanged(); -} - -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()]; - - switch (role) { - case ConnectionRole: - return QVariant::fromValue(account); - case UserIdRole: - return QVariant::fromValue(account->userId()); - default: - return {}; - } - - return {}; -} - -int AccountRegistry::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid()) { - return 0; - } - - return m_accounts.count(); -} - -QHash AccountRegistry::roleNames() const -{ - return {{ConnectionRole, "connection"}, {UserIdRole, "userId"}}; -} - -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/neochataccountregistry.h b/src/neochataccountregistry.h deleted file mode 100644 index 221ec7b33..000000000 --- a/src/neochataccountregistry.h +++ /dev/null @@ -1,54 +0,0 @@ -// 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 - Q_PROPERTY(int accountCount READ count NOTIFY accountCountChanged); - -public: - enum EventRoles { - ConnectionRole = Qt::UserRole + 1, - UserIdRole = Qt::DisplayRole, - }; - - 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; - -Q_SIGNALS: - void accountCountChanged(); - -private: - AccountRegistry(); - - QVector m_accounts; -}; -} diff --git a/src/neochatroom.cpp b/src/neochatroom.cpp index 461810f22..4e108a846 100644 --- a/src/neochatroom.cpp +++ b/src/neochatroom.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -31,23 +32,19 @@ #include #include #include +#include +#include #include -#ifndef QUOTIENT_07 -#include -#endif #include #include "controller.h" +#include "filetransferpseudojob.h" #include "joinrulesevent.h" #include "neochatconfig.h" #include "neochatuser.h" #include "notificationsmanager.h" -#ifdef QUOTIENT_07 #include "pollevent.h" #include "pollhandler.h" -#endif -#include "filetransferpseudojob.h" -#include "stickerevent.h" #include "texthandler.h" #ifndef Q_OS_ANDROID @@ -85,7 +82,7 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS if (this->joinState() != JoinState::Invite) { return; } - const QString senderId = getCurrentState(localUser()->id())->senderId(); + const QString senderId = currentState().get(localUser()->id())->senderId(); QImage avatar_image; if (!user(senderId)->avatarUrl(this).isEmpty()) { avatar_image = user(senderId)->avatar(128, this); @@ -174,17 +171,9 @@ QCoro::Task NeoChatRoom::doUploadFile(QUrl url, QString body) } else { content = new EventContent::FileContent(url, fileInfo.size(), mime, fileInfo.fileName()); } -#ifdef QUOTIENT_07 QString txnId = postFile(body.isEmpty() ? url.fileName() : body, content); -#else - QString txnId = postFile(body.isEmpty() ? url.fileName() : body, url, false); -#endif setHasFileUploading(true); -#ifdef QUOTIENT_07 connect(this, &Room::fileTransferCompleted, [this, txnId](const QString &id, FileSourceInfo) { -#else - connect(this, &Room::fileTransferCompleted, [this, txnId](const QString &id, const QUrl & /*localFile*/, const QUrl & /*mxcUrl*/) { -#endif if (id == txnId) { setFileUploadingProgress(0); setHasFileUploading(false); @@ -254,21 +243,20 @@ const RoomEvent *NeoChatRoom::lastEvent() const continue; } - if (event->isStateEvent() && !NeoChatConfig::self()->showStateEvent()) { + if (event->isStateEvent() && !NeoChatConfig::showStateEvent()) { continue; } if (auto roomMemberEvent = eventCast(event)) { - if ((roomMemberEvent->isJoin() || roomMemberEvent->isLeave()) && !NeoChatConfig::self()->showLeaveJoinEvent()) { + if ((roomMemberEvent->isJoin() || roomMemberEvent->isLeave()) && !NeoChatConfig::showLeaveJoinEvent()) { continue; - } else if (roomMemberEvent->isRename() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave() && !NeoChatConfig::self()->showRename()) { + } else if (roomMemberEvent->isRename() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave() && !NeoChatConfig::showRename()) { continue; - } else if (roomMemberEvent->isAvatarUpdate() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave() - && !NeoChatConfig::self()->showAvatarUpdate()) { + } else if (roomMemberEvent->isAvatarUpdate() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave() && !NeoChatConfig::showAvatarUpdate()) { continue; } } - if (event->isStateEvent() && static_cast(*event).repeatsState()) { + if (event->isStateEvent() && static_cast(*event).repeatsState()) { continue; } @@ -293,11 +281,9 @@ const RoomEvent *NeoChatRoom::lastEvent() const if (auto lastEvent = eventCast(event)) { return lastEvent; } -#ifdef QUOTIENT_07 if (auto lastEvent = eventCast(event)) { return lastEvent; } -#endif } return nullptr; } @@ -318,10 +304,9 @@ bool NeoChatRoom::lastEventIsSpoiler() const QString NeoChatRoom::lastEventToString(Qt::TextFormat format, bool stripNewlines) const { if (auto event = lastEvent()) { - return roomMembername(event->senderId()) + (event->isStateEvent() ? QLatin1String(" ") : QLatin1String(": ")) - + eventToString(*event, format, stripNewlines); + return safeMemberName(event->senderId()) + (event->isStateEvent() ? " " : ": ") + eventToString(*event, format, stripNewlines); } - return QLatin1String(""); + return {}; } bool NeoChatRoom::isEventHighlighted(const RoomEvent *e) const @@ -337,7 +322,7 @@ void NeoChatRoom::checkForHighlights(const Quotient::TimelineItem &ti) } if (auto *e = ti.viewAs()) { const auto &text = e->plainBody(); - if (text.contains(localUserId) || text.contains(roomMembername(localUserId))) { + if (text.contains(localUserId) || text.contains(safeMemberName(localUserId))) { highlights.insert(e); } } @@ -360,7 +345,7 @@ void NeoChatRoom::onAddHistoricalTimelineEvents(rev_iter_t from) void NeoChatRoom::onRedaction(const RoomEvent &prevEvent, const RoomEvent & /*after*/) { if (const auto &e = eventCast(&prevEvent)) { - if (auto relatedEventId = e->relation().eventId; !relatedEventId.isEmpty()) { + if (auto relatedEventId = e->eventId(); !relatedEventId.isEmpty()) { Q_EMIT updatedEvent(relatedEventId); } } @@ -368,9 +353,8 @@ void NeoChatRoom::onRedaction(const RoomEvent &prevEvent, const RoomEvent & /*af void NeoChatRoom::countChanged() { - if (displayed() && !hasUnreadMessages()) { - resetNotificationCount(); - resetHighlightCount(); + if (displayed() && unreadStats().empty()) { + setReadReceipt(lastEvent()->id()); } } @@ -443,11 +427,7 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format, const bool prettyPrint = (format == Qt::RichText); using namespace Quotient; -#ifdef QUOTIENT_07 return switchOnType( -#else - return visit( -#endif evt, [this, format, stripNewlines](const RoomMessageEvent &e) { using namespace MessageEventContent; @@ -493,14 +473,9 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format, [this, prettyPrint](const RoomMemberEvent &e) { // FIXME: Rewind to the name that was at the time of this event auto subjectName = this->htmlSafeMemberName(e.userId()); - if (e.membership() == MembershipType::Leave) { -#ifdef QUOTIENT_07 + if (e.membership() == Membership::Leave) { if (e.prevContent() && e.prevContent()->displayName) { subjectName = sanitized(*e.prevContent()->displayName).toHtmlEscaped(); -#else - if (e.prevContent() && e.prevContent()->displayName.isEmpty()) { - subjectName = sanitized(e.prevContent()->displayName).toHtmlEscaped(); -#endif } } @@ -511,7 +486,7 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format, // The below code assumes senderName output in AuthorRole switch (e.membership()) { - case MembershipType::Invite: + case Membership::Invite: if (e.repeatsState()) { auto text = i18n("reinvited %1 to the room", subjectName); if (!e.reason().isEmpty()) { @@ -520,13 +495,13 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format, return text; } Q_FALLTHROUGH(); - case MembershipType::Join: { + case Membership::Join: { QString text{}; // Part 1: invites and joins if (e.repeatsState()) { text = i18n("joined the room (repeated)"); } else if (e.changesMembership()) { - text = e.membership() == MembershipType::Invite ? i18n("invited %1 to the room", subjectName) : i18n("joined the room"); + text = e.membership() == Membership::Invite ? i18n("invited %1 to the room", subjectName) : i18n("joined the room"); } if (!text.isEmpty()) { if (!e.reason().isEmpty()) { @@ -536,23 +511,19 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format, } // Part 2: profile changes of joined members if (e.isRename()) { - if (e.displayName().isEmpty()) { + if (e.newDisplayName()) { text = i18nc("their refers to a singular user", "cleared their display name"); } else { - text = i18nc("their refers to a singular user", "changed their display name to %1", e.displayName().toHtmlEscaped()); + text = i18nc("their refers to a singular user", "changed their display name to %1", e.newDisplayName()->toHtmlEscaped()); } } if (e.isAvatarUpdate()) { if (!text.isEmpty()) { text += i18n(" and "); } - if (e.avatarUrl().isEmpty()) { + if (e.newAvatarUrl()) { text += i18nc("their refers to a singular user", "cleared their avatar"); -#ifdef QUOTIENT_07 } else if (!e.prevContent()->avatarUrl) { -#else - } else if (e.prevContent()->avatarUrl.isEmpty()) { -#endif text += i18n("set an avatar"); } else { text += i18nc("their refers to a singular user", "updated their avatar"); @@ -563,18 +534,18 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format, } return text; } - case MembershipType::Leave: - if (e.prevContent() && e.prevContent()->membership == MembershipType::Invite) { + case Membership::Leave: + if (e.prevContent() && e.prevContent()->membership == Membership::Invite) { return (e.senderId() != e.userId()) ? i18n("withdrew %1's invitation", subjectName) : i18n("rejected the invitation"); } - if (e.prevContent() && e.prevContent()->membership == MembershipType::Ban) { + if (e.prevContent() && e.prevContent()->membership == Membership::Ban) { return (e.senderId() != e.userId()) ? i18n("unbanned %1", subjectName) : i18n("self-unbanned"); } return (e.senderId() != e.userId()) ? i18n("has put %1 out of the room: %2", subjectName, e.contentJson()["reason"_ls].toString().toHtmlEscaped()) : i18n("left the room"); - case MembershipType::Ban: + case Membership::Ban: if (e.senderId() != e.userId()) { if (e.reason().isEmpty()) { return i18n("banned %1 from the room", subjectName); @@ -584,7 +555,7 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format, } else { return i18n("self-banned from the room"); } - case MembershipType::Knock: { + case Membership::Knock: { QString reason(e.contentJson()["reason"_ls].toString().toHtmlEscaped()); return reason.isEmpty() ? i18n("requested an invite") : i18n("requested an invite with reason: %1", reason); } @@ -618,7 +589,7 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format, [](const RoomPowerLevelsEvent &) { return i18nc("'power level' means permission level", "changed the power levels for this room"); }, - [](const StateEventBase &e) { + [](const StateEvent &e) { if (e.matrixType() == QLatin1String("m.room.server_acl")) { return i18n("changed the server access control lists for this room"); } @@ -634,21 +605,15 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format, return e.stateKey().isEmpty() ? i18n("updated %1 state", e.matrixType()) : i18n("updated %1 state for %2", e.matrixType(), e.stateKey().toHtmlEscaped()); }, -#ifdef QUOTIENT_07 [](const PollStartEvent &e) { return e.question(); }, -#endif i18n("Unknown event")); } QString NeoChatRoom::eventToGenericString(const RoomEvent &evt) const { -#ifdef QUOTIENT_07 return switchOnType( -#else - return visit( -#endif evt, [](const RoomMessageEvent &e) { Q_UNUSED(e) @@ -660,25 +625,25 @@ QString NeoChatRoom::eventToGenericString(const RoomEvent &evt) const }, [](const RoomMemberEvent &e) { switch (e.membership()) { - case MembershipType::Invite: + case Membership::Invite: if (e.repeatsState()) { return i18n("reinvited someone to the room"); } Q_FALLTHROUGH(); - case MembershipType::Join: { + case Membership::Join: { QString text{}; // Part 1: invites and joins if (e.repeatsState()) { text = i18n("joined the room (repeated)"); } else if (e.changesMembership()) { - text = e.membership() == MembershipType::Invite ? i18n("invited someone to the room") : i18n("joined the room"); + text = e.membership() == Membership::Invite ? i18n("invited someone to the room") : i18n("joined the room"); } if (!text.isEmpty()) { return text; } // Part 2: profile changes of joined members if (e.isRename()) { - if (e.displayName().isEmpty()) { + if (e.newDisplayName()) { text = i18nc("their refers to a singular user", "cleared their display name"); } else { text = i18nc("their refers to a singular user", "changed their display name"); @@ -688,13 +653,9 @@ QString NeoChatRoom::eventToGenericString(const RoomEvent &evt) const if (!text.isEmpty()) { text += i18n(" and "); } - if (e.avatarUrl().isEmpty()) { + if (e.newAvatarUrl()) { text += i18nc("their refers to a singular user", "cleared their avatar"); -#ifdef QUOTIENT_07 } else if (!e.prevContent()->avatarUrl) { -#else - } else if (e.prevContent()->avatarUrl.isEmpty()) { -#endif text += i18n("set an avatar"); } else { text += i18nc("their refers to a singular user", "updated their avatar"); @@ -705,22 +666,22 @@ QString NeoChatRoom::eventToGenericString(const RoomEvent &evt) const } return text; } - case MembershipType::Leave: - if (e.prevContent() && e.prevContent()->membership == MembershipType::Invite) { + case Membership::Leave: + if (e.prevContent() && e.prevContent()->membership == Membership::Invite) { return (e.senderId() != e.userId()) ? i18n("withdrew a user's invitation") : i18n("rejected the invitation"); } - if (e.prevContent() && e.prevContent()->membership == MembershipType::Ban) { + if (e.prevContent() && e.prevContent()->membership == Membership::Ban) { return (e.senderId() != e.userId()) ? i18n("unbanned a user") : i18n("self-unbanned"); } return (e.senderId() != e.userId()) ? i18n("put a user out of the room") : i18n("left the room"); - case MembershipType::Ban: + case Membership::Ban: if (e.senderId() != e.userId()) { return i18n("banned a user from the room"); } else { return i18n("self-banned from the room"); } - case MembershipType::Knock: { + case Membership::Knock: { return i18n("requested an invite"); } default:; @@ -748,7 +709,7 @@ QString NeoChatRoom::eventToGenericString(const RoomEvent &evt) const [](const RoomPowerLevelsEvent &) { return i18nc("'power level' means permission level", "changed the power levels for this room"); }, - [](const StateEventBase &e) { + [](const StateEvent &e) { if (e.matrixType() == QLatin1String("m.room.server_acl")) { return i18n("changed the server access control lists for this room"); } @@ -763,28 +724,19 @@ QString NeoChatRoom::eventToGenericString(const RoomEvent &evt) const } return i18n("updated the state"); }, -#ifdef QUOTIENT_07 [](const PollStartEvent &e) { + Q_UNUSED(e); return i18n("started a poll"); }, -#endif i18n("Unknown event")); } void NeoChatRoom::changeAvatar(const QUrl &localFile) { const auto job = connection()->uploadFile(localFile.toLocalFile()); -#ifdef QUOTIENT_07 if (isJobPending(job)) { -#else - if (isJobRunning(job)) { -#endif connect(job, &BaseJob::success, this, [this, job] { -#ifdef QUOTIENT_07 connection()->callApi(id(), "m.room.avatar", QString(), QJsonObject{{"url", job->contentUri().toString()}}); -#else - connection()->callApi(id(), "m.room.avatar", QString(), QJsonObject{{"url", job->contentUri()}}); -#endif }); } } @@ -893,11 +845,11 @@ void NeoChatRoom::toggleReaction(const QString &eventId, const QString &reaction QStringList redactEventIds; // What if there are multiple reaction events? - const auto &annotations = relatedEvents(evt, EventRelation::Annotation()); + const auto &annotations = relatedEvents(evt, EventRelation::AnnotationType); if (!annotations.isEmpty()) { for (const auto &a : annotations) { if (auto e = eventCast(a)) { - if (e->relation().key != reaction) { + if (e->key() != reaction) { continue; } @@ -920,18 +872,15 @@ void NeoChatRoom::toggleReaction(const QString &eventId, const QString &reaction bool NeoChatRoom::containsUser(const QString &userID) const { - auto u = Room::user(userID); - - if (!u) { - return false; - } - - return Room::memberJoinState(u) != JoinState::Leave; + return !isMember(userID); } bool NeoChatRoom::canSendEvent(const QString &eventType) const { - auto plEvent = getCurrentState(); + auto plEvent = currentState().get(); + if (!plEvent) { + return true; + } auto pl = plEvent->powerLevelForEvent(eventType); auto currentPl = plEvent->powerLevelForUser(localUser()->id()); @@ -940,28 +889,19 @@ bool NeoChatRoom::canSendEvent(const QString &eventType) const bool NeoChatRoom::canSendState(const QString &eventType) const { - auto plEvent = getCurrentState(); + auto plEvent = currentState().get(); + if (!plEvent) { + return false; + } auto pl = plEvent->powerLevelForState(eventType); auto currentPl = plEvent->powerLevelForUser(localUser()->id()); -#ifndef QUOTIENT_07 - if (eventType == "m.room.history_visibility" || eventType == "org.matrix.room.preview_urls") { - return false; - } else { - return currentPl >= pl; - } -#else return currentPl >= pl; -#endif } bool NeoChatRoom::readMarkerLoaded() const { -#ifdef QUOTIENT_07 const auto it = findInTimeline(lastFullyReadEventId()); -#else - const auto it = findInTimeline(readMarkerEventId()); -#endif return it != historyEdge(); } @@ -972,7 +912,7 @@ bool NeoChatRoom::isInvite() const bool NeoChatRoom::isUserBanned(const QString &user) const { - return getCurrentState(user)->membership() == MembershipType::Ban; + return currentState().get(user)->membership() == Membership::Ban; } QString NeoChatRoom::htmlSafeDisplayName() const @@ -987,7 +927,7 @@ void NeoChatRoom::deleteMessagesByUser(const QString &user, const QString &reaso QString NeoChatRoom::joinRule() const { - return getCurrentState()->joinRule(); + return currentState().get()->joinRule(); } void NeoChatRoom::setJoinRule(const QString &joinRule) @@ -996,21 +936,13 @@ void NeoChatRoom::setJoinRule(const QString &joinRule) qWarning() << "Power level too low to set join rules"; return; } -#ifdef QUOTIENT_07 setState("m.room.join_rules", "", QJsonObject{{"join_rule", joinRule}}); -#else - setState(QJsonObject{{"type", "m.room.join_rules"}, {"state_key", ""}, {"content", QJsonObject{{"join_rule", joinRule}}}}); -#endif // Not emitting joinRuleChanged() here, since that would override the change in the UI with the *current* value, which is not the *new* value. } QString NeoChatRoom::historyVisibility() const { -#ifdef QUOTIENT_07 return currentState().get("m.room.history_visibility")->contentJson()["history_visibility"_ls].toString(); -#else - return getCurrentState("m.room.history_visibility")->contentJson()["history_visibility"_ls].toString(); -#endif } void NeoChatRoom::setHistoryVisibility(const QString &historyVisibilityRule) @@ -1020,23 +952,14 @@ void NeoChatRoom::setHistoryVisibility(const QString &historyVisibilityRule) return; } -#ifdef QUOTIENT_07 setState("m.room.history_visibility", "", QJsonObject{{"history_visibility", historyVisibilityRule}}); -#else - qWarning() << "Quotient 0.7 required to set history visibility"; - return; -#endif // Not emitting historyVisibilityChanged() here, since that would override the change in the UI with the *current* value, which is not the *new* value. } bool NeoChatRoom::defaultUrlPreviewState() const { -#ifdef QUOTIENT_07 auto urlPreviewsDisabled = currentState().get("org.matrix.room.preview_urls"); -#else - auto urlPreviewsDisabled = getCurrentState("org.matrix.room.preview_urls"); -#endif // Some rooms will not have this state event set so check for a nullptr return. if (urlPreviewsDisabled != nullptr) { @@ -1082,12 +1005,7 @@ void NeoChatRoom::setDefaultUrlPreviewState(const bool &defaultUrlPreviewState) * * You just have to set disable to true to disable URL previews by default. */ -#ifdef QUOTIENT_07 setState("org.matrix.room.preview_urls", "", QJsonObject{{"disable", !defaultUrlPreviewState}}); -#else - qWarning() << "Quotient 0.7 required to set room default url preview setting"; - return; -#endif } bool NeoChatRoom::urlPreviewEnabled() const @@ -1125,32 +1043,20 @@ void NeoChatRoom::setUserPowerLevel(const QString &userID, const int &powerLevel qWarning() << "Power level too low to set user power levels"; return; } -#ifdef QUOTIENT_07 if (!isMember(userID)) { -#else - if (memberJoinState(user(userID)) == JoinState::Join) { -#endif qWarning() << "User is not a member of this room so power level cannot be set"; return; } int clampPowerLevel = std::clamp(powerLevel, 0, 100); -#ifdef QUOTIENT_07 auto powerLevelContent = currentState().get("m.room.power_levels")->contentJson(); -#else - auto powerLevelContent = getCurrentState()->contentJson(); -#endif auto powerLevelUserOverrides = powerLevelContent["users"].toObject(); if (powerLevelUserOverrides[userID] != clampPowerLevel) { powerLevelUserOverrides[userID] = clampPowerLevel; powerLevelContent["users"] = powerLevelUserOverrides; -#ifdef QUOTIENT_07 setState("m.room.power_levels", "", powerLevelContent); -#else - setState(QJsonObject{{"type", "m.room.power_levels"}, {"state_key", ""}, {"content", powerLevelContent}}); -#endif } } @@ -1162,11 +1068,7 @@ int NeoChatRoom::getUserPowerLevel(const QString &userId) const int NeoChatRoom::powerLevel(const QString &eventName, const bool &isStateEvent) const { -#ifdef QUOTIENT_07 const auto powerLevelEvent = currentState().get(); -#else - const auto powerLevelEvent = getCurrentState(); -#endif if (eventName == "ban") { return powerLevelEvent->ban(); } else if (eventName == "kick") { @@ -1190,11 +1092,7 @@ int NeoChatRoom::powerLevel(const QString &eventName, const bool &isStateEvent) void NeoChatRoom::setPowerLevel(const QString &eventName, const int &newPowerLevel, const bool &isStateEvent) { -#ifdef QUOTIENT_07 auto powerLevelContent = currentState().get("m.room.power_levels")->contentJson(); -#else - auto powerLevelContent = getCurrentState()->contentJson(); -#endif int clampPowerLevel = std::clamp(newPowerLevel, 0, 100); int powerLevel = 0; @@ -1223,11 +1121,7 @@ void NeoChatRoom::setPowerLevel(const QString &eventName, const int &newPowerLev } } -#ifdef QUOTIENT_07 setState("m.room.power_levels", "", powerLevelContent); -#else - setState(QJsonObject{{"type", "m.room.power_levels"}, {"state_key", ""}, {"content", powerLevelContent}}); -#endif } int NeoChatRoom::defaultUserPowerLevel() const @@ -1450,11 +1344,7 @@ bool NeoChatRoom::isSpace() return false; } -#ifdef QUOTIENT_07 return creationEvent->roomType() == RoomType::Space; -#else - return false; -#endif } PushNotificationState::State NeoChatRoom::pushNotificationState() const @@ -1781,15 +1671,9 @@ void NeoChatRoom::setSavedText(const QString &savedText) bool NeoChatRoom::canEncryptRoom() const { -#ifdef QUOTIENT_07 -#ifdef Quotient_E2EE_ENABLED return !usesEncryption() && canSendState("m.room.encryption"); -#endif -#endif - return false; } -#ifdef QUOTIENT_07 PollHandler *NeoChatRoom::poll(const QString &eventId) { if (!m_polls.contains(eventId)) { @@ -1800,7 +1684,6 @@ PollHandler *NeoChatRoom::poll(const QString &eventId) } return m_polls[eventId]; } -#endif bool NeoChatRoom::downloadTempFile(const QString &eventId) { diff --git a/src/neochatroom.h b/src/neochatroom.h index 4c4e1c1d9..4b876bf9e 100644 --- a/src/neochatroom.h +++ b/src/neochatroom.h @@ -735,7 +735,6 @@ public: */ void setSavedText(const QString &savedText); -#ifdef QUOTIENT_07 /** * @brief Get a PollHandler object for the given event Id. * @@ -747,7 +746,6 @@ public: * @sa PollHandler */ Q_INVOKABLE PollHandler *poll(const QString &eventId); -#endif private: QSet highlights; @@ -775,9 +773,7 @@ private: QVector m_mentions; QVector m_editMentions; QString m_savedText; -#ifdef QUOTIENT_07 QCache m_polls; -#endif private Q_SLOTS: void countChanged(); diff --git a/src/notificationsmanager.cpp b/src/notificationsmanager.cpp index cffed6a1a..f7c404176 100644 --- a/src/notificationsmanager.cpp +++ b/src/notificationsmanager.cpp @@ -11,11 +11,7 @@ #include #include -#ifdef QUOTIENT_07 #include -#else -#include "neochataccountregistry.h" -#endif #include #include @@ -72,11 +68,7 @@ void NotificationsManager::postNotification(NeoChatRoom *room, connect(notification, &KNotification::defaultActivated, this, [=]() { WindowController::instance().showAndRaiseWindow(notification->xdgActivationToken()); if (room->localUser()->id() != Controller::instance().activeConnection()->userId()) { -#ifdef QUOTIENT_07 Controller::instance().setActiveConnection(Accounts.get(room->localUser()->id())); -#else - Controller::instance().setActiveConnection(AccountRegistry::instance().get(room->localUser()->id())); -#endif } RoomManager::instance().enterRoom(room); }); diff --git a/src/pollhandler.cpp b/src/pollhandler.cpp index 76051d7cc..cea72fb8a 100644 --- a/src/pollhandler.cpp +++ b/src/pollhandler.cpp @@ -34,7 +34,7 @@ void PollHandler::setRoom(NeoChatRoom *room) connect(room, &NeoChatRoom::aboutToAddNewMessages, this, [this](Quotient::RoomEventsRange events) { for (const auto &event : events) { if (event->is()) { - auto pl = m_room->getCurrentState(); + auto pl = m_room->currentState().get(); auto userPl = pl->powerLevelForUser(event->senderId()); if (event->senderId() == (*m_room->findInTimeline(m_pollStartEventId))->senderId() || userPl >= pl->redact()) { m_hasEnded = true; @@ -75,7 +75,7 @@ void PollHandler::checkLoadRelations() connect(job, &BaseJob::success, this, [this, job]() { for (const auto &event : job->chunk()) { if (event->is()) { - auto pl = m_room->getCurrentState(); + auto pl = m_room->currentState().get(); auto userPl = pl->powerLevelForUser(event->senderId()); if (event->senderId() == (*m_room->findInTimeline(m_pollStartEventId))->senderId() || userPl >= pl->redact()) { m_hasEnded = true; diff --git a/src/qml/Dialog/ConfirmLogout.qml b/src/qml/Dialog/ConfirmLogout.qml index 6b53cccd1..2db44ca83 100644 --- a/src/qml/Dialog/ConfirmLogout.qml +++ b/src/qml/Dialog/ConfirmLogout.qml @@ -35,7 +35,7 @@ QQC2.Dialog { text: i18n("Sign out") QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole onClicked: { - Controller.logout(Controller.activeConnection, true); + Controller.activeConnection.logout(); root.close(); } } diff --git a/src/qml/Menu/GlobalMenu.qml b/src/qml/Menu/GlobalMenu.qml index 7114463ed..cea1ffa4b 100644 --- a/src/qml/Menu/GlobalMenu.qml +++ b/src/qml/Menu/GlobalMenu.qml @@ -40,12 +40,12 @@ Labs.MenuBar { Labs.MenuItem { text: i18nc("menu", "New Private Chat…") - enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.accountCount > 0 + enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.activeConnection onTriggered: pushReplaceLayer("qrc:/StartChatPage.qml", {connection: Controller.activeConnection}) } Labs.MenuItem { text: i18nc("menu", "New Group…") - enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.accountCount > 0 + enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.activeConnection shortcut: StandardKey.New onTriggered: { const dialog = createRoomDialog.createObject(root.overlay) diff --git a/src/qml/Page/RoomList/UserInfo.qml b/src/qml/Page/RoomList/UserInfo.qml index 32cff67d7..35a8d14e7 100644 --- a/src/qml/Page/RoomList/UserInfo.qml +++ b/src/qml/Page/RoomList/UserInfo.qml @@ -76,7 +76,7 @@ QQC2.ToolBar { visible: switchUserButton.checked onVisibleChanged: if (visible) accounts.forceActiveFocus() clip: true - model: AccountRegistry + model: Accounts keyNavigationEnabled: false Keys.onDownPressed: { diff --git a/src/qml/Page/RoomListPage.qml b/src/qml/Page/RoomListPage.qml new file mode 100644 index 000000000..151ec844e --- /dev/null +++ b/src/qml/Page/RoomListPage.qml @@ -0,0 +1,502 @@ +// SPDX-FileCopyrightText: 2019 Black Hat +// SPDX-FileCopyrightText: 2020 Carl Schwan +// SPDX-License-Identifier: GPL-3.0-only + +import QtQuick 2.15 +import QtQuick.Controls 2.15 as QQC2 +import QtQuick.Layouts 1.15 +import QtQml.Models 2.15 + +import org.kde.kirigami 2.15 as Kirigami +import org.kde.kitemmodels 1.0 + +import org.kde.neochat 1.0 + +Kirigami.ScrollablePage { + id: page + + /** + * @brief The current width of the room list. + * + * @note Other objects can access the value but the private function makes sure + * that only the internal members can modify it. + */ + readonly property int currentWidth: _private.currentWidth + + readonly property bool collapsed: Config.collapsed + onCollapsedChanged: if (collapsed) { + sortFilterRoomListModel.filterText = ""; + } + + header: ColumnLayout { + visible: !page.collapsed + spacing: 0 + + ListView { + id: spaceList + property string activeSpaceId: "" + + orientation: Qt.Horizontal + spacing: Kirigami.Units.smallSpacing + clip: true + visible: spaceList.count > 0 + + Layout.preferredHeight: Kirigami.Units.gridUnit * 2 + Layout.fillWidth: true + + model: SortFilterSpaceListModel { + id: sortFilterSpaceListModel + sourceModel: roomListModel + } + + header: QQC2.ItemDelegate { + id: homeButton + icon.name: "home" + text: i18nc("@action:button", "Show All Rooms") + height: parent.height + width: height + leftPadding: topPadding + rightPadding: topPadding + + contentItem: Kirigami.Icon { + source: "home" + } + + onClicked: { + sortFilterRoomListModel.activeSpaceId = ""; + spaceList.activeSpaceId = ''; + listView.positionViewAtIndex(0, ListView.Beginning); + } + + QQC2.ToolTip.text: homeButton.text + QQC2.ToolTip.visible: hovered + QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay + } + + delegate: QQC2.ItemDelegate { + required property string avatar + required property var currentRoom + required property int index + required property string id + + height: parent.height + width: height + leftPadding: topPadding + rightPadding: topPadding + + contentItem: Kirigami.Avatar { + name: currentRoom.displayName + source: avatar !== "" ? "image://mxc/" + avatar : "" + } + + onClicked: { + spaceList.activeSpaceId = id; + sortFilterRoomListModel.activeSpaceId = id; + } + + Accessible.name: currentRoom.displayName + + QQC2.ToolTip.text: currentRoom.displayName + QQC2.ToolTip.visible: hovered + QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay + + onPressAndHold: { + spaceList.createContextMenu(currentRoom) + } + TapHandler { + acceptedButtons: Qt.RightButton + acceptedDevices: PointerDevice.Mouse + onTapped: spaceList.createContextMenu(currentRoom) + } + } + function createContextMenu(room) { + const menu = spaceListContextMenu.createObject(page, {room: room}) + menu.open() + } + } + + Kirigami.Separator { + Layout.fillWidth: true + } + + Component { + id: spaceListContextMenu + SpaceListContextMenu {} + } + } + + property var enteredRoom + + Connections { + target: RoomManager + function onCurrentRoomChanged() { + itemSelection.setCurrentIndex(roomListModel.index(roomListModel.indexForRoom(RoomManager.currentRoom), 0), ItemSelectionModel.SelectCurrent) + } + } + + function goToNextRoomFiltered(condition) { + let index = listView.currentIndex; + while (index++ !== listView.count - 1) { + if (condition(listView.itemAtIndex(index))) { + listView.currentIndex = index; + listView.currentItem.action.trigger(); + return; + } + } + } + + function goToPreviousRoomFiltered(condition) { + let index = listView.currentIndex; + while (index-- !== 0) { + if (condition(listView.itemAtIndex(index))) { + listView.currentIndex = index; + listView.currentItem.action.trigger(); + return; + } + } + } + + function goToNextRoom() { + goToNextRoomFiltered((item) => item.visible); + } + + function goToPreviousRoom() { + goToPreviousRoomFiltered((item) => item.visible); + } + + function goToNextUnreadRoom() { + goToNextRoomFiltered((item) => (item.visible && item.hasUnread)); + } + + function goToPreviousUnreadRoom() { + goToPreviousRoomFiltered((item) => (item.visible && item.hasUnread)); + } + + titleDelegate: ExploreComponent { + Layout.fillWidth: true + desiredWidth: page.width - Kirigami.Units.largeSpacing + collapsed: page.collapsed + } + + ListView { + id: listView + + activeFocusOnTab: true + clip: Accounts.count > 1 + + header: QQC2.ItemDelegate { + visible: page.collapsed + action: Kirigami.Action { + id: enterRoomAction + onTriggered: quickView.item.open(); + } + topPadding: Kirigami.Units.largeSpacing + leftPadding: Kirigami.Units.largeSpacing + rightPadding: Kirigami.Units.largeSpacing + bottomPadding: Kirigami.Units.largeSpacing + width: visible ? ListView.view.width : 0 + height: visible ? Kirigami.Units.gridUnit * 2 : 0 + + Kirigami.Icon { + anchors.centerIn: parent + width: 22 + height: 22 + source: "search" + } + Kirigami.Separator { + width: parent.width + anchors.bottom: parent.bottom + } + } + + Layout.fillWidth: true + + Kirigami.PlaceholderMessage { + anchors.centerIn: parent + width: parent.width - (Kirigami.Units.largeSpacing * 4) + visible: listView.count == 0 + text: sortFilterRoomListModel.filterText.length > 0 ? i18n("No rooms found") : i18n("Join some rooms to get started") + helpfulAction: Kirigami.Action { + icon.name: sortFilterRoomListModel.filterText.length > 0 ? "search" : "list-add" + text: sortFilterRoomListModel.filterText.length > 0 ? i18n("Search in room directory") : i18n("Explore rooms") + onTriggered: pageStack.layers.push("qrc:/JoinRoomPage.qml", { + connection: Controller.activeConnection, + keyword: sortFilterRoomListModel.filterText + }) + } + } + + + ItemSelectionModel { + id: itemSelection + model: roomListModel + onCurrentChanged: { + listView.currentIndex = sortFilterRoomListModel.mapFromSource(current).row + } + } + + model: SortFilterRoomListModel { + id: sortFilterRoomListModel + sourceModel: RoomListModel { + id: roomListModel + connection: Controller.activeConnection + } + roomSortOrder: Config.mergeRoomList ? SortFilterRoomListModel.LastActivity : SortFilterRoomListModel.Categories + onLayoutChanged: { + listView.currentIndex = sortFilterRoomListModel.mapFromSource(itemSelection.currentIndex).row + } + } + + section.property: sortFilterRoomListModel.filterText.length === 0 && !Config.mergeRoomList ? "category" : null + section.delegate: page.collapsed ? foldButton : sectionHeader + + Component { + id: sectionHeader + Kirigami.ListSectionHeader { + height: implicitHeight + label: roomListModel.categoryName(section) + action: Kirigami.Action { + onTriggered: roomListModel.setCategoryVisible(section, !roomListModel.categoryVisible(section)) + } + contentItem.children: QQC2.ToolButton { + icon.name: (roomListModel.categoryVisible(section) ? "go-up" : "go-down") + icon.width: Kirigami.Units.iconSizes.small + icon.height: Kirigami.Units.iconSizes.small + + onClicked: roomListModel.setCategoryVisible(section, !roomListModel.categoryVisible(section)) + } + } + } + Component { + id: foldButton + Item { + width: ListView.view.width + height: visible ? width : 0 + QQC2.ToolButton { + id: button + anchors.centerIn: parent + + icon.name: hovered ? (roomListModel.categoryVisible(section) ? "go-up" : "go-down") : roomListModel.categoryIconName(section) + icon.width: Kirigami.Units.iconSizes.smallMedium + icon.height: Kirigami.Units.iconSizes.smallMedium + + onClicked: roomListModel.setCategoryVisible(section, !roomListModel.categoryVisible(section)) + + QQC2.ToolTip.text: roomListModel.categoryName(section) + QQC2.ToolTip.visible: hovered + QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay + } + } + } + + reuseItems: true + currentIndex: -1 // we don't want any room highlighted by default + + delegate: page.collapsed ? collapsedModeListComponent : normalModeListComponent + + Component { + id: collapsedModeListComponent + + QQC2.ItemDelegate { + action: Kirigami.Action { + id: enterRoomAction + onTriggered: { + RoomManager.enterRoom(currentRoom); + } + } + Keys.onEnterPressed: enterRoomAction.trigger() + Keys.onReturnPressed: enterRoomAction.trigger() + topPadding: Kirigami.Units.largeSpacing + leftPadding: Kirigami.Units.largeSpacing + rightPadding: Kirigami.Units.largeSpacing + bottomPadding: Kirigami.Units.largeSpacing + width: ListView.view.width + height: visible ? ListView.view.width : 0 + visible: model.categoryVisible || sortFilterRoomListModel.filterText.length > 0 || Config.mergeRoomList + + contentItem: Kirigami.Avatar { + source: avatar ? "image://mxc/" + avatar : "" + name: model.name || i18n("No Name") + sourceSize.width: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing * 2 + sourceSize.height: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing * 2 + } + + QQC2.ToolTip { + enabled: text.length !== 0 + text: name ?? "" + } + } + } + + Component { + id: roomListContextMenu + RoomListContextMenu {} + } + + Component { + id: normalModeListComponent + Kirigami.BasicListItem { + id: roomListItem + visible: model.categoryVisible || sortFilterRoomListModel.filterText.length > 0 || Config.mergeRoomList + topPadding: Kirigami.Units.largeSpacing + bottomPadding: Kirigami.Units.largeSpacing + highlighted: listView.currentIndex === index + focus: true + icon: undefined + action: Kirigami.Action { + id: enterRoomAction + onTriggered: { + RoomManager.enterRoom(currentRoom); + } + } + Keys.onEnterPressed: enterRoomAction.trigger() + Keys.onReturnPressed: enterRoomAction.trigger() + bold: unreadCount > 0 + label: name ?? "" + labelItem.textFormat: Text.PlainText + subtitle: subtitleText + subtitleItem.textFormat: Text.PlainText + subtitleItem.visible: !Config.compactRoomList + onPressAndHold: { + createRoomListContextMenu() + } + TapHandler { + acceptedButtons: Qt.RightButton + acceptedDevices: PointerDevice.Mouse + onTapped: createRoomListContextMenu() + } + + leading: Kirigami.Avatar { + source: avatar ? "image://mxc/" + avatar : "" + name: model.name || i18n("No Name") + implicitWidth: visible ? height : 0 + visible: Config.showAvatarInRoomDrawer + sourceSize.width: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing * 2 + sourceSize.height: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing * 2 + } + + trailing: RowLayout { + Kirigami.Icon { + source: "notifications-disabled" + enabled: false + implicitWidth: Kirigami.Units.iconSizes.smallMedium + implicitHeight: Kirigami.Units.iconSizes.smallMedium + Layout.rightMargin: Kirigami.Units.smallSpacing + visible: currentRoom.pushNotificationState === PushNotificationState.Mute && !configButton.visible && unreadCount <= 0 + Accessible.name: i18n("Muted room") + } + QQC2.Label { + id: notificationCountLabel + text: notificationCount > 0 ? notificationCount : "●" + visible: unreadCount > 0 + color: Kirigami.Theme.textColor + Layout.rightMargin: Kirigami.Units.smallSpacing + Layout.minimumHeight: Kirigami.Units.iconSizes.smallMedium + Layout.minimumWidth: Math.max(notificationCountTextMetrics.advanceWidth + Kirigami.Units.smallSpacing * 2, height) + horizontalAlignment: Text.AlignHCenter + background: Rectangle { + visible: notificationCount > 0 + Kirigami.Theme.colorSet: Kirigami.Theme.Button + color: highlightCount > 0 ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.disabledTextColor + opacity: highlightCount > 0 ? 1 : 0.3 + radius: height / 2 + } + + TextMetrics { + id: notificationCountTextMetrics + text: notificationCountLabel.text + } + } + QQC2.Button { + id: configButton + visible: roomListItem.hovered && !Kirigami.Settings.isMobile && !Config.compactRoomList + Accessible.name: i18n("Configure room") + + action: Kirigami.Action { + id: optionAction + icon.name: "configure" + onTriggered: { + createRoomListContextMenu() + } + } + } + } + + function createRoomListContextMenu() { + const menu = roomListContextMenu.createObject(page, {room: currentRoom}) + if (!Kirigami.Settings.isMobile && !Config.compactRoomList) { + configButton.visible = true + configButton.down = true + } + menu.closed.connect(function() { + configButton.down = undefined + configButton.visible = Qt.binding(function() { return roomListItem.hovered && !Kirigami.Settings.isMobile && !Config.compactRoomList }) + }) + menu.open() + } + + readonly property bool hasUnread: unreadCount > 0 + } + } + } + + footer: UserInfo { + width: parent.width + visible: !page.collapsed + } + + MouseArea { + anchors.top: parent.top + anchors.bottom: parent.bottom + parent: applicationWindow().overlay.parent + + x: page.currentWidth - width / 2 + width: Kirigami.Units.smallSpacing * 2 + z: page.z + 1 + enabled: RoomManager.hasOpenRoom && applicationWindow().width >= Kirigami.Units.gridUnit * 35 + visible: enabled + cursorShape: Qt.SplitHCursor + + property int _lastX + + onPressed: mouse => { + _lastX = mouse.x; + } + onPositionChanged: mouse => { + if (_lastX == -1) { + return; + } + if (mouse.x > _lastX) { + // we moved to the right + if (_private.currentWidth < _private.collapseWidth && _private.currentWidth + (mouse.x - _lastX) >= _private.collapseWidth) { + // Here we get back directly to a more wide mode. + _private.currentWidth = _private.collapseWidth; + Config.collapsed = false; + } else if (_private.currentWidth >= _private.collapseWidth) { + // Increase page width + _private.currentWidth = Math.min(_private.defaultWidth, _private.currentWidth + (mouse.x - _lastX)); + } + } else if (mouse.x < _lastX) { + const tmpWidth = _private.currentWidth - (_lastX - mouse.x); + if (tmpWidth < _private.collapseWidth) { + _private.currentWidth = Qt.binding(() => _private.collapsedSize + (page.contentItem.QQC2.ScrollBar.vertical.visible ? page.contentItem.QQC2.ScrollBar.vertical.width : 0)); + Config.collapsed = true; + } else { + _private.currentWidth = tmpWidth; + } + } + } + } + + /* + * Hold the modifiable currentWidth in a private object so that only internal + * members can modify it. + */ + QtObject { + id: _private + property int currentWidth: Config.collapsed ? collapsedSize : defaultWidth + readonly property int defaultWidth: Kirigami.Units.gridUnit * 17 + readonly property int collapseWidth: Kirigami.Units.gridUnit * 10 + readonly property int collapsedSize: Kirigami.Units.gridUnit * 3 - Kirigami.Units.smallSpacing * 3 + } +} diff --git a/src/qml/Settings/AccountsPage.qml b/src/qml/Settings/AccountsPage.qml index e8a7f75fa..b929772ae 100644 --- a/src/qml/Settings/AccountsPage.qml +++ b/src/qml/Settings/AccountsPage.qml @@ -27,7 +27,7 @@ Kirigami.ScrollablePage { } Repeater { - model: AccountRegistry + model: Accounts delegate: MobileForm.AbstractFormDelegate { Layout.fillWidth: true onClicked: pageSettingStack.pushDialogLayer("qrc:/AccountEditorPage.qml", { diff --git a/src/qml/main.qml b/src/qml/main.qml index 5dc3a740a..4e34f956f 100644 --- a/src/qml/main.qml +++ b/src/qml/main.qml @@ -231,24 +231,12 @@ Kirigami.ApplicationWindow { } } - Connections { - target: AccountRegistry - function onRowsRemoved() { - if (AccountRegistry.rowCount() === 0) { - RoomManager.reset(); - pageStack.clear(); - roomListLoaded = false; - pageStack.push("qrc:/WelcomePage.qml"); - } - } - } - Connections { target: Controller - function onInitiated() { - if (Controller.accountCount === 0) { - pageStack.replace("qrc:/WelcomePage.qml", {}); + function onActiveConnectionChanged() { + if (!Controller.activeConnection) { + pageStack.replace("qrc:/imports/NeoChat/Page/WelcomePage.qml", {}); } else if (!roomListLoaded) { pageStack.replace(roomListComponent, { activeConnection: Controller.activeConnection diff --git a/src/roommanager.cpp b/src/roommanager.cpp index b1b1ce1cb..484a4763d 100644 --- a/src/roommanager.cpp +++ b/src/roommanager.cpp @@ -11,9 +11,7 @@ #include #include #include -#ifdef QUOTIENT_07 #include -#endif #include #include @@ -158,16 +156,12 @@ UriResolveResult RoomManager::visitUser(User *user, const QString &action) { if (action == "mention" || action.isEmpty()) { // send it has QVariantMap because the properties in the -#ifdef QUOTIENT_07 user->load(); -#endif Q_EMIT showUserDetail(user); } else if (action == "_interactive") { user->requestDirectChat(); } else if (action == "chat") { -#ifdef QUOTIENT_07 user->load(); -#endif Q_EMIT askDirectChatConfirmation(user); } else { return Quotient::IncorrectAction; @@ -210,8 +204,6 @@ void RoomManager::joinRoom(Quotient::Connection *account, const QString &roomAli }); } -// TODO: maybe need use the function upstream later -#ifdef QUOTIENT_07 void RoomManager::knockRoom(Quotient::Connection *account, const QString &roomAliasOrId, const QString &reason, const QStringList &viaServers) { auto *const job = account->callApi(roomAliasOrId, viaServers, reason); @@ -229,7 +221,6 @@ void RoomManager::knockRoom(Quotient::Connection *account, const QString &roomAl } }); } -#endif bool RoomManager::visitNonMatrix(const QUrl &url) { diff --git a/src/roommanager.h b/src/roommanager.h index 60267149d..d14da8679 100644 --- a/src/roommanager.h +++ b/src/roommanager.h @@ -59,10 +59,7 @@ public: // Overrided methods from UriResolverBase UriResolveResult visitUser(User *user, const QString &action) override; void joinRoom(Quotient::Connection *account, const QString &roomAliasOrId, const QStringList &viaServers) override; - // TODO: it need also override in the feature? -#ifdef QUOTIENT_07 void knockRoom(Quotient::Connection *account, const QString &roomAliasOrId, const QString &reason, const QStringList &viaServers); -#endif Q_INVOKABLE void visitRoom(Quotient::Room *room, const QString &eventId) override; Q_INVOKABLE bool visitNonMatrix(const QUrl &url) override; diff --git a/src/spacehierarchycache.cpp b/src/spacehierarchycache.cpp index 91b182901..f16197ad5 100644 --- a/src/spacehierarchycache.cpp +++ b/src/spacehierarchycache.cpp @@ -3,13 +3,11 @@ #include "spacehierarchycache.h" -#ifdef QUOTIENT_07 -#include -#endif #include #include "controller.h" #include "neochatroom.h" +#include using namespace Quotient; @@ -26,7 +24,6 @@ SpaceHierarchyCache::SpaceHierarchyCache(QObject *parent) void SpaceHierarchyCache::cacheSpaceHierarchy() { -#ifdef QUOTIENT_07 auto connection = Controller::instance().activeConnection(); if (!connection) { return; @@ -45,7 +42,6 @@ void SpaceHierarchyCache::cacheSpaceHierarchy() }); } } -#endif } void SpaceHierarchyCache::populateSpaceHierarchy(const QString &spaceId) @@ -54,7 +50,6 @@ void SpaceHierarchyCache::populateSpaceHierarchy(const QString &spaceId) if (!connection) { return; } -#ifdef QUOTIENT_07 GetSpaceHierarchyJob *job = connection->callApi(spaceId); connect(job, &BaseJob::success, this, [this, job, spaceId]() { @@ -69,7 +64,6 @@ void SpaceHierarchyCache::populateSpaceHierarchy(const QString &spaceId) m_spaceHierarchy.insert(spaceId, roomList); Q_EMIT spaceHierarchyChanged(); }); -#endif } void SpaceHierarchyCache::addSpaceToHierarchy(Quotient::Room *room) diff --git a/src/stickerevent.cpp b/src/stickerevent.cpp deleted file mode 100644 index 6fd9591b5..000000000 --- a/src/stickerevent.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-FileCopyrightText: 2020 Carl Schwan -// SPDX-License-Identifier: LGPL-2.1-or-later - -#include "stickerevent.h" - -using namespace Quotient; - -StickerEvent::StickerEvent(const QJsonObject &obj) -#ifdef QUOTIENT_07 - : RoomEvent(obj) -#else - : RoomEvent(typeId(), obj) -#endif - , m_imageContent(EventContent::ImageContent(obj["content"_ls].toObject())) -{ -} - -QString StickerEvent::body() const -{ - return content("body"_ls); -} - -const EventContent::ImageContent &StickerEvent::image() const -{ - return m_imageContent; -} - -QUrl StickerEvent::url() const -{ -#ifdef QUOTIENT_07 - return m_imageContent.url(); -#else - return m_imageContent.url; -#endif -} diff --git a/src/stickerevent.h b/src/stickerevent.h deleted file mode 100644 index e44e098b0..000000000 --- a/src/stickerevent.h +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: 2020 Carl Schwan -// SPDX-License-Identifier: LGPL-2.1-or-later - -#pragma once - -#include -#include - -namespace Quotient -{ -/// Sticker messages are specialised image messages that are displayed without -/// controls (e.g. no "download" link, or light-box view on click, as would be -/// displayed for for m.image events). -class StickerEvent : public RoomEvent -{ -public: -#ifdef QUOTIENT_07 - QUO_EVENT(StickerEvent, "m.sticker") -#else - DEFINE_EVENT_TYPEID("m.sticker", StickerEvent) -#endif - - explicit StickerEvent(const QJsonObject &obj); - - /// \brief A textual representation or associated description of the - /// sticker image. - /// - /// This could be the alt text of the original image, or a message to - /// accompany and further describe the sticker. - QString body() const; - - /// \brief Metadata about the image referred to in url including a - /// thumbnail representation. - const EventContent::ImageContent &image() const; - - /// \brief The URL to the sticker image. This must be a valid mxc:// URI. - QUrl url() const; - -private: - EventContent::ImageContent m_imageContent; -}; -REGISTER_EVENT_TYPE(StickerEvent) -}