Compare commits
3 Commits
v25.04.2
...
work/yeetk
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce3915e22a | ||
|
|
741f6ea659 | ||
|
|
310e9b7ba3 |
@@ -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()
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 ¤tPassword, const QString &newPassword)
|
void Controller::changePassword(Connection *connection, const QString ¤tPassword, 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 ¤tPa
|
|||||||
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
|
||||||
|
|||||||
@@ -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 ¤tPassword, const QString &newPassword);
|
Q_INVOKABLE void changePassword(Quotient::Connection *connection, const QString ¤tPassword, 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:
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
30
src/main.cpp
30
src/main.cpp
@@ -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());
|
||||||
|
|||||||
@@ -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."));
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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]() {
|
||||||
|
|||||||
@@ -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 {};
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
502
src/qml/Page/RoomListPage.qml
Normal file
502
src/qml/Page/RoomListPage.qml
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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", {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user