Compare commits

...

3 Commits

Author SHA1 Message Date
Tobias Fella
ce3915e22a Remove unneeded change 2023-04-11 16:02:47 +02:00
Tobias Fella
741f6ea659 Apply 1 suggestion(s) to 1 file(s) 2023-04-11 16:02:47 +02:00
Tobias Fella
310e9b7ba3 Remove compatibility with libQuotient 0.6 2023-04-11 16:02:45 +02:00
39 changed files with 676 additions and 1069 deletions

View File

@@ -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(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) if(ANDROID)
find_package(OpenSSL) find_package(OpenSSL)
set_package_properties(OpenSSL PROPERTIES 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) find_package(KF${QT_MAJOR_VERSION}DBusAddons ${KF_MIN_VERSION} REQUIRED)
endif() endif()
find_package(Quotient 0.6) find_package(Quotient 0.7)
set_package_properties(Quotient PROPERTIES set_package_properties(Quotient PROPERTIES
TYPE REQUIRED TYPE REQUIRED
DESCRIPTION "Qt wrapper around Matrix API" DESCRIPTION "Qt wrapper around Matrix API"
@@ -131,9 +125,6 @@ set_package_properties(KF${QT_MAJOR_VERSION}DocTools PROPERTIES DESCRIPTION
TYPE OPTIONAL TYPE OPTIONAL
) )
if(NOT Quotient_VERSION_MINOR GREATER 6)
cmake_policy(SET CMP0063 OLD)
endif()
if(ANDROID) if(ANDROID)
find_package(Sqlite3) find_package(Sqlite3)
@@ -150,7 +141,7 @@ install(FILES org.kde.neochat.tray.svg DESTINATION ${KDE_INSTALL_FULL_ICONDIR}/h
add_definitions(-DQT_NO_FOREACH) add_definitions(-DQT_NO_FOREACH)
add_subdirectory(src) 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) find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test)
add_subdirectory(autotests) add_subdirectory(autotests)
endif() endif()

View File

@@ -65,7 +65,6 @@ private Q_SLOTS:
void receiveRichEdited(); void receiveRichEdited();
}; };
#ifdef QUOTIENT_07
void TextHandlerTest::initTestCase() void TextHandlerTest::initTestCase()
{ {
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org")); connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
@@ -198,7 +197,6 @@ void TextHandlerTest::initTestCase()
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, json.object()); SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, json.object());
room->update(std::move(roomData)); room->update(std::move(roomData));
} }
#endif
void TextHandlerTest::allowedAttributes() void TextHandlerTest::allowedAttributes()
{ {
@@ -473,7 +471,6 @@ void TextHandlerTest::receiveRichtextIn()
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString); QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
} }
#ifdef QUOTIENT_07
void TextHandlerTest::receiveRichMxcUrl() void TextHandlerTest::receiveRichMxcUrl()
{ {
const QString testInputString = QStringLiteral( const QString testInputString = QStringLiteral(
@@ -491,7 +488,6 @@ void TextHandlerTest::receiveRichMxcUrl()
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText, room, room->messageEvents().at(0).get()), testOutputString); 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. * For when your rich input string has a plain text url left in.

View File

@@ -30,7 +30,6 @@ add_library(neochat STATIC
models/devicesmodel.cpp models/devicesmodel.cpp
filetypesingleton.cpp filetypesingleton.cpp
login.cpp login.cpp
stickerevent.cpp
models/webshortcutmodel.cpp models/webshortcutmodel.cpp
blurhash.cpp blurhash.cpp
blurhashimageprovider.cpp blurhashimageprovider.cpp
@@ -47,8 +46,12 @@ add_library(neochat STATIC
filetransferpseudojob.cpp filetransferpseudojob.cpp
models/searchmodel.cpp models/searchmodel.cpp
texthandler.cpp texthandler.cpp
pollevent.cpp
pollhandler.cpp
) )
target_compile_definitions(neochat PUBLIC "-DQT_NO_KEYWORDS")
add_executable(neochat-app add_executable(neochat-app
main.cpp main.cpp
res.qrc res.qrc
@@ -60,14 +63,6 @@ target_link_libraries(neochat-app PRIVATE
neochat 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) ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png)
target_sources(neochat-app PRIVATE ${NEOCHAT_ICON}) target_sources(neochat-app PRIVATE ${NEOCHAT_ICON})
@@ -95,6 +90,7 @@ endif()
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR}) 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) 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) kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
if(NEOCHAT_FLATPAK) if(NEOCHAT_FLATPAK)

View File

@@ -31,11 +31,7 @@
#include <signal.h> #include <signal.h>
#ifdef QUOTIENT_07 #include <accountregistry.h>
#include "accountregistry.h"
#else
#include "neochataccountregistry.h"
#endif
#include <connection.h> #include <connection.h>
#include <csapi/content-repo.h> #include <csapi/content-repo.h>
@@ -43,11 +39,8 @@
#include <csapi/profile.h> #include <csapi/profile.h>
#include <jobs/downloadfilejob.h> #include <jobs/downloadfilejob.h>
#include <qt_connection_util.h> #include <qt_connection_util.h>
#ifdef QUOTIENT_07
#include <csapi/notifications.h> #include <csapi/notifications.h>
#include <eventstats.h> #include <eventstats.h>
#endif
#include "neochatconfig.h" #include "neochatconfig.h"
#include "neochatroom.h" #include "neochatroom.h"
@@ -56,6 +49,8 @@
#include "roommanager.h" #include "roommanager.h"
#include "windowcontroller.h" #include "windowcontroller.h"
#include <accountregistry.h>
#if defined(Q_OS_WIN) || defined(Q_OS_MAC) #if defined(Q_OS_WIN) || defined(Q_OS_MAC)
#include "trayicon.h" #include "trayicon.h"
#elif !defined(Q_OS_ANDROID) #elif !defined(Q_OS_ANDROID)
@@ -93,10 +88,28 @@ Controller::Controller(QObject *parent)
}); });
#endif #endif
QTimer::singleShot(0, this, [this] { connectUntil(&Accounts, &AccountRegistry::rowsInserted, this, [this]() {
invokeLogin(); 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(), [] { QObject::connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit, QGuiApplication::instance(), [] {
NeoChatConfig::self()->save(); NeoChatConfig::self()->save();
}); });
@@ -125,23 +138,29 @@ Controller::Controller(QObject *parent)
} }
#endif #endif
connect(&AccountRegistry::instance(), &AccountRegistry::accountCountChanged, this, &Controller::activeConnectionIndexChanged); connect(&Accounts, &AccountRegistry::accountCountChanged, this, &Controller::activeConnectionIndexChanged);
#ifdef QUOTIENT_07
static int oldAccountCount = 0; static int oldAccountCount = 0;
connect(&AccountRegistry::instance(), &AccountRegistry::accountCountChanged, this, [=]() { connect(&Accounts, &AccountRegistry::accountCountChanged, this, [this]() {
if (AccountRegistry::instance().size() > oldAccountCount) { if (Accounts.size() > oldAccountCount) {
auto connection = AccountRegistry::instance().accounts()[AccountRegistry::instance().size() - 1]; auto connection = Accounts.accounts()[Accounts.size() - 1];
connect(connection, &Connection::syncDone, this, [=]() { connect(connection, &Connection::syncDone, this, [this, connection]() {
handleNotifications(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<Quotient::Connection> connection) void Controller::handleNotifications(QPointer<Quotient::Connection> connection)
{ {
static QStringList initial; static QStringList initial;
@@ -215,7 +234,6 @@ void Controller::handleNotifications(QPointer<Quotient::Connection> connection)
} }
}); });
} }
#endif
Controller &Controller::instance() Controller &Controller::instance()
{ {
@@ -228,239 +246,10 @@ void Controller::showWindow()
WindowController::instance().showAndRaiseWindow(QString()); 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) void Controller::changeAvatar(Connection *conn, const QUrl &localFile)
{ {
auto job = conn->uploadFile(localFile.toLocalFile()); auto job = conn->uploadFile(localFile.toLocalFile());
#ifdef QUOTIENT_07
if (isJobPending(job)) { if (isJobPending(job)) {
#else
if (isJobRunning(job)) {
#endif
connect(job, &BaseJob::success, this, [conn, job] { connect(job, &BaseJob::success, this, [conn, job] {
conn->callApi<SetAvatarUrlJob>(conn->userId(), job->contentUri()); conn->callApi<SetAvatarUrlJob>(conn->userId(), job->contentUri());
}); });
@@ -487,7 +276,7 @@ bool Controller::supportSystemTray() const
void Controller::changePassword(Connection *connection, const QString &currentPassword, const QString &newPassword) void Controller::changePassword(Connection *connection, const QString &currentPassword, const QString &newPassword)
{ {
NeochatChangePasswordJob *job = connection->callApi<NeochatChangePasswordJob>(newPassword, false); auto *job = connection->callApi<NeochatChangePasswordJob>(newPassword, false);
connect(job, &BaseJob::result, this, [this, job, currentPassword, newPassword, connection] { connect(job, &BaseJob::result, this, [this, job, currentPassword, newPassword, connection] {
if (job->error() == 103) { if (job->error() == 103) {
QJsonObject replyData = job->jsonData(); QJsonObject replyData = job->jsonData();
@@ -498,7 +287,7 @@ void Controller::changePassword(Connection *connection, const QString &currentPa
authData["user"] = connection->user()->id(); authData["user"] = connection->user()->id();
QJsonObject identifier = {{"type", "m.id.user"}, {"user", connection->user()->id()}}; QJsonObject identifier = {{"type", "m.id.user"}, {"user", connection->user()->id()}};
authData["identifier"] = identifier; authData["identifier"] = identifier;
NeochatChangePasswordJob *innerJob = connection->callApi<NeochatChangePasswordJob>(newPassword, false, authData); auto *innerJob = connection->callApi<NeochatChangePasswordJob>(newPassword, false, authData);
connect(innerJob, &BaseJob::success, this, [this]() { connect(innerJob, &BaseJob::success, this, [this]() {
Q_EMIT passwordStatus(PasswordStatus::Success); Q_EMIT passwordStatus(PasswordStatus::Success);
}); });
@@ -518,11 +307,7 @@ bool Controller::setAvatar(Connection *connection, const QUrl &avatarSource)
User *localUser = connection->user(); User *localUser = connection->user();
QString decoded = avatarSource.path(); QString decoded = avatarSource.path();
if (decoded.isEmpty()) { if (decoded.isEmpty()) {
#ifdef QUOTIENT_07
connection->callApi<SetAvatarUrlJob>(localUser->id(), avatarSource); connection->callApi<SetAvatarUrlJob>(localUser->id(), avatarSource);
#else
connection->callApi<SetAvatarUrlJob>(localUser->id(), QString());
#endif
return true; return true;
} }
if (QImageReader(decoded).read().isNull()) { 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<QJsonObject> &auth) NeochatChangePasswordJob::NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const Omittable<QJsonObject> &auth)
#ifdef QUOTIENT_07
: BaseJob(HttpVerb::Post, QStringLiteral("ChangePasswordJob"), "/_matrix/client/r0/account/password") : 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; QJsonObject _data;
addParam<>(_data, QStringLiteral("new_password"), newPassword); addParam<>(_data, QStringLiteral("new_password"), newPassword);
@@ -546,9 +327,12 @@ NeochatChangePasswordJob::NeochatChangePasswordJob(const QString &newPassword, b
setRequestData(_data); setRequestData(_data);
} }
int Controller::accountCount() const NeochatDeleteDeviceJob::NeochatDeleteDeviceJob(const QString &deviceId, const Omittable<QJsonObject> &auth)
: Quotient::BaseJob(HttpVerb::Delete, QStringLiteral("DeleteDeviceJob"), QStringLiteral("/matrix/client/r0/devices/%1").arg(deviceId).toLatin1())
{ {
return AccountRegistry::instance().count(); QJsonObject _data;
addParam<IfNotEmpty>(_data, QStringLiteral("auth"), auth);
setRequestData(std::move(_data));
} }
bool Controller::quitOnLastWindowClosed() bool Controller::quitOnLastWindowClosed()
@@ -629,18 +413,6 @@ void Controller::saveWindowGeometry()
WindowController::instance().saveGeometry(); WindowController::instance().saveGeometry();
} }
NeochatDeleteDeviceJob::NeochatDeleteDeviceJob(const QString &deviceId, const Omittable<QJsonObject> &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<IfNotEmpty>(_data, QStringLiteral("auth"), auth);
setRequestData(std::move(_data));
}
void Controller::createRoom(const QString &name, const QString &topic) void Controller::createRoom(const QString &name, const QString &topic)
{ {
auto createRoomJob = m_connection->createRoom(Connection::PublishRoom, "", name, topic, QStringList()); auto createRoomJob = m_connection->createRoom(Connection::PublishRoom, "", name, topic, QStringList());
@@ -651,7 +423,7 @@ void Controller::createRoom(const QString &name, const QString &topic)
this, this,
&Controller::roomAdded, &Controller::roomAdded,
this, this,
[this](NeoChatRoom *room) { [](NeoChatRoom *room) {
RoomManager::instance().enterRoom(room); RoomManager::instance().enterRoom(room);
}, },
Qt::QueuedConnection); Qt::QueuedConnection);
@@ -731,11 +503,7 @@ QString Controller::plainText(QQuickTextDocument *document) const
bool Controller::encryptionSupported() const bool Controller::encryptionSupported() const
{ {
#ifdef QUOTIENT_07
return Quotient::encryptionSupported(); return Quotient::encryptionSupported();
#else
return false;
#endif
} }
void Controller::forceRefreshTextDocument(QQuickTextDocument *textDocument, QQuickItem *item) void Controller::forceRefreshTextDocument(QQuickTextDocument *textDocument, QQuickItem *item)
@@ -776,29 +544,10 @@ void Controller::setApplicationProxy()
int Controller::activeConnectionIndex() const int Controller::activeConnectionIndex() const
{ {
#ifdef QUOTIENT_07
auto result = std::find_if(Accounts.accounts().begin(), Accounts.accounts().end(), [this](const auto &it) { auto result = std::find_if(Accounts.accounts().begin(), Accounts.accounts().end(), [this](const auto &it) {
return it == m_connection; return it == m_connection;
}); });
return result - Accounts.accounts().begin(); 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 bool Controller::isFlatpak() const

View File

@@ -23,15 +23,9 @@ class Connection;
class Room; class Room;
} }
namespace QKeychain
{
class ReadPasswordJob;
}
class Controller : public QObject class Controller : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(int accountCount READ accountCount NOTIFY accountCountChanged)
Q_PROPERTY(bool quitOnLastWindowClosed READ quitOnLastWindowClosed WRITE setQuitOnLastWindowClosed NOTIFY quitOnLastWindowClosedChanged) Q_PROPERTY(bool quitOnLastWindowClosed READ quitOnLastWindowClosed WRITE setQuitOnLastWindowClosed NOTIFY quitOnLastWindowClosedChanged)
Q_PROPERTY(Quotient::Connection *activeConnection READ activeConnection WRITE setActiveConnection NOTIFY activeConnectionChanged) Q_PROPERTY(Quotient::Connection *activeConnection READ activeConnection WRITE setActiveConnection NOTIFY activeConnectionChanged)
Q_PROPERTY(bool busy READ busy WRITE setBusy NOTIFY busyChanged) 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 isOnline READ isOnline NOTIFY isOnlineChanged)
Q_PROPERTY(bool encryptionSupported READ encryptionSupported CONSTANT) Q_PROPERTY(bool encryptionSupported READ encryptionSupported CONSTANT)
Q_PROPERTY(int activeConnectionIndex READ activeConnectionIndex NOTIFY activeConnectionIndexChanged) Q_PROPERTY(int activeConnectionIndex READ activeConnectionIndex NOTIFY activeConnectionIndexChanged)
Q_PROPERTY(int quotientMinorVersion READ quotientMinorVersion CONSTANT)
Q_PROPERTY(bool isFlatpak READ isFlatpak CONSTANT) Q_PROPERTY(bool isFlatpak READ isFlatpak CONSTANT)
public: public:
@@ -49,17 +42,10 @@ public:
void setActiveConnection(Quotient::Connection *connection); void setActiveConnection(Quotient::Connection *connection);
[[nodiscard]] Quotient::Connection *activeConnection() const; [[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 &currentPassword, const QString &newPassword); Q_INVOKABLE void changePassword(Quotient::Connection *connection, const QString &currentPassword, const QString &newPassword);
Q_INVOKABLE bool setAvatar(Quotient::Connection *connection, const QUrl &avatarSource); Q_INVOKABLE bool setAvatar(Quotient::Connection *connection, const QUrl &avatarSource);
[[nodiscard]] int accountCount() const;
[[nodiscard]] static bool quitOnLastWindowClosed(); [[nodiscard]] static bool quitOnLastWindowClosed();
void setQuitOnLastWindowClosed(bool value); void setQuitOnLastWindowClosed(bool value);
@@ -68,8 +54,6 @@ public:
[[nodiscard]] bool supportSystemTray() const; [[nodiscard]] bool supportSystemTray() const;
bool saveAccessTokenToKeyChain(const Quotient::AccountSettings &account, const QByteArray &accessToken);
int activeConnectionIndex() const; int activeConnectionIndex() const;
enum PasswordStatus { enum PasswordStatus {
@@ -100,7 +84,6 @@ public:
Q_INVOKABLE void setApplicationProxy(); Q_INVOKABLE void setApplicationProxy();
int quotientMinorVersion() const;
bool isFlatpak() const; bool isFlatpak() const;
private: private:
@@ -110,20 +93,15 @@ private:
bool m_busy = false; bool m_busy = false;
TrayIcon *m_trayIcon = nullptr; TrayIcon *m_trayIcon = nullptr;
QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const Quotient::AccountSettings &account);
void loadSettings(); void loadSettings();
void saveSettings() const; void saveSettings() const;
bool m_isOnline = true; bool m_isOnline = true;
QMap<Quotient::Room *, int> m_notificationCounts; QMap<Quotient::Room *, int> m_notificationCounts;
bool hasWindowSystem() const; bool hasWindowSystem() const;
#ifdef QUOTIENT_07
void handleNotifications(QPointer<Quotient::Connection> connection); void handleNotifications(QPointer<Quotient::Connection> connection);
#endif
private Q_SLOTS: private Q_SLOTS:
void invokeLogin();
void showWindow(); void showWindow();
Q_SIGNALS: Q_SIGNALS:
@@ -136,7 +114,6 @@ Q_SIGNALS:
void syncDone(); void syncDone();
void connectionAdded(Quotient::Connection *_t1); void connectionAdded(Quotient::Connection *_t1);
void connectionDropped(Quotient::Connection *_t1); void connectionDropped(Quotient::Connection *_t1);
void accountCountChanged();
void initiated(); void initiated();
void notificationClicked(const QString &_t1, const QString &_t2); void notificationClicked(const QString &_t1, const QString &_t2);
void quitOnLastWindowClosedChanged(); void quitOnLastWindowClosedChanged();
@@ -154,13 +131,12 @@ Q_SIGNALS:
void roomAdded(NeoChatRoom *room); void roomAdded(NeoChatRoom *room);
public Q_SLOTS: public Q_SLOTS:
void logout(Quotient::Connection *conn, bool serverSideLogout);
void changeAvatar(Quotient::Connection *conn, const QUrl &localFile); void changeAvatar(Quotient::Connection *conn, const QUrl &localFile);
static void markAllMessagesAsRead(Quotient::Connection *conn); static void markAllMessagesAsRead(Quotient::Connection *conn);
void saveWindowGeometry(); void saveWindowGeometry();
}; };
// TODO libQuotient 0.7: Drop // TODO libQuotient 0.?: Drop
class NeochatChangePasswordJob : public Quotient::BaseJob class NeochatChangePasswordJob : public Quotient::BaseJob
{ {
public: public:

View File

@@ -25,6 +25,7 @@ void FileTransferPseudoJob::fileTransferProgress(QString id, qint64 progress, qi
void FileTransferPseudoJob::fileTransferCompleted(QString id, QUrl localFile) void FileTransferPseudoJob::fileTransferCompleted(QString id, QUrl localFile)
{ {
Q_UNUSED(localFile);
if (id != m_eventId) { if (id != m_eventId) {
return; return;
} }

View File

@@ -7,30 +7,17 @@
namespace Quotient namespace Quotient
{ {
#ifdef QUOTIENT_07
class JoinRulesEvent : public StateEvent class JoinRulesEvent : public StateEvent
#else
class JoinRulesEvent : public StateEventBase
#endif
{ {
public: public:
#ifdef QUOTIENT_07
QUO_EVENT(JoinRulesEvent, "m.room.join_rules") QUO_EVENT(JoinRulesEvent, "m.room.join_rules")
#else
DEFINE_EVENT_TYPEID("m.room.join_rules", JoinRulesEvent)
#endif
explicit JoinRulesEvent(const QJsonObject &obj) explicit JoinRulesEvent(const QJsonObject &obj)
#ifdef QUOTIENT_07
: StateEvent(obj) : StateEvent(obj)
#else
: StateEventBase(typeId(), obj)
#endif
{ {
} }
QString joinRule() const; QString joinRule() const;
QJsonArray allow() const; QJsonArray allow() const;
}; };
REGISTER_EVENT_TYPE(JoinRulesEvent)
} }

View File

@@ -3,16 +3,13 @@
#include "login.h" #include "login.h"
#ifdef QUOTIENT_07
#include <accountregistry.h> #include <accountregistry.h>
#else
#include "neochataccountregistry.h"
#endif
#include <connection.h> #include <connection.h>
#include <qt_connection_util.h> #include <qt_connection_util.h>
#include "controller.h" #include "controller.h"
#include "neochatroom.h"
#include <KLocalizedString> #include <KLocalizedString>
@@ -43,7 +40,7 @@ void Login::init()
return; return;
} }
m_isLoggedIn = AccountRegistry::instance().isLoggedIn(m_matrixId); m_isLoggedIn = Accounts.isLoggedIn(m_matrixId);
Q_EMIT isLoggedInChanged(); Q_EMIT isLoggedInChanged();
if (m_isLoggedIn) { if (m_isLoggedIn) {
return; return;
@@ -74,11 +71,7 @@ void Login::init()
account.setHomeserver(m_connection->homeserver()); account.setHomeserver(m_connection->homeserver());
account.setDeviceId(m_connection->deviceId()); account.setDeviceId(m_connection->deviceId());
account.setDeviceName(m_deviceName); account.setDeviceName(m_deviceName);
if (!Controller::instance().saveAccessTokenToKeyChain(account, m_connection->accessToken())) {
qWarning() << "Couldn't save access token";
}
account.sync(); account.sync();
Controller::instance().addConnection(m_connection);
Controller::instance().setActiveConnection(m_connection); Controller::instance().setActiveConnection(m_connection);
m_connection = nullptr; m_connection = nullptr;
}); });
@@ -97,8 +90,9 @@ void Login::init()
Q_EMIT Controller::instance().globalErrorOccured(i18n("Network Error"), std::move(error)); Q_EMIT Controller::instance().globalErrorOccured(i18n("Network Error"), std::move(error));
}); });
connectSingleShot(m_connection, &Connection::syncDone, this, [this]() { connectSingleShot(m_connection, &Connection::loadedRoomState, this, [this]() {
Q_EMIT Controller::instance().initiated(); Controller::instance().setActiveConnection(m_connection);
// TODO close settings window
}); });
} }

View File

@@ -28,15 +28,14 @@
#include "neochat-version.h" #include "neochat-version.h"
#ifdef QUOTIENT_07
#include <accountregistry.h> #include <accountregistry.h>
#else
#include "neochataccountregistry.h"
#endif
#include <networkaccessmanager.h> #include <networkaccessmanager.h>
#include <room.h> #include <room.h>
#include <util.h> #include <util.h>
#include <keyverificationsession.h>
#include <accountregistry.h>
#include <room.h>
#include <networkaccessmanager.h>
#include "actionshandler.h" #include "actionshandler.h"
#include "blurhashimageprovider.h" #include "blurhashimageprovider.h"
@@ -68,16 +67,11 @@
#include "neochatroom.h" #include "neochatroom.h"
#include "neochatuser.h" #include "neochatuser.h"
#include "notificationsmanager.h" #include "notificationsmanager.h"
#ifdef QUOTIENT_07
#include "pollhandler.h" #include "pollhandler.h"
#endif
#include "roommanager.h" #include "roommanager.h"
#include "spacehierarchycache.h" #include "spacehierarchycache.h"
#include "urlhelper.h" #include "urlhelper.h"
#include "windowcontroller.h" #include "windowcontroller.h"
#ifdef QUOTIENT_07
#include <keyverificationsession.h>
#endif
#ifdef HAVE_COLORSCHEME #ifdef HAVE_COLORSCHEME
#include "colorschemer.h" #include "colorschemer.h"
#endif #endif
@@ -85,6 +79,7 @@
#include "models/statemodel.h" #include "models/statemodel.h"
#include "neochatuser.h" #include "neochatuser.h"
#ifdef HAVE_RUNNER #ifdef HAVE_RUNNER
#include "runner.h" #include "runner.h"
#include <QDBusConnection> #include <QDBusConnection>
@@ -165,14 +160,10 @@ int main(int argc, char *argv[])
about.addComponent(QStringLiteral("libQuotient"), about.addComponent(QStringLiteral("libQuotient"),
i18n("A Qt5 library to write cross-platform clients for Matrix"), i18n("A Qt5 library to write cross-platform clients for Matrix"),
#ifdef QUOTIENT_07
i18nc("<version number> (built against <possibly different version number>)", i18nc("<version number> (built against <possibly different version number>)",
"%1 (built against %2)", "%1 (built against %2)",
Quotient::versionString(), Quotient::versionString(),
QStringLiteral(Quotient_VERSION_STRING)), QStringLiteral(Quotient_VERSION_STRING)),
#else
QStringLiteral(QUOTIENT_VERSION),
#endif
QStringLiteral("https://github.com/quotient-im/libquotient"), QStringLiteral("https://github.com/quotient-im/libquotient"),
KAboutLicense::LGPL_V2_1); 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, "LoginHelper", login);
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "UrlHelper", &urlHelper); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "UrlHelper", &urlHelper);
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "EmojiModel", &EmojiModel::instance()); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "EmojiModel", &EmojiModel::instance());
#ifdef QUOTIENT_07 qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Accounts", &Quotient::Accounts);
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "AccountRegistry", &Quotient::Accounts); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Accounts", &Quotient::Accounts);
#else
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "AccountRegistry", &Quotient::AccountRegistry::instance());
#endif
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "SpaceHierarchyCache", &SpaceHierarchyCache::instance()); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "SpaceHierarchyCache", &SpaceHierarchyCache::instance());
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "CustomEmojiModel", &CustomEmojiModel::instance()); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "CustomEmojiModel", &CustomEmojiModel::instance());
qmlRegisterType<ActionsHandler>("org.kde.neochat", 1, 0, "ActionsHandler"); qmlRegisterType<ActionsHandler>("org.kde.neochat", 1, 0, "ActionsHandler");
@@ -235,9 +223,7 @@ int main(int argc, char *argv[])
qmlRegisterType<CompletionModel>("org.kde.neochat", 1, 0, "CompletionModel"); qmlRegisterType<CompletionModel>("org.kde.neochat", 1, 0, "CompletionModel");
qmlRegisterType<StateModel>("org.kde.neochat", 1, 0, "StateModel"); qmlRegisterType<StateModel>("org.kde.neochat", 1, 0, "StateModel");
qmlRegisterType<SearchModel>("org.kde.neochat", 1, 0, "SearchModel"); qmlRegisterType<SearchModel>("org.kde.neochat", 1, 0, "SearchModel");
#ifdef QUOTIENT_07
qmlRegisterType<PollHandler>("org.kde.neochat", 1, 0, "PollHandler"); qmlRegisterType<PollHandler>("org.kde.neochat", 1, 0, "PollHandler");
#endif
qmlRegisterType<KeywordNotificationRuleModel>("org.kde.neochat", 1, 0, "KeywordNotificationRuleModel"); qmlRegisterType<KeywordNotificationRuleModel>("org.kde.neochat", 1, 0, "KeywordNotificationRuleModel");
qmlRegisterUncreatableType<RoomMessageEvent>("org.kde.neochat", 1, 0, "RoomMessageEvent", "ENUM"); qmlRegisterUncreatableType<RoomMessageEvent>("org.kde.neochat", 1, 0, "RoomMessageEvent", "ENUM");
qmlRegisterUncreatableType<PushNotificationState>("org.kde.neochat", 1, 0, "PushNotificationState", "ENUM"); qmlRegisterUncreatableType<PushNotificationState>("org.kde.neochat", 1, 0, "PushNotificationState", "ENUM");
@@ -256,12 +242,10 @@ int main(int argc, char *argv[])
qRegisterMetaType<NeoChatUser *>("NeoChatUser*"); qRegisterMetaType<NeoChatUser *>("NeoChatUser*");
qRegisterMetaType<GetRoomEventsJob *>("GetRoomEventsJob*"); qRegisterMetaType<GetRoomEventsJob *>("GetRoomEventsJob*");
qRegisterMetaType<QMimeType>("QMimeType"); qRegisterMetaType<QMimeType>("QMimeType");
#ifdef QUOTIENT_07
#ifdef Quotient_E2EE_ENABLED #ifdef Quotient_E2EE_ENABLED
qRegisterMetaType<KeyVerificationSession *>("KeyVerificationSession*"); qRegisterMetaType<KeyVerificationSession *>("KeyVerificationSession*");
qmlRegisterUncreatableType<KeyVerificationSession>("org.kde.neochat", 1, 0, "KeyVerificationSession", {}); qmlRegisterUncreatableType<KeyVerificationSession>("org.kde.neochat", 1, 0, "KeyVerificationSession", {});
qRegisterMetaType<QVector<EmojiEntry>>("QVector<EmojiEntry>"); qRegisterMetaType<QVector<EmojiEntry>>("QVector<EmojiEntry>");
#endif
#endif #endif
qmlRegisterSingletonType("org.kde.neochat", 1, 0, "About", [](QQmlEngine *engine, QJSEngine *) -> QJSValue { qmlRegisterSingletonType("org.kde.neochat", 1, 0, "About", [](QQmlEngine *engine, QJSEngine *) -> QJSValue {
return engine->toScriptValue(KAboutData::applicationData()); return engine->toScriptValue(KAboutData::applicationData());

View File

@@ -192,7 +192,6 @@ QVector<ActionsModel::Action> actions{
Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", text)); Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", text));
return QString(); return QString();
} }
#ifdef QUOTIENT_07
const RoomMemberEvent *roomMemberEvent = room->currentState().get<RoomMemberEvent>(text); const RoomMemberEvent *roomMemberEvent = room->currentState().get<RoomMemberEvent>(text);
if (roomMemberEvent && roomMemberEvent->membership() == Membership::Invite) { if (roomMemberEvent && roomMemberEvent->membership() == Membership::Invite) {
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is already invited to this room.", "%1 is already invited to this room.", text)); Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is already invited to this room.", "%1 is already invited to this room.", text));
@@ -202,7 +201,6 @@ QVector<ActionsModel::Action> actions{
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is banned from this room.", "%1 is banned from this room.", text)); Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is banned from this room.", "%1 is banned from this room.", text));
return QString(); return QString();
} }
#endif
if (room->localUser()->id() == text) { if (room->localUser()->id() == text) {
Q_EMIT room->showMessage(NeoChatRoom::Positive, i18n("You are already in this room.")); Q_EMIT room->showMessage(NeoChatRoom::Positive, i18n("You are already in this room."));
return QString(); return QString();
@@ -244,7 +242,6 @@ QVector<ActionsModel::Action> actions{
kli18n("<room alias or id>"), kli18n("<room alias or id>"),
kli18n("Joins the given room"), kli18n("Joins the given room"),
}, },
#ifdef QUOTIENT_07
Action{ Action{
QStringLiteral("knock"), QStringLiteral("knock"),
[](const QString &text, NeoChatRoom *room) { [](const QString &text, NeoChatRoom *room) {
@@ -277,7 +274,6 @@ QVector<ActionsModel::Action> actions{
kli18n("<room alias or id> [<reason>]"), kli18n("<room alias or id> [<reason>]"),
kli18n("Requests to join the given room"), kli18n("Requests to join the given room"),
}, },
#endif
Action{ Action{
QStringLiteral("j"), QStringLiteral("j"),
[](const QString &text, NeoChatRoom *room) { [](const QString &text, NeoChatRoom *room) {
@@ -436,14 +432,12 @@ QVector<ActionsModel::Action> actions{
Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", text)); Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", text));
return QString(); return QString();
} }
#ifdef QUOTIENT_07
auto state = room->currentState().get<RoomMemberEvent>(parts[0]); auto state = room->currentState().get<RoomMemberEvent>(parts[0]);
if (state && state->membership() == Membership::Ban) { if (state && state->membership() == Membership::Ban) {
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is already banned from this room.", "%1 is already banned from this room.", text)); Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is already banned from this room.", "%1 is already banned from this room.", text));
return QString(); return QString();
} }
#endif auto plEvent = room->currentState().get<RoomPowerLevelsEvent>();
auto plEvent = room->getCurrentState<RoomPowerLevelsEvent>();
if (plEvent->ban() > plEvent->powerLevelForUser(room->localUser()->id())) { 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.")); Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You are not allowed to ban users from this room."));
return QString(); return QString();
@@ -473,18 +467,16 @@ QVector<ActionsModel::Action> actions{
Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", text)); Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", text));
return QString(); return QString();
} }
auto plEvent = room->getCurrentState<RoomPowerLevelsEvent>(); auto plEvent = room->currentState().get<RoomPowerLevelsEvent>();
if (plEvent->ban() > plEvent->powerLevelForUser(room->localUser()->id())) { 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.")); Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You are not allowed to unban users from this room."));
return QString(); return QString();
} }
#ifdef QUOTIENT_07
auto state = room->currentState().get<RoomMemberEvent>(text); auto state = room->currentState().get<RoomMemberEvent>(text);
if (state && state->membership() != Membership::Ban) { if (state && state->membership() != Membership::Ban) {
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is not banned from this room.", "%1 is not banned from this room.", text)); Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is not banned from this room.", "%1 is not banned from this room.", text));
return QString(); return QString();
} }
#endif
room->unban(text); room->unban(text);
Q_EMIT room->showMessage(NeoChatRoom::Positive, i18nc("<username> was unbanned from this room.", "%1 was unbanned from this room.", text)); Q_EMIT room->showMessage(NeoChatRoom::Positive, i18nc("<username> was unbanned from this room.", "%1 was unbanned from this room.", text));
@@ -511,13 +503,11 @@ QVector<ActionsModel::Action> actions{
Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You cannot kick yourself from the room.")); Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You cannot kick yourself from the room."));
return QString(); return QString();
} }
#ifdef QUOTIENT_07
if (!room->isMember(parts[0])) { if (!room->isMember(parts[0])) {
Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("<username> is not in this room", "%1 is not in this room.", parts[0])); Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("<username> is not in this room", "%1 is not in this room.", parts[0]));
return QString(); return QString();
} }
#endif auto plEvent = room->currentState().get<RoomPowerLevelsEvent>();
auto plEvent = room->getCurrentState<RoomPowerLevelsEvent>();
auto kick = plEvent->kick(); auto kick = plEvent->kick();
if (plEvent->powerLevelForUser(room->localUser()->id()) < 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.")); Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You are not allowed to kick users from this room."));

View File

@@ -11,12 +11,6 @@
using namespace Quotient; using namespace Quotient;
#ifdef QUOTIENT_07
#define running isJobPending
#else
#define running isJobRunning
#endif
void CustomEmojiModel::fetchEmojis() void CustomEmojiModel::fetchEmojis()
{ {
if (!Controller::instance().activeConnection()) { 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()); auto job = Controller::instance().activeConnection()->uploadFile(location.toLocalFile());
if (running(job)) { if (isJobPending(job)) {
connect(job, &BaseJob::success, this, [this, name, job] { connect(job, &BaseJob::success, this, [name, job] {
const auto &data = Controller::instance().activeConnection()->accountData("im.ponies.user_emotes"); const auto &data = Controller::instance().activeConnection()->accountData("im.ponies.user_emotes");
auto json = data != nullptr ? data->contentJson() : QJsonObject(); auto json = data != nullptr ? data->contentJson() : QJsonObject();
auto emojiData = json["images"].toObject(); auto emojiData = json["images"].toObject();
emojiData[QStringLiteral("%1").arg(name)] = QJsonObject({ emojiData[QStringLiteral("%1").arg(name)] = QJsonObject({{QStringLiteral("url"), job->contentUri().toString()}});
#ifdef QUOTIENT_07
{QStringLiteral("url"), job->contentUri().toString()}
#else
{QStringLiteral("url"), job->contentUri()}
#endif
});
json["images"] = emojiData; json["images"] = emojiData;
Controller::instance().activeConnection()->setAccountData("im.ponies.user_emotes", json); 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)); return QUrl(QStringLiteral("image://mxc/") + data.url.mid(6));
case Roles::MxcUrl: case Roles::MxcUrl:
return data.url.mid(6); return data.url.mid(6);
default:
return {};
} }
return QVariant(); return QVariant();
} }

View File

@@ -67,7 +67,7 @@ QHash<int, QByteArray> DevicesModel::roleNames() const
void DevicesModel::logout(int index, const QString &password) void DevicesModel::logout(int index, const QString &password)
{ {
auto job = Controller::instance().activeConnection()->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId); auto job = Controller::instance().activeConnection()->callApi<DeleteDeviceJob>(m_devices[index].deviceId);
connect(job, &BaseJob::result, this, [this, job, password, index] { connect(job, &BaseJob::result, this, [this, job, password, index] {
auto onSuccess = [this, index]() { auto onSuccess = [this, index]() {

View File

@@ -11,12 +11,10 @@
#include <events/roomavatarevent.h> #include <events/roomavatarevent.h>
#include <events/roommemberevent.h> #include <events/roommemberevent.h>
#include <events/simplestateevents.h> #include <events/simplestateevents.h>
#include <events/stickerevent.h>
#include <user.h> #include <user.h>
#ifdef QUOTIENT_07
#include "pollevent.h" #include "pollevent.h"
#endif
#include "stickerevent.h"
#include <QDebug> #include <QDebug>
#include <QGuiApplication> #include <QGuiApplication>
@@ -109,11 +107,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
if (m_currentRoom->timelineSize() < 10 && !room->allHistoryLoaded()) { if (m_currentRoom->timelineSize() < 10 && !room->allHistoryLoaded()) {
room->getPreviousContent(50); room->getPreviousContent(50);
} }
#ifdef QUOTIENT_07
lastReadEventId = room->lastFullyReadEventId(); lastReadEventId = room->lastFullyReadEventId();
#else
lastReadEventId = room->readMarkerEventId();
#endif
using namespace Quotient; using namespace Quotient;
connect(m_currentRoom, &Room::aboutToAddNewMessages, this, [this](RoomEventsRange events) { connect(m_currentRoom, &Room::aboutToAddNewMessages, this, [this](RoomEventsRange events) {
@@ -162,11 +156,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
endInsertRows(); endInsertRows();
if (!m_lastReadEventIndex.isValid()) { if (!m_lastReadEventIndex.isValid()) {
// no read marker, so see if we need to create one. // no read marker, so see if we need to create one.
#ifdef QUOTIENT_07
moveReadMarker(m_currentRoom->lastFullyReadEventId()); moveReadMarker(m_currentRoom->lastFullyReadEventId());
#else
moveReadMarker(m_currentRoom->readMarkerEventId());
#endif
} }
if (biggest < m_currentRoom->maxTimelineIndex()) { if (biggest < m_currentRoom->maxTimelineIndex()) {
auto rowBelowInserted = m_currentRoom->maxTimelineIndex() - biggest + timelineBaseIndex() - 1; auto rowBelowInserted = m_currentRoom->maxTimelineIndex() - biggest + timelineBaseIndex() - 1;
@@ -207,7 +197,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
beginRemoveRows({}, i, i); beginRemoveRows({}, i, i);
}); });
connect(m_currentRoom, &Room::pendingEventDiscarded, this, &MessageEventModel::endRemoveRows); 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); Q_UNUSED(fromEventId);
moveReadMarker(toEventId); moveReadMarker(toEventId);
}); });
@@ -230,9 +220,6 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
connect(m_currentRoom, &Room::fileTransferProgress, this, &MessageEventModel::refreshEvent); connect(m_currentRoom, &Room::fileTransferProgress, this, &MessageEventModel::refreshEvent);
connect(m_currentRoom, &Room::fileTransferCompleted, this, &MessageEventModel::refreshEvent); connect(m_currentRoom, &Room::fileTransferCompleted, this, &MessageEventModel::refreshEvent);
connect(m_currentRoom, &Room::fileTransferFailed, 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] { connect(m_currentRoom->connection(), &Connection::ignoredUsersListChanged, this, [this] {
beginResetModel(); beginResetModel();
endResetModel(); endResetModel();
@@ -498,7 +485,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
} }
if (role == SourceRole) { if (role == SourceRole) {
return evt.originalJson(); return QJsonDocument(evt.fullJson()).toJson();
} }
if (role == DelegateTypeRole) { if (role == DelegateTypeRole) {
@@ -534,20 +521,18 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
if (is<const EncryptedEvent>(evt)) { if (is<const EncryptedEvent>(evt)) {
return DelegateType::Encrypted; return DelegateType::Encrypted;
} }
#ifdef QUOTIENT_07
if (is<PollStartEvent>(evt)) { if (is<PollStartEvent>(evt)) {
if (evt.isRedacted()) { if (evt.isRedacted()) {
return DelegateType::Message; return DelegateType::Message;
} }
return DelegateType::Poll; return DelegateType::Poll;
} }
#endif
return DelegateType::Other; return DelegateType::Other;
} }
if (role == EventResolvedTypeRole) { if (role == EventResolvedTypeRole) {
return EventTypeRegistry::getMatrixType(evt.type()); return evt.type();
} }
if (role == AuthorRole) { if (role == AuthorRole) {
@@ -646,7 +631,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
return EventStatus::Hidden; return EventStatus::Hidden;
} }
if (evt.isStateEvent() && static_cast<const StateEventBase &>(evt).repeatsState()) { if (evt.isStateEvent() && static_cast<const StateEvent &>(evt).repeatsState()) {
return EventStatus::Hidden; return EventStatus::Hidden;
} }
@@ -820,22 +805,13 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
} }
if (role == ReadMarkersRole) { if (role == ReadMarkersRole) {
#ifdef QUOTIENT_07
auto userIds = room()->userIdsAtEvent(evt.id()); auto userIds = room()->userIdsAtEvent(evt.id());
userIds.remove(m_currentRoom->localUser()->id()); userIds.remove(m_currentRoom->localUser()->id());
#else
auto userIds = room()->usersAtEventId(evt.id());
userIds.removeAll(m_currentRoom->localUser());
#endif
QVariantList users; QVariantList users;
users.reserve(userIds.size()); users.reserve(userIds.size());
for (const auto &userId : userIds) { for (const auto &userId : userIds) {
#ifdef QUOTIENT_07
auto user = static_cast<NeoChatUser *>(m_currentRoom->user(userId)); auto user = static_cast<NeoChatUser *>(m_currentRoom->user(userId));
#else
auto user = static_cast<NeoChatUser *>(userId);
#endif
users += userAtEvent(user, m_currentRoom, evt); users += userAtEvent(user, m_currentRoom, evt);
} }
@@ -843,24 +819,15 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
} }
if (role == ReadMarkersStringRole) { if (role == ReadMarkersStringRole) {
#ifdef QUOTIENT_07
auto userIds = room()->userIdsAtEvent(evt.id()); auto userIds = room()->userIdsAtEvent(evt.id());
userIds.remove(m_currentRoom->localUser()->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 * The string ends up in the form
* "x users: user1DisplayName, user2DisplayName, etc." * "x users: user1DisplayName, user2DisplayName, etc."
*/ */
QString readMarkersString = i18np("1 user: ", "%1 users: ", userIds.size()); QString readMarkersString = i18np("1 user: ", "%1 users: ", userIds.size());
for (const auto &userId : userIds) { for (const auto &userId : userIds) {
#ifdef QUOTIENT_07
auto user = static_cast<NeoChatUser *>(m_currentRoom->user(userId)); auto user = static_cast<NeoChatUser *>(m_currentRoom->user(userId));
#else
auto user = static_cast<NeoChatUser *>(userId);
#endif
readMarkersString += user->displayname(m_currentRoom) + i18nc("list separator", ", "); readMarkersString += user->displayname(m_currentRoom) + i18nc("list separator", ", ");
} }
readMarkersString.chop(2); readMarkersString.chop(2);
@@ -868,18 +835,13 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
} }
if (role == ShowReadMarkersRole) { if (role == ShowReadMarkersRole) {
#ifdef QUOTIENT_07
auto userIds = room()->userIdsAtEvent(evt.id()); auto userIds = room()->userIdsAtEvent(evt.id());
userIds.remove(m_currentRoom->localUser()->id()); userIds.remove(m_currentRoom->localUser()->id());
#else
auto userIds = room()->usersAtEventId(evt.id());
userIds.removeAll(m_currentRoom->localUser());
#endif
return userIds.size() > 0; return userIds.size() > 0;
} }
if (role == ReactionRole) { if (role == ReactionRole) {
const auto &annotations = m_currentRoom->relatedEvents(evt, EventRelation::Annotation()); const auto &annotations = m_currentRoom->relatedEvents(evt, EventRelation::AnnotationType);
if (annotations.isEmpty()) { if (annotations.isEmpty()) {
return {}; return {};
}; };
@@ -889,7 +851,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
continue; continue;
} }
if (auto e = eventCast<const ReactionEvent>(a)) { if (auto e = eventCast<const ReactionEvent>(a)) {
reactions[e->relation().key].append(static_cast<NeoChatUser *>(m_currentRoom->user(e->senderId()))); reactions[e->eventId()].append(static_cast<NeoChatUser *>(m_currentRoom->user(e->senderId())));
} }
} }
@@ -916,7 +878,6 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
} }
if (role == MediaUrlRole) { if (role == MediaUrlRole) {
#ifdef QUOTIENT_07
if (auto e = eventCast<const RoomMessageEvent>(&evt)) { if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
if (!e->hasFileContent()) { if (!e->hasFileContent()) {
return QVariant(); return QVariant();
@@ -933,7 +894,6 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
// if (auto e = eventCast<const StickerEvent>(&evt)) { // if (auto e = eventCast<const StickerEvent>(&evt)) {
// return m_currentRoom->makeMediaUrl(e->id(), e->url()); // 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 // Construct link in the same form as urlToDownload as that function doesn't work for stickers
if (auto e = eventCast<const StickerEvent>(&evt)) { if (auto e = eventCast<const StickerEvent>(&evt)) {
@@ -948,14 +908,12 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
} }
if (role == VerifiedRole) { if (role == VerifiedRole) {
#ifdef QUOTIENT_07
#ifdef Quotient_E2EE_ENABLED #ifdef Quotient_E2EE_ENABLED
if (evt.originalEvent()) { if (evt.originalEvent()) {
auto encrypted = dynamic_cast<const EncryptedEvent *>(evt.originalEvent()); auto encrypted = dynamic_cast<const EncryptedEvent *>(evt.originalEvent());
Q_ASSERT(encrypted); Q_ASSERT(encrypted);
return m_currentRoom->connection()->isVerifiedSession(encrypted->sessionId().toLatin1()); return m_currentRoom->connection()->isVerifiedSession(encrypted->sessionId().toLatin1());
} }
#endif
#endif #endif
return false; return false;
} }
@@ -983,7 +941,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
} }
if (role == IsPendingRole) { if (role == IsPendingRole) {
return row < m_currentRoom->pendingEvents().size(); return row < static_cast<int>(m_currentRoom->pendingEvents().size());
} }
return {}; return {};

View File

@@ -117,11 +117,7 @@ void PublicRoomListModel::next(int count)
return; return;
} }
#ifdef QUOTIENT_07
job = m_connection->callApi<QueryPublicRoomsJob>(m_server, count, nextBatch, QueryPublicRoomsJob::Filter{m_keyword, {}}); job = m_connection->callApi<QueryPublicRoomsJob>(m_server, count, nextBatch, QueryPublicRoomsJob::Filter{m_keyword, {}});
#else
job = m_connection->callApi<QueryPublicRoomsJob>(m_server, count, nextBatch, QueryPublicRoomsJob::Filter{m_keyword});
#endif
connect(job, &BaseJob::finished, this, [this] { connect(job, &BaseJob::finished, this, [this] {
attempted = true; attempted = true;
@@ -177,11 +173,7 @@ QVariant PublicRoomListModel::data(const QModelIndex &index, int role) const
if (avatarUrl.isEmpty()) { if (avatarUrl.isEmpty()) {
return ""; return "";
} }
#ifdef QUOTIENT_07
return avatarUrl.url().remove(0, 6); return avatarUrl.url().remove(0, 6);
#else
return avatarUrl.remove(0, 6);
#endif
} }
if (role == TopicRole) { if (role == TopicRole) {
return room.topic; return room.topic;

View File

@@ -8,6 +8,7 @@
#include "neochatroom.h" #include "neochatroom.h"
#include "roommanager.h" #include "roommanager.h"
#include "user.h" #include "user.h"
#include <eventstats.h>
#include <QDebug> #include <QDebug>
#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0) #if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
@@ -22,11 +23,6 @@
#include <QGuiApplication> #include <QGuiApplication>
#include <utility> #include <utility>
#ifndef QUOTIENT_07
#include "notificationsmanager.h"
#include <csapi/notifications.h>
#endif
using namespace Quotient; using namespace Quotient;
Q_DECLARE_METATYPE(Quotient::JoinState) Q_DECLARE_METATYPE(Quotient::JoinState)
@@ -151,7 +147,7 @@ void RoomListModel::connectRoomSignals(NeoChatRoom *room)
connect(room, &Room::displaynameChanged, this, [this, room] { connect(room, &Room::displaynameChanged, this, [this, room] {
refresh(room, {DisplayNameRole, NameRole}); refresh(room, {DisplayNameRole, NameRole});
}); });
connect(room, &Room::unreadMessagesChanged, this, [this, room] { connect(room, &Room::unreadStatsChanged, this, [this, room] {
refresh(room, {UnreadCountRole, NotificationCountRole, HighlightCountRole}); refresh(room, {UnreadCountRole, NotificationCountRole, HighlightCountRole});
}); });
connect(room, &Room::notificationCountChanged, this, [this, room] { connect(room, &Room::notificationCountChanged, this, [this, room] {
@@ -172,71 +168,9 @@ void RoomListModel::connectRoomSignals(NeoChatRoom *room)
connect(room, &Room::pendingEventMerged, this, [this, room] { connect(room, &Room::pendingEventMerged, this, [this, room] {
refresh(room, {LastEventRole, SubtitleTextRole}); 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); 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<GetNotificationsJob>();
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<NeoChatRoom *>(room),
sender->displayname(room),
notification["event"].toObject()["content"].toObject()["body"].toString(),
avatar_image,
notification["event"].toObject()["event_id"].toString(),
true);
}
}
});
}
#endif
void RoomListModel::refreshNotificationCount() void RoomListModel::refreshNotificationCount()
{ {
int count = 0; int count = 0;
@@ -361,7 +295,7 @@ QVariant RoomListModel::data(const QModelIndex &index, int role) const
return NeoChatRoomType::Normal; return NeoChatRoomType::Normal;
} }
if (role == UnreadCountRole) { if (role == UnreadCountRole) {
return room->unreadCount(); return room->unreadStats().notableCount;
} }
if (role == NotificationCountRole) { if (role == NotificationCountRole) {
return room->notificationCount(); return room->notificationCount();

View File

@@ -107,9 +107,6 @@ private:
QString m_activeSpaceId = ""; QString m_activeSpaceId = "";
void connectRoomSignals(NeoChatRoom *room); void connectRoomSignals(NeoChatRoom *room);
#ifndef QUOTIENT_07
void handleNotifications();
#endif
Q_SIGNALS: Q_SIGNALS:
void connectionChanged(); void connectionChanged();

View File

@@ -8,9 +8,7 @@
#include <KLocalizedString> #include <KLocalizedString>
#include <connection.h> #include <connection.h>
#ifdef QUOTIENT_07
#include <csapi/search.h> #include <csapi/search.h>
#endif
using namespace Quotient; using namespace Quotient;
@@ -34,7 +32,6 @@ void SearchModel::setSearchText(const QString &searchText)
void SearchModel::search() void SearchModel::search()
{ {
#ifdef QUOTIENT_07
Q_ASSERT(m_connection); Q_ASSERT(m_connection);
setSearching(true); setSearching(true);
if (m_job) { if (m_job) {
@@ -43,20 +40,26 @@ void SearchModel::search()
} }
SearchJob::RoomEventsCriteria criteria{ SearchJob::RoomEventsCriteria criteria{
m_searchText, .searchTerm = m_searchText,
{}, .keys = {},
RoomEventFilter{ .filter =
.rooms = {m_room->id()}, RoomEventFilter{
}, .unreadThreadNotifications = none,
"recent", .lazyLoadMembers = true,
SearchJob::IncludeEventContext{3, 3, true}, .includeRedundantMembers = false,
false, .notRooms = {},
none, .rooms = {m_room->id()},
.containsUrl = false,
},
.orderBy = "recent",
.eventContext = SearchJob::IncludeEventContext{3, 3, true},
.includeState = false,
.groupings = none,
}; };
auto job = m_connection->callApi<SearchJob>(SearchJob::Categories{criteria}); auto job = m_connection->callApi<SearchJob>(SearchJob::Categories{criteria});
m_job = job; m_job = job;
connect(job, &BaseJob::finished, this, [=] { connect(job, &BaseJob::finished, this, [this, job] {
beginResetModel(); beginResetModel();
m_result = job->searchCategories().roomEvents; m_result = job->searchCategories().roomEvents;
endResetModel(); endResetModel();
@@ -64,7 +67,6 @@ void SearchModel::search()
m_job = nullptr; m_job = nullptr;
// TODO error handling // TODO error handling
}); });
#endif
} }
Connection *SearchModel::connection() const Connection *SearchModel::connection() const
@@ -80,7 +82,6 @@ void SearchModel::setConnection(Connection *connection)
QVariant SearchModel::data(const QModelIndex &index, int role) const QVariant SearchModel::data(const QModelIndex &index, int role) const
{ {
#ifdef QUOTIENT_07
auto row = index.row(); auto row = index.row();
const auto &event = *m_result->results[row].result; const auto &event = *m_result->results[row].result;
switch (role) { switch (role) {
@@ -110,17 +111,14 @@ QVariant SearchModel::data(const QModelIndex &index, int role) const
return event.originTimestamp(); return event.originTimestamp();
} }
return MessageEventModel::DelegateType::Message; return MessageEventModel::DelegateType::Message;
#endif
return {};
} }
int SearchModel::rowCount(const QModelIndex &parent) const int SearchModel::rowCount(const QModelIndex &parent) const
{ {
#ifdef QUOTIENT_07 Q_UNUSED(parent);
if (m_result.has_value()) { if (m_result.has_value()) {
return m_result->results.size(); return m_result->results.size();
} }
#endif
return 0; return 0;
} }

View File

@@ -6,9 +6,7 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QString> #include <QString>
#ifdef QUOTIENT_07
#include <csapi/search.h> #include <csapi/search.h>
#endif
namespace Quotient namespace Quotient
{ {
@@ -68,10 +66,8 @@ private:
QString m_searchText; QString m_searchText;
Quotient::Connection *m_connection = nullptr; Quotient::Connection *m_connection = nullptr;
NeoChatRoom *m_room = nullptr; NeoChatRoom *m_room = nullptr;
#ifdef QUOTIENT_07
Quotient::Omittable<Quotient::SearchJob::ResultRoomEvents> m_result = Quotient::none; Quotient::Omittable<Quotient::SearchJob::ResultRoomEvents> m_result = Quotient::none;
Quotient::SearchJob *m_job = nullptr; Quotient::SearchJob *m_job = nullptr;
#endif
bool m_searching = false; bool m_searching = false;
}; };

View File

@@ -95,11 +95,7 @@ void ServerListModel::checkServer(const QString &url)
KConfigGroup serverGroup(&dataResource, "Servers"); KConfigGroup serverGroup(&dataResource, "Servers");
if (!serverGroup.hasKey(url)) { if (!serverGroup.hasKey(url)) {
#ifdef QUOTIENT_07
if (Quotient::isJobPending(m_checkServerJob)) { if (Quotient::isJobPending(m_checkServerJob)) {
#else
if (Quotient::isJobRunning(m_checkServerJob)) {
#endif
m_checkServerJob->abandon(); m_checkServerJob->abandon();
} }

View File

@@ -14,7 +14,6 @@ QHash<int, QByteArray> StateModel::roleNames() const
} }
QVariant StateModel::data(const QModelIndex &index, int role) const QVariant StateModel::data(const QModelIndex &index, int role) const
{ {
#ifdef QUOTIENT_07
auto row = index.row(); auto row = index.row();
switch (role) { switch (role) {
case TypeRole: case TypeRole:
@@ -24,18 +23,13 @@ QVariant StateModel::data(const QModelIndex &index, int role) const
case SourceRole: case SourceRole:
return QJsonDocument(m_room->currentState().events()[m_room->currentState().events().keys()[row]]->fullJson()).toJson(); return QJsonDocument(m_room->currentState().events()[m_room->currentState().events().keys()[row]]->fullJson()).toJson();
} }
#endif
return {}; return {};
} }
int StateModel::rowCount(const QModelIndex &parent) const int StateModel::rowCount(const QModelIndex &parent) const
{ {
Q_UNUSED(parent); Q_UNUSED(parent);
#ifdef QUOTIENT_07
return m_room->currentState().events().size(); return m_room->currentState().events().size();
#else
return 0;
#endif
} }
NeoChatRoom *StateModel::room() const NeoChatRoom *StateModel::room() const
@@ -49,7 +43,7 @@ void StateModel::setRoom(NeoChatRoom *room)
Q_EMIT roomChanged(); Q_EMIT roomChanged();
beginResetModel(); beginResetModel();
endResetModel(); endResetModel();
connect(room, &NeoChatRoom::changed, this, [=] { connect(room, &NeoChatRoom::changed, this, [this] {
beginResetModel(); beginResetModel();
endResetModel(); endResetModel();
}); });

View File

@@ -131,11 +131,7 @@ QVariant UserDirectoryListModel::data(const QModelIndex &index, int role) const
if (avatarUrl.isEmpty()) { if (avatarUrl.isEmpty()) {
return ""; return "";
} }
#ifdef QUOTIENT_07
return avatarUrl.url().remove(0, 6); return avatarUrl.url().remove(0, 6);
#else
return avatarUrl.remove(0, 6);
#endif
} }
if (role == UserIDRole) { if (role == UserIDRole) {
return user.userId; return user.userId;

View File

@@ -44,13 +44,9 @@ void UserListModel::setRoom(NeoChatRoom *room)
std::sort(m_users.begin(), m_users.end(), room->memberSorter()); std::sort(m_users.begin(), m_users.end(), room->memberSorter());
} }
for (User *user : std::as_const(m_users)) { for (User *user : std::as_const(m_users)) {
#ifdef QUOTIENT_07
connect(user, &User::defaultAvatarChanged, this, [this, user]() { connect(user, &User::defaultAvatarChanged, this, [this, user]() {
avatarChanged(user, m_currentRoom); avatarChanged(user, m_currentRoom);
}); });
#else
connect(user, &User::avatarChanged, this, &UserListModel::avatarChanged);
#endif
} }
connect(m_currentRoom->connection(), &Connection::loggedOut, this, [this]() { connect(m_currentRoom->connection(), &Connection::loggedOut, this, [this]() {
setRoom(nullptr); setRoom(nullptr);
@@ -96,15 +92,14 @@ QVariant UserListModel::data(const QModelIndex &index, int role) const
return QVariant::fromValue(user); return QVariant::fromValue(user);
} }
if (role == PowerLevelRole) { if (role == PowerLevelRole) {
auto pl = m_currentRoom->getCurrentState<RoomPowerLevelsEvent>(); auto pl = m_currentRoom->currentState().get<RoomPowerLevelsEvent>();
if (!pl) {
return 0;
}
return pl->powerLevelForUser(user->id()); return pl->powerLevelForUser(user->id());
} }
if (role == PowerLevelStringRole) { if (role == PowerLevelStringRole) {
#ifdef QUOTIENT_07
auto pl = m_currentRoom->currentState().get<RoomPowerLevelsEvent>(); auto pl = m_currentRoom->currentState().get<RoomPowerLevelsEvent>();
#else
auto pl = m_currentRoom->getCurrentState<RoomPowerLevelsEvent>();
#endif
// User might not in the room yet, in this case pl can be nullptr. // 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. // e.g. When invited but user not accepted or denied the invitation.
if (!pl) { if (!pl) {
@@ -143,13 +138,9 @@ void UserListModel::userAdded(Quotient::User *user)
beginInsertRows(QModelIndex(), pos, pos); beginInsertRows(QModelIndex(), pos, pos);
m_users.insert(pos, user); m_users.insert(pos, user);
endInsertRows(); endInsertRows();
#ifdef QUOTIENT_07
connect(user, &User::defaultAvatarChanged, this, [this, user]() { connect(user, &User::defaultAvatarChanged, this, [this, user]() {
avatarChanged(user, m_currentRoom); avatarChanged(user, m_currentRoom);
}); });
#else
connect(user, &Quotient::User::avatarChanged, this, &UserListModel::avatarChanged);
#endif
} }
void UserListModel::userRemoved(Quotient::User *user) void UserListModel::userRemoved(Quotient::User *user)
@@ -188,13 +179,9 @@ void UserListModel::refreshAll()
std::sort(m_users.begin(), m_users.end(), m_currentRoom->memberSorter()); std::sort(m_users.begin(), m_users.end(), m_currentRoom->memberSorter());
} }
for (User *user : std::as_const(m_users)) { for (User *user : std::as_const(m_users)) {
#ifdef QUOTIENT_07
connect(user, &User::defaultAvatarChanged, this, [this, user]() { connect(user, &User::defaultAvatarChanged, this, [this, user]() {
avatarChanged(user, m_currentRoom); avatarChanged(user, m_currentRoom);
}); });
#else
connect(user, &User::avatarChanged, this, &UserListModel::avatarChanged);
#endif
} }
connect(m_currentRoom->connection(), &Connection::loggedOut, this, [this]() { connect(m_currentRoom->connection(), &Connection::loggedOut, this, [this]() {
setRoom(nullptr); setRoom(nullptr);

View File

@@ -1,105 +0,0 @@
// SPDX-FileCopyrightText: Kitsune Ral <Kitsune-Ral@users.sf.net>
// SPDX-FileCopyrightText: Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: LGPL-2.1-or-later
#include "neochataccountregistry.h"
#include <connection.h>
using namespace Quotient;
void AccountRegistry::add(Connection *c)
{
if (m_accounts.contains(c))
return;
beginInsertRows(QModelIndex(), m_accounts.size(), m_accounts.size());
m_accounts += c;
endInsertRows();
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<int, QByteArray> 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<Connection *> 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;
}

View File

@@ -1,54 +0,0 @@
// SPDX-FileCopyrightText: 2020 Kitsune Ral <Kitsune-Ral@users.sf.net>
// SPDX-FileCopyrightText: Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include <QAbstractListModel>
#include <QList>
#include <QObject>
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<Connection *> 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<int, QByteArray> roleNames() const override;
Q_SIGNALS:
void accountCountChanged();
private:
AccountRegistry();
QVector<Connection *> m_accounts;
};
}

View File

@@ -24,6 +24,7 @@
#include <csapi/room_state.h> #include <csapi/room_state.h>
#include <csapi/typing.h> #include <csapi/typing.h>
#include <events/encryptionevent.h> #include <events/encryptionevent.h>
#include <events/eventrelation.h>
#include <events/reactionevent.h> #include <events/reactionevent.h>
#include <events/redactionevent.h> #include <events/redactionevent.h>
#include <events/roomavatarevent.h> #include <events/roomavatarevent.h>
@@ -31,23 +32,19 @@
#include <events/roommemberevent.h> #include <events/roommemberevent.h>
#include <events/roompowerlevelsevent.h> #include <events/roompowerlevelsevent.h>
#include <events/simplestateevents.h> #include <events/simplestateevents.h>
#include <events/stickerevent.h>
#include <eventstats.h>
#include <jobs/downloadfilejob.h> #include <jobs/downloadfilejob.h>
#ifndef QUOTIENT_07
#include <joinstate.h>
#endif
#include <qt_connection_util.h> #include <qt_connection_util.h>
#include "controller.h" #include "controller.h"
#include "filetransferpseudojob.h"
#include "joinrulesevent.h" #include "joinrulesevent.h"
#include "neochatconfig.h" #include "neochatconfig.h"
#include "neochatuser.h" #include "neochatuser.h"
#include "notificationsmanager.h" #include "notificationsmanager.h"
#ifdef QUOTIENT_07
#include "pollevent.h" #include "pollevent.h"
#include "pollhandler.h" #include "pollhandler.h"
#endif
#include "filetransferpseudojob.h"
#include "stickerevent.h"
#include "texthandler.h" #include "texthandler.h"
#ifndef Q_OS_ANDROID #ifndef Q_OS_ANDROID
@@ -85,7 +82,7 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
if (this->joinState() != JoinState::Invite) { if (this->joinState() != JoinState::Invite) {
return; return;
} }
const QString senderId = getCurrentState<RoomMemberEvent>(localUser()->id())->senderId(); const QString senderId = currentState().get<RoomMemberEvent>(localUser()->id())->senderId();
QImage avatar_image; QImage avatar_image;
if (!user(senderId)->avatarUrl(this).isEmpty()) { if (!user(senderId)->avatarUrl(this).isEmpty()) {
avatar_image = user(senderId)->avatar(128, this); avatar_image = user(senderId)->avatar(128, this);
@@ -174,17 +171,9 @@ QCoro::Task<void> NeoChatRoom::doUploadFile(QUrl url, QString body)
} else { } else {
content = new EventContent::FileContent(url, fileInfo.size(), mime, fileInfo.fileName()); content = new EventContent::FileContent(url, fileInfo.size(), mime, fileInfo.fileName());
} }
#ifdef QUOTIENT_07
QString txnId = postFile(body.isEmpty() ? url.fileName() : body, content); QString txnId = postFile(body.isEmpty() ? url.fileName() : body, content);
#else
QString txnId = postFile(body.isEmpty() ? url.fileName() : body, url, false);
#endif
setHasFileUploading(true); setHasFileUploading(true);
#ifdef QUOTIENT_07
connect(this, &Room::fileTransferCompleted, [this, txnId](const QString &id, FileSourceInfo) { 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) { if (id == txnId) {
setFileUploadingProgress(0); setFileUploadingProgress(0);
setHasFileUploading(false); setHasFileUploading(false);
@@ -254,21 +243,20 @@ const RoomEvent *NeoChatRoom::lastEvent() const
continue; continue;
} }
if (event->isStateEvent() && !NeoChatConfig::self()->showStateEvent()) { if (event->isStateEvent() && !NeoChatConfig::showStateEvent()) {
continue; continue;
} }
if (auto roomMemberEvent = eventCast<const RoomMemberEvent>(event)) { if (auto roomMemberEvent = eventCast<const RoomMemberEvent>(event)) {
if ((roomMemberEvent->isJoin() || roomMemberEvent->isLeave()) && !NeoChatConfig::self()->showLeaveJoinEvent()) { if ((roomMemberEvent->isJoin() || roomMemberEvent->isLeave()) && !NeoChatConfig::showLeaveJoinEvent()) {
continue; continue;
} else if (roomMemberEvent->isRename() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave() && !NeoChatConfig::self()->showRename()) { } else if (roomMemberEvent->isRename() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave() && !NeoChatConfig::showRename()) {
continue; continue;
} else if (roomMemberEvent->isAvatarUpdate() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave() } else if (roomMemberEvent->isAvatarUpdate() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave() && !NeoChatConfig::showAvatarUpdate()) {
&& !NeoChatConfig::self()->showAvatarUpdate()) {
continue; continue;
} }
} }
if (event->isStateEvent() && static_cast<const StateEventBase &>(*event).repeatsState()) { if (event->isStateEvent() && static_cast<const StateEvent &>(*event).repeatsState()) {
continue; continue;
} }
@@ -293,11 +281,9 @@ const RoomEvent *NeoChatRoom::lastEvent() const
if (auto lastEvent = eventCast<const RoomMessageEvent>(event)) { if (auto lastEvent = eventCast<const RoomMessageEvent>(event)) {
return lastEvent; return lastEvent;
} }
#ifdef QUOTIENT_07
if (auto lastEvent = eventCast<const PollStartEvent>(event)) { if (auto lastEvent = eventCast<const PollStartEvent>(event)) {
return lastEvent; return lastEvent;
} }
#endif
} }
return nullptr; return nullptr;
} }
@@ -318,10 +304,9 @@ bool NeoChatRoom::lastEventIsSpoiler() const
QString NeoChatRoom::lastEventToString(Qt::TextFormat format, bool stripNewlines) const QString NeoChatRoom::lastEventToString(Qt::TextFormat format, bool stripNewlines) const
{ {
if (auto event = lastEvent()) { if (auto event = lastEvent()) {
return roomMembername(event->senderId()) + (event->isStateEvent() ? QLatin1String(" ") : QLatin1String(": ")) return safeMemberName(event->senderId()) + (event->isStateEvent() ? " " : ": ") + eventToString(*event, format, stripNewlines);
+ eventToString(*event, format, stripNewlines);
} }
return QLatin1String(""); return {};
} }
bool NeoChatRoom::isEventHighlighted(const RoomEvent *e) const bool NeoChatRoom::isEventHighlighted(const RoomEvent *e) const
@@ -337,7 +322,7 @@ void NeoChatRoom::checkForHighlights(const Quotient::TimelineItem &ti)
} }
if (auto *e = ti.viewAs<RoomMessageEvent>()) { if (auto *e = ti.viewAs<RoomMessageEvent>()) {
const auto &text = e->plainBody(); const auto &text = e->plainBody();
if (text.contains(localUserId) || text.contains(roomMembername(localUserId))) { if (text.contains(localUserId) || text.contains(safeMemberName(localUserId))) {
highlights.insert(e); highlights.insert(e);
} }
} }
@@ -360,7 +345,7 @@ void NeoChatRoom::onAddHistoricalTimelineEvents(rev_iter_t from)
void NeoChatRoom::onRedaction(const RoomEvent &prevEvent, const RoomEvent & /*after*/) void NeoChatRoom::onRedaction(const RoomEvent &prevEvent, const RoomEvent & /*after*/)
{ {
if (const auto &e = eventCast<const ReactionEvent>(&prevEvent)) { if (const auto &e = eventCast<const ReactionEvent>(&prevEvent)) {
if (auto relatedEventId = e->relation().eventId; !relatedEventId.isEmpty()) { if (auto relatedEventId = e->eventId(); !relatedEventId.isEmpty()) {
Q_EMIT updatedEvent(relatedEventId); Q_EMIT updatedEvent(relatedEventId);
} }
} }
@@ -368,9 +353,8 @@ void NeoChatRoom::onRedaction(const RoomEvent &prevEvent, const RoomEvent & /*af
void NeoChatRoom::countChanged() void NeoChatRoom::countChanged()
{ {
if (displayed() && !hasUnreadMessages()) { if (displayed() && unreadStats().empty()) {
resetNotificationCount(); setReadReceipt(lastEvent()->id());
resetHighlightCount();
} }
} }
@@ -443,11 +427,7 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format,
const bool prettyPrint = (format == Qt::RichText); const bool prettyPrint = (format == Qt::RichText);
using namespace Quotient; using namespace Quotient;
#ifdef QUOTIENT_07
return switchOnType( return switchOnType(
#else
return visit(
#endif
evt, evt,
[this, format, stripNewlines](const RoomMessageEvent &e) { [this, format, stripNewlines](const RoomMessageEvent &e) {
using namespace MessageEventContent; using namespace MessageEventContent;
@@ -493,14 +473,9 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format,
[this, prettyPrint](const RoomMemberEvent &e) { [this, prettyPrint](const RoomMemberEvent &e) {
// FIXME: Rewind to the name that was at the time of this event // FIXME: Rewind to the name that was at the time of this event
auto subjectName = this->htmlSafeMemberName(e.userId()); auto subjectName = this->htmlSafeMemberName(e.userId());
if (e.membership() == MembershipType::Leave) { if (e.membership() == Membership::Leave) {
#ifdef QUOTIENT_07
if (e.prevContent() && e.prevContent()->displayName) { if (e.prevContent() && e.prevContent()->displayName) {
subjectName = sanitized(*e.prevContent()->displayName).toHtmlEscaped(); 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 // The below code assumes senderName output in AuthorRole
switch (e.membership()) { switch (e.membership()) {
case MembershipType::Invite: case Membership::Invite:
if (e.repeatsState()) { if (e.repeatsState()) {
auto text = i18n("reinvited %1 to the room", subjectName); auto text = i18n("reinvited %1 to the room", subjectName);
if (!e.reason().isEmpty()) { if (!e.reason().isEmpty()) {
@@ -520,13 +495,13 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format,
return text; return text;
} }
Q_FALLTHROUGH(); Q_FALLTHROUGH();
case MembershipType::Join: { case Membership::Join: {
QString text{}; QString text{};
// Part 1: invites and joins // Part 1: invites and joins
if (e.repeatsState()) { if (e.repeatsState()) {
text = i18n("joined the room (repeated)"); text = i18n("joined the room (repeated)");
} else if (e.changesMembership()) { } 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 (!text.isEmpty()) {
if (!e.reason().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 // Part 2: profile changes of joined members
if (e.isRename()) { if (e.isRename()) {
if (e.displayName().isEmpty()) { if (e.newDisplayName()) {
text = i18nc("their refers to a singular user", "cleared their display name"); text = i18nc("their refers to a singular user", "cleared their display name");
} else { } 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 (e.isAvatarUpdate()) {
if (!text.isEmpty()) { if (!text.isEmpty()) {
text += i18n(" and "); text += i18n(" and ");
} }
if (e.avatarUrl().isEmpty()) { if (e.newAvatarUrl()) {
text += i18nc("their refers to a singular user", "cleared their avatar"); text += i18nc("their refers to a singular user", "cleared their avatar");
#ifdef QUOTIENT_07
} else if (!e.prevContent()->avatarUrl) { } else if (!e.prevContent()->avatarUrl) {
#else
} else if (e.prevContent()->avatarUrl.isEmpty()) {
#endif
text += i18n("set an avatar"); text += i18n("set an avatar");
} else { } else {
text += i18nc("their refers to a singular user", "updated their avatar"); 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; return text;
} }
case MembershipType::Leave: case Membership::Leave:
if (e.prevContent() && e.prevContent()->membership == MembershipType::Invite) { if (e.prevContent() && e.prevContent()->membership == Membership::Invite) {
return (e.senderId() != e.userId()) ? i18n("withdrew %1's invitation", subjectName) : i18n("rejected the invitation"); 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("unbanned %1", subjectName) : i18n("self-unbanned");
} }
return (e.senderId() != e.userId()) return (e.senderId() != e.userId())
? i18n("has put %1 out of the room: %2", subjectName, e.contentJson()["reason"_ls].toString().toHtmlEscaped()) ? i18n("has put %1 out of the room: %2", subjectName, e.contentJson()["reason"_ls].toString().toHtmlEscaped())
: i18n("left the room"); : i18n("left the room");
case MembershipType::Ban: case Membership::Ban:
if (e.senderId() != e.userId()) { if (e.senderId() != e.userId()) {
if (e.reason().isEmpty()) { if (e.reason().isEmpty()) {
return i18n("banned %1 from the room", subjectName); return i18n("banned %1 from the room", subjectName);
@@ -584,7 +555,7 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format,
} else { } else {
return i18n("self-banned from the room"); return i18n("self-banned from the room");
} }
case MembershipType::Knock: { case Membership::Knock: {
QString reason(e.contentJson()["reason"_ls].toString().toHtmlEscaped()); QString reason(e.contentJson()["reason"_ls].toString().toHtmlEscaped());
return reason.isEmpty() ? i18n("requested an invite") : i18n("requested an invite with reason: %1", reason); 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 &) { [](const RoomPowerLevelsEvent &) {
return i18nc("'power level' means permission level", "changed the power levels for this room"); 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")) { if (e.matrixType() == QLatin1String("m.room.server_acl")) {
return i18n("changed the server access control lists for this room"); 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()) return e.stateKey().isEmpty() ? i18n("updated %1 state", e.matrixType())
: i18n("updated %1 state for %2", e.matrixType(), e.stateKey().toHtmlEscaped()); : i18n("updated %1 state for %2", e.matrixType(), e.stateKey().toHtmlEscaped());
}, },
#ifdef QUOTIENT_07
[](const PollStartEvent &e) { [](const PollStartEvent &e) {
return e.question(); return e.question();
}, },
#endif
i18n("Unknown event")); i18n("Unknown event"));
} }
QString NeoChatRoom::eventToGenericString(const RoomEvent &evt) const QString NeoChatRoom::eventToGenericString(const RoomEvent &evt) const
{ {
#ifdef QUOTIENT_07
return switchOnType( return switchOnType(
#else
return visit(
#endif
evt, evt,
[](const RoomMessageEvent &e) { [](const RoomMessageEvent &e) {
Q_UNUSED(e) Q_UNUSED(e)
@@ -660,25 +625,25 @@ QString NeoChatRoom::eventToGenericString(const RoomEvent &evt) const
}, },
[](const RoomMemberEvent &e) { [](const RoomMemberEvent &e) {
switch (e.membership()) { switch (e.membership()) {
case MembershipType::Invite: case Membership::Invite:
if (e.repeatsState()) { if (e.repeatsState()) {
return i18n("reinvited someone to the room"); return i18n("reinvited someone to the room");
} }
Q_FALLTHROUGH(); Q_FALLTHROUGH();
case MembershipType::Join: { case Membership::Join: {
QString text{}; QString text{};
// Part 1: invites and joins // Part 1: invites and joins
if (e.repeatsState()) { if (e.repeatsState()) {
text = i18n("joined the room (repeated)"); text = i18n("joined the room (repeated)");
} else if (e.changesMembership()) { } 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()) { if (!text.isEmpty()) {
return text; return text;
} }
// Part 2: profile changes of joined members // Part 2: profile changes of joined members
if (e.isRename()) { if (e.isRename()) {
if (e.displayName().isEmpty()) { if (e.newDisplayName()) {
text = i18nc("their refers to a singular user", "cleared their display name"); text = i18nc("their refers to a singular user", "cleared their display name");
} else { } else {
text = i18nc("their refers to a singular user", "changed their display name"); 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()) { if (!text.isEmpty()) {
text += i18n(" and "); text += i18n(" and ");
} }
if (e.avatarUrl().isEmpty()) { if (e.newAvatarUrl()) {
text += i18nc("their refers to a singular user", "cleared their avatar"); text += i18nc("their refers to a singular user", "cleared their avatar");
#ifdef QUOTIENT_07
} else if (!e.prevContent()->avatarUrl) { } else if (!e.prevContent()->avatarUrl) {
#else
} else if (e.prevContent()->avatarUrl.isEmpty()) {
#endif
text += i18n("set an avatar"); text += i18n("set an avatar");
} else { } else {
text += i18nc("their refers to a singular user", "updated their avatar"); text += i18nc("their refers to a singular user", "updated their avatar");
@@ -705,22 +666,22 @@ QString NeoChatRoom::eventToGenericString(const RoomEvent &evt) const
} }
return text; return text;
} }
case MembershipType::Leave: case Membership::Leave:
if (e.prevContent() && e.prevContent()->membership == MembershipType::Invite) { if (e.prevContent() && e.prevContent()->membership == Membership::Invite) {
return (e.senderId() != e.userId()) ? i18n("withdrew a user's invitation") : i18n("rejected the invitation"); 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("unbanned a user") : i18n("self-unbanned");
} }
return (e.senderId() != e.userId()) ? i18n("put a user out of the room") : i18n("left the room"); 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()) { if (e.senderId() != e.userId()) {
return i18n("banned a user from the room"); return i18n("banned a user from the room");
} else { } else {
return i18n("self-banned from the room"); return i18n("self-banned from the room");
} }
case MembershipType::Knock: { case Membership::Knock: {
return i18n("requested an invite"); return i18n("requested an invite");
} }
default:; default:;
@@ -748,7 +709,7 @@ QString NeoChatRoom::eventToGenericString(const RoomEvent &evt) const
[](const RoomPowerLevelsEvent &) { [](const RoomPowerLevelsEvent &) {
return i18nc("'power level' means permission level", "changed the power levels for this room"); 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")) { if (e.matrixType() == QLatin1String("m.room.server_acl")) {
return i18n("changed the server access control lists for this room"); 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"); return i18n("updated the state");
}, },
#ifdef QUOTIENT_07
[](const PollStartEvent &e) { [](const PollStartEvent &e) {
Q_UNUSED(e);
return i18n("started a poll"); return i18n("started a poll");
}, },
#endif
i18n("Unknown event")); i18n("Unknown event"));
} }
void NeoChatRoom::changeAvatar(const QUrl &localFile) void NeoChatRoom::changeAvatar(const QUrl &localFile)
{ {
const auto job = connection()->uploadFile(localFile.toLocalFile()); const auto job = connection()->uploadFile(localFile.toLocalFile());
#ifdef QUOTIENT_07
if (isJobPending(job)) { if (isJobPending(job)) {
#else
if (isJobRunning(job)) {
#endif
connect(job, &BaseJob::success, this, [this, job] { connect(job, &BaseJob::success, this, [this, job] {
#ifdef QUOTIENT_07
connection()->callApi<SetRoomStateWithKeyJob>(id(), "m.room.avatar", QString(), QJsonObject{{"url", job->contentUri().toString()}}); connection()->callApi<SetRoomStateWithKeyJob>(id(), "m.room.avatar", QString(), QJsonObject{{"url", job->contentUri().toString()}});
#else
connection()->callApi<SetRoomStateWithKeyJob>(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? 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()) { if (!annotations.isEmpty()) {
for (const auto &a : annotations) { for (const auto &a : annotations) {
if (auto e = eventCast<const ReactionEvent>(a)) { if (auto e = eventCast<const ReactionEvent>(a)) {
if (e->relation().key != reaction) { if (e->key() != reaction) {
continue; continue;
} }
@@ -920,18 +872,15 @@ void NeoChatRoom::toggleReaction(const QString &eventId, const QString &reaction
bool NeoChatRoom::containsUser(const QString &userID) const bool NeoChatRoom::containsUser(const QString &userID) const
{ {
auto u = Room::user(userID); return !isMember(userID);
if (!u) {
return false;
}
return Room::memberJoinState(u) != JoinState::Leave;
} }
bool NeoChatRoom::canSendEvent(const QString &eventType) const bool NeoChatRoom::canSendEvent(const QString &eventType) const
{ {
auto plEvent = getCurrentState<RoomPowerLevelsEvent>(); auto plEvent = currentState().get<RoomPowerLevelsEvent>();
if (!plEvent) {
return true;
}
auto pl = plEvent->powerLevelForEvent(eventType); auto pl = plEvent->powerLevelForEvent(eventType);
auto currentPl = plEvent->powerLevelForUser(localUser()->id()); auto currentPl = plEvent->powerLevelForUser(localUser()->id());
@@ -940,28 +889,19 @@ bool NeoChatRoom::canSendEvent(const QString &eventType) const
bool NeoChatRoom::canSendState(const QString &eventType) const bool NeoChatRoom::canSendState(const QString &eventType) const
{ {
auto plEvent = getCurrentState<RoomPowerLevelsEvent>(); auto plEvent = currentState().get<RoomPowerLevelsEvent>();
if (!plEvent) {
return false;
}
auto pl = plEvent->powerLevelForState(eventType); auto pl = plEvent->powerLevelForState(eventType);
auto currentPl = plEvent->powerLevelForUser(localUser()->id()); 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; return currentPl >= pl;
#endif
} }
bool NeoChatRoom::readMarkerLoaded() const bool NeoChatRoom::readMarkerLoaded() const
{ {
#ifdef QUOTIENT_07
const auto it = findInTimeline(lastFullyReadEventId()); const auto it = findInTimeline(lastFullyReadEventId());
#else
const auto it = findInTimeline(readMarkerEventId());
#endif
return it != historyEdge(); return it != historyEdge();
} }
@@ -972,7 +912,7 @@ bool NeoChatRoom::isInvite() const
bool NeoChatRoom::isUserBanned(const QString &user) const bool NeoChatRoom::isUserBanned(const QString &user) const
{ {
return getCurrentState<RoomMemberEvent>(user)->membership() == MembershipType::Ban; return currentState().get<RoomMemberEvent>(user)->membership() == Membership::Ban;
} }
QString NeoChatRoom::htmlSafeDisplayName() const QString NeoChatRoom::htmlSafeDisplayName() const
@@ -987,7 +927,7 @@ void NeoChatRoom::deleteMessagesByUser(const QString &user, const QString &reaso
QString NeoChatRoom::joinRule() const QString NeoChatRoom::joinRule() const
{ {
return getCurrentState<JoinRulesEvent>()->joinRule(); return currentState().get<JoinRulesEvent>()->joinRule();
} }
void NeoChatRoom::setJoinRule(const QString &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"; qWarning() << "Power level too low to set join rules";
return; return;
} }
#ifdef QUOTIENT_07
setState("m.room.join_rules", "", QJsonObject{{"join_rule", joinRule}}); setState("m.room.join_rules", "", QJsonObject{{"join_rule", joinRule}});
#else
setState<JoinRulesEvent>(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. // 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 QString NeoChatRoom::historyVisibility() const
{ {
#ifdef QUOTIENT_07
return currentState().get("m.room.history_visibility")->contentJson()["history_visibility"_ls].toString(); 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) void NeoChatRoom::setHistoryVisibility(const QString &historyVisibilityRule)
@@ -1020,23 +952,14 @@ void NeoChatRoom::setHistoryVisibility(const QString &historyVisibilityRule)
return; return;
} }
#ifdef QUOTIENT_07
setState("m.room.history_visibility", "", QJsonObject{{"history_visibility", historyVisibilityRule}}); 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. // 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 bool NeoChatRoom::defaultUrlPreviewState() const
{ {
#ifdef QUOTIENT_07
auto urlPreviewsDisabled = currentState().get("org.matrix.room.preview_urls"); 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. // Some rooms will not have this state event set so check for a nullptr return.
if (urlPreviewsDisabled != nullptr) { 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. * 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}}); 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 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"; qWarning() << "Power level too low to set user power levels";
return; return;
} }
#ifdef QUOTIENT_07
if (!isMember(userID)) { 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"; qWarning() << "User is not a member of this room so power level cannot be set";
return; return;
} }
int clampPowerLevel = std::clamp(powerLevel, 0, 100); int clampPowerLevel = std::clamp(powerLevel, 0, 100);
#ifdef QUOTIENT_07
auto powerLevelContent = currentState().get("m.room.power_levels")->contentJson(); auto powerLevelContent = currentState().get("m.room.power_levels")->contentJson();
#else
auto powerLevelContent = getCurrentState<RoomPowerLevelsEvent>()->contentJson();
#endif
auto powerLevelUserOverrides = powerLevelContent["users"].toObject(); auto powerLevelUserOverrides = powerLevelContent["users"].toObject();
if (powerLevelUserOverrides[userID] != clampPowerLevel) { if (powerLevelUserOverrides[userID] != clampPowerLevel) {
powerLevelUserOverrides[userID] = clampPowerLevel; powerLevelUserOverrides[userID] = clampPowerLevel;
powerLevelContent["users"] = powerLevelUserOverrides; powerLevelContent["users"] = powerLevelUserOverrides;
#ifdef QUOTIENT_07
setState("m.room.power_levels", "", powerLevelContent); setState("m.room.power_levels", "", powerLevelContent);
#else
setState<RoomPowerLevelsEvent>(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 int NeoChatRoom::powerLevel(const QString &eventName, const bool &isStateEvent) const
{ {
#ifdef QUOTIENT_07
const auto powerLevelEvent = currentState().get<RoomPowerLevelsEvent>(); const auto powerLevelEvent = currentState().get<RoomPowerLevelsEvent>();
#else
const auto powerLevelEvent = getCurrentState<RoomPowerLevelsEvent>();
#endif
if (eventName == "ban") { if (eventName == "ban") {
return powerLevelEvent->ban(); return powerLevelEvent->ban();
} else if (eventName == "kick") { } 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) void NeoChatRoom::setPowerLevel(const QString &eventName, const int &newPowerLevel, const bool &isStateEvent)
{ {
#ifdef QUOTIENT_07
auto powerLevelContent = currentState().get("m.room.power_levels")->contentJson(); auto powerLevelContent = currentState().get("m.room.power_levels")->contentJson();
#else
auto powerLevelContent = getCurrentState<RoomPowerLevelsEvent>()->contentJson();
#endif
int clampPowerLevel = std::clamp(newPowerLevel, 0, 100); int clampPowerLevel = std::clamp(newPowerLevel, 0, 100);
int powerLevel = 0; 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); setState("m.room.power_levels", "", powerLevelContent);
#else
setState<RoomPowerLevelsEvent>(QJsonObject{{"type", "m.room.power_levels"}, {"state_key", ""}, {"content", powerLevelContent}});
#endif
} }
int NeoChatRoom::defaultUserPowerLevel() const int NeoChatRoom::defaultUserPowerLevel() const
@@ -1450,11 +1344,7 @@ bool NeoChatRoom::isSpace()
return false; return false;
} }
#ifdef QUOTIENT_07
return creationEvent->roomType() == RoomType::Space; return creationEvent->roomType() == RoomType::Space;
#else
return false;
#endif
} }
PushNotificationState::State NeoChatRoom::pushNotificationState() const PushNotificationState::State NeoChatRoom::pushNotificationState() const
@@ -1781,15 +1671,9 @@ void NeoChatRoom::setSavedText(const QString &savedText)
bool NeoChatRoom::canEncryptRoom() const bool NeoChatRoom::canEncryptRoom() const
{ {
#ifdef QUOTIENT_07
#ifdef Quotient_E2EE_ENABLED
return !usesEncryption() && canSendState("m.room.encryption"); return !usesEncryption() && canSendState("m.room.encryption");
#endif
#endif
return false;
} }
#ifdef QUOTIENT_07
PollHandler *NeoChatRoom::poll(const QString &eventId) PollHandler *NeoChatRoom::poll(const QString &eventId)
{ {
if (!m_polls.contains(eventId)) { if (!m_polls.contains(eventId)) {
@@ -1800,7 +1684,6 @@ PollHandler *NeoChatRoom::poll(const QString &eventId)
} }
return m_polls[eventId]; return m_polls[eventId];
} }
#endif
bool NeoChatRoom::downloadTempFile(const QString &eventId) bool NeoChatRoom::downloadTempFile(const QString &eventId)
{ {

View File

@@ -735,7 +735,6 @@ public:
*/ */
void setSavedText(const QString &savedText); void setSavedText(const QString &savedText);
#ifdef QUOTIENT_07
/** /**
* @brief Get a PollHandler object for the given event Id. * @brief Get a PollHandler object for the given event Id.
* *
@@ -747,7 +746,6 @@ public:
* @sa PollHandler * @sa PollHandler
*/ */
Q_INVOKABLE PollHandler *poll(const QString &eventId); Q_INVOKABLE PollHandler *poll(const QString &eventId);
#endif
private: private:
QSet<const Quotient::RoomEvent *> highlights; QSet<const Quotient::RoomEvent *> highlights;
@@ -775,9 +773,7 @@ private:
QVector<Mention> m_mentions; QVector<Mention> m_mentions;
QVector<Mention> m_editMentions; QVector<Mention> m_editMentions;
QString m_savedText; QString m_savedText;
#ifdef QUOTIENT_07
QCache<QString, PollHandler> m_polls; QCache<QString, PollHandler> m_polls;
#endif
private Q_SLOTS: private Q_SLOTS:
void countChanged(); void countChanged();

View File

@@ -11,11 +11,7 @@
#include <KNotification> #include <KNotification>
#include <KNotificationReplyAction> #include <KNotificationReplyAction>
#ifdef QUOTIENT_07
#include <accountregistry.h> #include <accountregistry.h>
#else
#include "neochataccountregistry.h"
#endif
#include <connection.h> #include <connection.h>
#include <csapi/pushrules.h> #include <csapi/pushrules.h>
@@ -72,11 +68,7 @@ void NotificationsManager::postNotification(NeoChatRoom *room,
connect(notification, &KNotification::defaultActivated, this, [=]() { connect(notification, &KNotification::defaultActivated, this, [=]() {
WindowController::instance().showAndRaiseWindow(notification->xdgActivationToken()); WindowController::instance().showAndRaiseWindow(notification->xdgActivationToken());
if (room->localUser()->id() != Controller::instance().activeConnection()->userId()) { if (room->localUser()->id() != Controller::instance().activeConnection()->userId()) {
#ifdef QUOTIENT_07
Controller::instance().setActiveConnection(Accounts.get(room->localUser()->id())); Controller::instance().setActiveConnection(Accounts.get(room->localUser()->id()));
#else
Controller::instance().setActiveConnection(AccountRegistry::instance().get(room->localUser()->id()));
#endif
} }
RoomManager::instance().enterRoom(room); RoomManager::instance().enterRoom(room);
}); });

View File

@@ -34,7 +34,7 @@ void PollHandler::setRoom(NeoChatRoom *room)
connect(room, &NeoChatRoom::aboutToAddNewMessages, this, [this](Quotient::RoomEventsRange events) { connect(room, &NeoChatRoom::aboutToAddNewMessages, this, [this](Quotient::RoomEventsRange events) {
for (const auto &event : events) { for (const auto &event : events) {
if (event->is<PollEndEvent>()) { if (event->is<PollEndEvent>()) {
auto pl = m_room->getCurrentState<RoomPowerLevelsEvent>(); auto pl = m_room->currentState().get<RoomPowerLevelsEvent>();
auto userPl = pl->powerLevelForUser(event->senderId()); auto userPl = pl->powerLevelForUser(event->senderId());
if (event->senderId() == (*m_room->findInTimeline(m_pollStartEventId))->senderId() || userPl >= pl->redact()) { if (event->senderId() == (*m_room->findInTimeline(m_pollStartEventId))->senderId() || userPl >= pl->redact()) {
m_hasEnded = true; m_hasEnded = true;
@@ -75,7 +75,7 @@ void PollHandler::checkLoadRelations()
connect(job, &BaseJob::success, this, [this, job]() { connect(job, &BaseJob::success, this, [this, job]() {
for (const auto &event : job->chunk()) { for (const auto &event : job->chunk()) {
if (event->is<PollEndEvent>()) { if (event->is<PollEndEvent>()) {
auto pl = m_room->getCurrentState<RoomPowerLevelsEvent>(); auto pl = m_room->currentState().get<RoomPowerLevelsEvent>();
auto userPl = pl->powerLevelForUser(event->senderId()); auto userPl = pl->powerLevelForUser(event->senderId());
if (event->senderId() == (*m_room->findInTimeline(m_pollStartEventId))->senderId() || userPl >= pl->redact()) { if (event->senderId() == (*m_room->findInTimeline(m_pollStartEventId))->senderId() || userPl >= pl->redact()) {
m_hasEnded = true; m_hasEnded = true;

View File

@@ -35,7 +35,7 @@ QQC2.Dialog {
text: i18n("Sign out") text: i18n("Sign out")
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole
onClicked: { onClicked: {
Controller.logout(Controller.activeConnection, true); Controller.activeConnection.logout();
root.close(); root.close();
} }
} }

View File

@@ -40,12 +40,12 @@ Labs.MenuBar {
Labs.MenuItem { Labs.MenuItem {
text: i18nc("menu", "New Private Chat…") 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}) onTriggered: pushReplaceLayer("qrc:/StartChatPage.qml", {connection: Controller.activeConnection})
} }
Labs.MenuItem { Labs.MenuItem {
text: i18nc("menu", "New Group…") 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 shortcut: StandardKey.New
onTriggered: { onTriggered: {
const dialog = createRoomDialog.createObject(root.overlay) const dialog = createRoomDialog.createObject(root.overlay)

View File

@@ -76,7 +76,7 @@ QQC2.ToolBar {
visible: switchUserButton.checked visible: switchUserButton.checked
onVisibleChanged: if (visible) accounts.forceActiveFocus() onVisibleChanged: if (visible) accounts.forceActiveFocus()
clip: true clip: true
model: AccountRegistry model: Accounts
keyNavigationEnabled: false keyNavigationEnabled: false
Keys.onDownPressed: { Keys.onDownPressed: {

View File

@@ -0,0 +1,502 @@
// SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
// 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
}
}

View File

@@ -27,7 +27,7 @@ Kirigami.ScrollablePage {
} }
Repeater { Repeater {
model: AccountRegistry model: Accounts
delegate: MobileForm.AbstractFormDelegate { delegate: MobileForm.AbstractFormDelegate {
Layout.fillWidth: true Layout.fillWidth: true
onClicked: pageSettingStack.pushDialogLayer("qrc:/AccountEditorPage.qml", { onClicked: pageSettingStack.pushDialogLayer("qrc:/AccountEditorPage.qml", {

View File

@@ -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 { Connections {
target: Controller target: Controller
function onInitiated() { function onActiveConnectionChanged() {
if (Controller.accountCount === 0) { if (!Controller.activeConnection) {
pageStack.replace("qrc:/WelcomePage.qml", {}); pageStack.replace("qrc:/imports/NeoChat/Page/WelcomePage.qml", {});
} else if (!roomListLoaded) { } else if (!roomListLoaded) {
pageStack.replace(roomListComponent, { pageStack.replace(roomListComponent, {
activeConnection: Controller.activeConnection activeConnection: Controller.activeConnection

View File

@@ -11,9 +11,7 @@
#include <QQuickTextDocument> #include <QQuickTextDocument>
#include <QStandardPaths> #include <QStandardPaths>
#include <csapi/joining.h> #include <csapi/joining.h>
#ifdef QUOTIENT_07
#include <csapi/knocking.h> #include <csapi/knocking.h>
#endif
#include <qt_connection_util.h> #include <qt_connection_util.h>
#include <user.h> #include <user.h>
@@ -158,16 +156,12 @@ UriResolveResult RoomManager::visitUser(User *user, const QString &action)
{ {
if (action == "mention" || action.isEmpty()) { if (action == "mention" || action.isEmpty()) {
// send it has QVariantMap because the properties in the // send it has QVariantMap because the properties in the
#ifdef QUOTIENT_07
user->load(); user->load();
#endif
Q_EMIT showUserDetail(user); Q_EMIT showUserDetail(user);
} else if (action == "_interactive") { } else if (action == "_interactive") {
user->requestDirectChat(); user->requestDirectChat();
} else if (action == "chat") { } else if (action == "chat") {
#ifdef QUOTIENT_07
user->load(); user->load();
#endif
Q_EMIT askDirectChatConfirmation(user); Q_EMIT askDirectChatConfirmation(user);
} else { } else {
return Quotient::IncorrectAction; 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) void RoomManager::knockRoom(Quotient::Connection *account, const QString &roomAliasOrId, const QString &reason, const QStringList &viaServers)
{ {
auto *const job = account->callApi<KnockRoomJob>(roomAliasOrId, viaServers, reason); auto *const job = account->callApi<KnockRoomJob>(roomAliasOrId, viaServers, reason);
@@ -229,7 +221,6 @@ void RoomManager::knockRoom(Quotient::Connection *account, const QString &roomAl
} }
}); });
} }
#endif
bool RoomManager::visitNonMatrix(const QUrl &url) bool RoomManager::visitNonMatrix(const QUrl &url)
{ {

View File

@@ -59,10 +59,7 @@ public:
// Overrided methods from UriResolverBase // Overrided methods from UriResolverBase
UriResolveResult visitUser(User *user, const QString &action) override; UriResolveResult visitUser(User *user, const QString &action) override;
void joinRoom(Quotient::Connection *account, const QString &roomAliasOrId, const QStringList &viaServers) 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); 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 void visitRoom(Quotient::Room *room, const QString &eventId) override;
Q_INVOKABLE bool visitNonMatrix(const QUrl &url) override; Q_INVOKABLE bool visitNonMatrix(const QUrl &url) override;

View File

@@ -3,9 +3,7 @@
#include "spacehierarchycache.h" #include "spacehierarchycache.h"
#ifdef QUOTIENT_07
#include <csapi/space_hierarchy.h> #include <csapi/space_hierarchy.h>
#endif
#include <qt_connection_util.h> #include <qt_connection_util.h>
#include "controller.h" #include "controller.h"
@@ -26,7 +24,6 @@ SpaceHierarchyCache::SpaceHierarchyCache(QObject *parent)
void SpaceHierarchyCache::cacheSpaceHierarchy() void SpaceHierarchyCache::cacheSpaceHierarchy()
{ {
#ifdef QUOTIENT_07
auto connection = Controller::instance().activeConnection(); auto connection = Controller::instance().activeConnection();
if (!connection) { if (!connection) {
return; return;
@@ -45,7 +42,6 @@ void SpaceHierarchyCache::cacheSpaceHierarchy()
}); });
} }
} }
#endif
} }
void SpaceHierarchyCache::populateSpaceHierarchy(const QString &spaceId) void SpaceHierarchyCache::populateSpaceHierarchy(const QString &spaceId)
@@ -54,7 +50,6 @@ void SpaceHierarchyCache::populateSpaceHierarchy(const QString &spaceId)
if (!connection) { if (!connection) {
return; return;
} }
#ifdef QUOTIENT_07
GetSpaceHierarchyJob *job = connection->callApi<GetSpaceHierarchyJob>(spaceId); GetSpaceHierarchyJob *job = connection->callApi<GetSpaceHierarchyJob>(spaceId);
connect(job, &BaseJob::success, this, [this, job, spaceId]() { connect(job, &BaseJob::success, this, [this, job, spaceId]() {
@@ -69,7 +64,6 @@ void SpaceHierarchyCache::populateSpaceHierarchy(const QString &spaceId)
m_spaceHierarchy.insert(spaceId, roomList); m_spaceHierarchy.insert(spaceId, roomList);
Q_EMIT spaceHierarchyChanged(); Q_EMIT spaceHierarchyChanged();
}); });
#endif
} }
void SpaceHierarchyCache::addSpaceToHierarchy(Quotient::Room *room) void SpaceHierarchyCache::addSpaceToHierarchy(Quotient::Room *room)

View File

@@ -1,35 +0,0 @@
// SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
// 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<QString>("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
}

View File

@@ -1,43 +0,0 @@
// SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include <events/eventcontent.h>
#include <events/roomevent.h>
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)
}