Compare commits

..

1 Commits

Author SHA1 Message Date
James Graham
db79d35c65 Use QQC2 Dialog with updated style instead of Kirigami.Dialog 2025-05-17 14:14:48 +01:00
139 changed files with 26958 additions and 37852 deletions

View File

@@ -164,7 +164,6 @@ endif()
if(ANDROID)
find_package(Sqlite3)
set(BUILD_TESTING FALSE)
endif()
ki18n_install(po)
@@ -179,7 +178,7 @@ add_definitions(-DQT_NO_FOREACH)
add_subdirectory(src)
if (BUILD_TESTING)
find_package(Qt6 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test HttpServer)
find_package(Qt6 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test)
add_subdirectory(autotests)
# add_subdirectory(appiumtests)
if (NOT ANDROID)

View File

@@ -3,10 +3,6 @@
enable_testing()
add_library(neochat_server STATIC server.cpp)
target_link_libraries(neochat_server PUBLIC Qt::HttpServer QuotientQt6)
add_definitions(-DDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" )
ecm_add_test(
@@ -89,6 +85,6 @@ ecm_add_test(
ecm_add_test(
actionstest.cpp
LINK_LIBRARIES neochat Qt::Test neochat_server
LINK_LIBRARIES neochat Qt::Test
TEST_NAME actionstest
)

View File

@@ -6,11 +6,9 @@
#include <QSignalSpy>
#include <QVariantList>
#include "accountmanager.h"
#include "chatbarcache.h"
#include "models/actionsmodel.h"
#include "server.h"
#include "testutils.h"
using namespace Quotient;
@@ -23,12 +21,10 @@ class ActionsTest : public QObject
private:
Connection *connection = nullptr;
NeoChatRoom *room = nullptr;
TestUtils::TestRoom *room = nullptr;
void expectMessage(const QString &actionName, const QString &args, MessageType::Type type, const QString &message);
Server server;
private Q_SLOTS:
void initTestCase();
void testActions();
@@ -38,23 +34,8 @@ private Q_SLOTS:
void ActionsTest::initTestCase()
{
Connection::setRoomType<NeoChatRoom>();
server.start();
KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));
auto accountManager = new AccountManager(true);
QSignalSpy spy(accountManager, &AccountManager::connectionAdded);
connection = accountManager->accounts()->front();
auto roomId = server.createRoom(u"@user:localhost:1234"_s);
server.inviteUser(roomId, u"@invited:example.com"_s);
server.banUser(roomId, u"@banned:example.com"_s);
server.joinUser(roomId, u"@example:example.com"_s);
QSignalSpy syncSpy(connection, &Connection::syncDone);
// We need to wait for two syncs, as the next one won't have the changes yet
QVERIFY(syncSpy.wait());
QVERIFY(syncSpy.wait());
room = dynamic_cast<NeoChatRoom *>(connection->room(roomId));
QVERIFY(room);
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-min-sync.json"));
}
void ActionsTest::testActions_data()
@@ -109,7 +90,7 @@ static ActionsModel::Action findAction(const QString &name)
void ActionsTest::expectMessage(const QString &actionName, const QString &args, MessageType::Type type, const QString &message)
{
auto action = findAction(actionName);
QSignalSpy spy(room, &NeoChatRoom::showMessage);
QSignalSpy spy(room, &TestUtils::TestRoom::showMessage);
auto result = action.handle(args, room, nullptr);
auto expected = QVariantList {type, message};
auto signal = spy.takeFirst();
@@ -125,26 +106,14 @@ void ActionsTest::testInvite()
QCOMPARE(room->memberState(u"@banned:example.com"_s), Membership::Ban);
expectMessage(u"invite"_s, connection->userId(), MessageType::Positive, u"You are already in this room."_s);
QCOMPARE(room->memberState(connection->userId()), Membership::Join);
expectMessage(u"invite"_s, u"@example:example.com"_s, MessageType::Information, u"@example:example.com is already in this room."_s);
QCOMPARE(room->memberState(u"@example:example.com"_s), Membership::Join);
expectMessage(u"invite"_s, u"@example:example.org"_s, MessageType::Information, u"@example:example.org is already in this room."_s);
QCOMPARE(room->memberState(u"@example:example.org"_s), Membership::Join);
QCOMPARE(room->memberState(u"@user:example.com"_s), Membership::Leave);
expectMessage(u"invite"_s, u"@user:example.com"_s, MessageType::Positive, u"@user:example.com was invited into this room."_s);
QSignalSpy spy(room, &NeoChatRoom::changed);
QVERIFY(spy.wait());
auto tries = 0;
while (room->memberState(u"@user:example.com"_s) != Membership::Invite) {
QVERIFY(spy.wait());
tries += 1;
if (tries > 3) {
QVERIFY(false);
}
}
QCOMPARE(room->memberState(u"@user:example.com"_s), Membership::Invite);
//TODO mock server, wait for invite state to change
//TODO QCOMPARE(room->memberState(u"@user:example.com"_s), Membership::Invite);
}
QTEST_MAIN(ActionsTest)

View File

@@ -1,20 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDNTCCAh2gAwIBAgIUXbyWfTfcvVLrVB1qx36pW/7IkwMwDQYJKoZIhvcNAQEL
BQAwQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE
CgwTRGVmYXVsdCBDb21wYW55IEx0ZDAgFw0yNDEyMjQxNTAxMDNaGA8yNTcyMDcy
NDE1MDEwM1owQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEc
MBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAKlxZ540TQ1uUDAR7ZJ9ue0PzcD2dPmblIIddyekvZS59V7X
drhamclXpHE2EelR87Sexst0BaHH/jmrHwxCtwbeXHZ8ueJHkGHJ5DLZCCiwfG+Q
gml7wlSXxXz37vie2tdlZh2yJSM8yvLAYceHb2zOskaGvul7ZITIS0JrPc3o6VZk
+MYGkYtA2JfUsv3jH4oQbxOf7RXqhWNAXbB+3hlwRBwMIdyoBNK6YS9QSrTeS9jj
UqgO5QmaQZOVvpaPf1Y/rHHLd2Qa6+a/cCJ1sr2biagb75AihpQFsK/oy6D1PP70
zTe7hPWn/efEpmtCV7CQ8ti4cRu0Kjy0T8grtCsCAwEAAaMhMB8wHQYDVR0OBBYE
FIFlylzwADNLfgTDNkhFeFelaEDxMA0GCSqGSIb3DQEBCwUAA4IBAQBQ2rw4GLIU
v+GY7Qru9LttkrQPd2bZXKxDMd/jT+wjmMVtqS4MAsCuDYwaYLjU1aWyqy0mN+lY
A17kD0VjBNBy45sYqkZveY0ks8mCScBemtrIDmjz2tiueecBIEASwEPBOZgv5/MV
cz864FiChF+2r8Zl8bhycGy9DEpRjzYKvIQWSDHQ3zpuh3iBnjfoieLHWX2kKCpk
ouS3V6485rHNCWsZT5IcCwfBFQkOuWRJpIazpz4AfwZh1TK9+bgiKA5EyZjSNrKw
xGQSpMSTRQMB0/FOCL/AixhN9unVFUViqUcdtSfoHE1VyBHv9kDT/cYms/Xl4B0t
/ZSQJ/D/Km1+
-----END CERTIFICATE-----

View File

@@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCpcWeeNE0NblAw
Ee2SfbntD83A9nT5m5SCHXcnpL2UufVe13a4WpnJV6RxNhHpUfO0nsbLdAWhx/45
qx8MQrcG3lx2fLniR5BhyeQy2QgosHxvkIJpe8JUl8V89+74ntrXZWYdsiUjPMry
wGHHh29szrJGhr7pe2SEyEtCaz3N6OlWZPjGBpGLQNiX1LL94x+KEG8Tn+0V6oVj
QF2wft4ZcEQcDCHcqATSumEvUEq03kvY41KoDuUJmkGTlb6Wj39WP6xxy3dkGuvm
v3AidbK9m4moG++QIoaUBbCv6Mug9Tz+9M03u4T1p/3nxKZrQlewkPLYuHEbtCo8
tE/IK7QrAgMBAAECggEAH9qmeKrra2F4KLlOGNKS//qPGz4Z+ozhi95/NpA1Zb7Z
3pUSCBFcROo5i2D3WA4kiymoRLpQjrv60puVcCggoWVvK4VCKsR6Y6/hOx/q9T9M
fWrE4ZC3FVEc+uPfZJT0nja9TkrdyXSV0LITD8Ap1eI7yJ9vR5R/bqj64QcpLMrU
QeoQIy1oTMR+qdjj33duyRwBZU3Yf8FRB2iW6OILZ8hzFo1jngec7dph9a1RK4e0
mEPdc9ywsKlDM7P0Y7zdmjar5XtQn87GiwNhz23f1fzCC2axLtOW0Xm4e4Qumehb
WrIi6Vfq8IWMglU7QrBJ7iR0Ls+XoKA5GxomV2IJZQKBgQDoIkOl5YGPQ3iGR+WK
e5/2Ml4G/uURzYiOlzSsyfoPXyO4EI2BJd5HkH+EvfgRx4xKkxUZRJdzR7llYPl8
BFYcFitvhO8SbD0mNAB5YW7f+3v1pgEN2umzoKd389Zx5WqTZ7YB1VG5RN/Q1JJL
2JM0Xgamq2vNtx3roRPxDBeW7QKBgQC63R/bmACJbgIzfaVBX4Zie3NQG0/Hf+gF
LnBwUmQDZOR7MY+kSiIUVMn3NuZRiCSCFBVwApruyK8r535JCibTVm5PWjvhFddY
LgaPOCKGlm9TLScjoH1pErYgG3uJ4nXeRfXhg4mco6EkrC7RzQywrd0VDoqpuc1Y
EKfEsYk8dwKBgE+mSh3nNOBKX1V73+f3aTiZqaeu2DyWkG+UtE9BclrJ40Cp9VPG
AZH+o7KRWEgJdzqzYv7riSfWCWgesRv7hOxYMwktzLY+i3DLUQpVAy05ZhwwnJX7
ckrfKfc/pGoqNLplUI8qecMfPciy14vMwR2r0Y5orTHFzi9mcqg35PQ1AoGAW2LX
OLq+0HdHhk0Va8I+450CSRQCUUvhed87SANTPEG0Z/dWC3/h6NWKrGdh/k+5oxAV
Z+EuSkdFPBCLt0bKtCKZ8h7sF+lplotz08kdQXsC2MfFU2wiySdIgK1QHp/tCxZl
6LM+sqdnoJrAjwRcB3AQJkMlV1ox7ba/hbdZqYMCgYBS6+JUXSSASpm5ZHd32a8m
xwryEZ7H6Hek6lvMHdxmwoKat5dCavxw64nrtyeeGZpg1W3zLLyamF9x/8kMyr6y
KKvtBfJ5sCvAbt80o9Pbs6R3yDB3AKiD3s3PQK7lol1nhE/8IbsF2r8JEQVcYd/k
oBzkl7MrMyLhhaCqSxwqQQ==
-----END PRIVATE KEY-----

View File

@@ -1,235 +0,0 @@
// SPDX-FileCopyrightText: 2025 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include "server.h"
#include <QFile>
#include <QHttpServer>
#include <QHttpServerResponder>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkReply>
#include <QSslCertificate>
#include <QSslKey>
#include <QSslServer>
#include <QUuid>
#include <Quotient/networkaccessmanager.h>
using namespace Qt::Literals::StringLiterals;
QString generateEventId()
{
return u"$"_s + QString::fromLatin1(QCryptographicHash::hash(QUuid::createUuid().toString().toLatin1(), QCryptographicHash::Sha1).toBase64());
}
QString generateRoomId()
{
return u"!%1:localhost:1234"_s
.arg(QString::fromLatin1(QCryptographicHash::hash(QUuid::createUuid().toString().toLatin1(), QCryptographicHash::Sha1).toBase64()))
.replace(u'/', QChar());
}
Server::Server()
{
}
void Server::start()
{
QObject::connect(Quotient::NetworkAccessManager::instance(),
&QNetworkAccessManager::sslErrors,
Quotient::NetworkAccessManager::instance(),
[](QNetworkReply *reply) {
reply->ignoreSslErrors();
});
m_server.route(u"/.well-known/matrix/client"_s, QHttpServerRequest::Method::Get, [](QHttpServerResponder &responder) {
responder.write(QJsonDocument(QJsonObject{
{u"m.homeserver"_s, QJsonObject{{u"base_url"_s, u"https://localhost:1234"_s}}},
}),
QHttpServerResponder::StatusCode::Ok);
});
m_server.route(u"/_matrix/client/versions"_s, QHttpServerRequest::Method::Get, [](QHttpServerResponder &responder) {
responder.write(QJsonDocument(QJsonObject{
{u"versions"_s,
QJsonArray{
u"v1.0"_s,
u"v1.1"_s,
u"v1.2"_s,
u"v1.3"_s,
u"v1.4"_s,
u"v1.5"_s,
u"v1.6"_s,
u"v1.7"_s,
u"v1.8"_s,
u"v1.9"_s,
u"v1.10"_s,
u"v1.11"_s,
u"v1.12"_s,
u"v1.13"_s,
}},
}),
QHttpServerResponder::StatusCode::Ok);
});
m_server.route(u"/_matrix/client/v3/capabilities"_s, QHttpServerRequest::Method::Get, [](QHttpServerResponder &responder) {
responder.write(
QJsonDocument(QJsonObject{{u"capabilities"_s,
QJsonObject{
{u"m.room_versions"_s, QJsonObject{{u"m.available"_s, QJsonObject{{u"1"_s, u"stable"_s}}}, {u"default"_s, u"1"_s}}},
}}}),
QHttpServerResponder::StatusCode::Ok);
});
m_server.route(u"/_matrix/client/v3/account/whoami"_s, QHttpServerRequest::Method::Get, [](QHttpServerResponder &responder) {
responder.write(QJsonDocument(QJsonObject{
{u"device_id"_s, u"device_id_1234"_s},
{u"user_id"_s, u"@user:localhost:1234"_s},
}),
QHttpServerResponder::StatusCode::Ok);
});
m_server.route(u"/_matrix/client/v3/login"_s, QHttpServerRequest::Method::Post, [](QHttpServerResponder &responder) {
// TODO
// if data["identifier"]["user"] != "user" or data["password"] != "1234":
// abort(403)
responder.write(QJsonDocument(QJsonObject{
{u"access_token"_s, u"token_login"_s},
{u"device_id"_s, u"device_1234"_s},
{u"user_id"_s, u"@user:localhost:1234"_s},
}),
QHttpServerResponder::StatusCode::Ok);
});
m_server.route(u"/_matrix/client/v3/login"_s, QHttpServerRequest::Method::Get, [](QHttpServerResponder &responder) {
responder.write(QJsonDocument(QJsonObject{
{u"flows"_s, QJsonArray{QJsonObject{{u"type"_s, u"m.login.password"_s}}}},
}),
QHttpServerResponder::StatusCode::Ok);
});
m_server.route(u"/_matrix/client/v3/rooms/<arg>/invite"_s,
QHttpServerRequest::Method::Post,
[this](const QString &roomId, QHttpServerResponder &responder, const QHttpServerRequest &request) {
m_invitedUsers[roomId] += QJsonDocument::fromJson(request.body()).object()[u"user_id"_s].toString();
responder.write(QJsonDocument(QJsonObject{}), QHttpServerResponder::StatusCode::Ok);
});
m_server.route(u"/_matrix/client/r0/sync"_s, QHttpServerRequest::Method::Get, [this](QHttpServerResponder &responder) {
QMap<QString, QJsonArray> stateEvents;
for (const auto &[roomId, matrixId] : m_roomsToCreate) {
stateEvents[roomId] += QJsonObject{
{u"content"_s, QJsonObject{{u"room_version"_s, u"11"_s}}},
{u"event_id"_s, generateEventId()},
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
{u"room_id"_s, roomId},
{u"sender"_s, matrixId},
{u"state_key"_s, QString()},
{u"type"_s, u"m.room.create"_s},
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
};
stateEvents[roomId] += QJsonObject{
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"join"_s}}},
{u"event_id"_s, generateEventId()},
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
{u"room_id"_s, roomId},
{u"sender"_s, matrixId},
{u"state_key"_s, matrixId},
{u"type"_s, u"m.room.member"_s},
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
};
}
m_roomsToCreate.clear();
for (const auto &roomId : m_invitedUsers.keys()) {
const auto &values = m_invitedUsers[roomId];
for (const auto &value : values) {
stateEvents[roomId] += QJsonObject{
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"invite"_s}}},
{u"event_id"_s, generateEventId()},
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
{u"room_id"_s, roomId},
{u"sender"_s, u"@user:localhost:1234"_s},
{u"state_key"_s, value},
{u"type"_s, u"m.room.member"_s},
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
};
}
}
m_invitedUsers.clear();
for (const auto &roomId : m_bannedUsers.keys()) {
const auto &values = m_bannedUsers[roomId];
for (const auto &value : values) {
stateEvents[roomId] += QJsonObject{
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"ban"_s}}},
{u"event_id"_s, generateEventId()},
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
{u"room_id"_s, roomId},
{u"sender"_s, u"@user:localhost:1234"_s},
{u"state_key"_s, value},
{u"type"_s, u"m.room.member"_s},
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
};
}
}
m_bannedUsers.clear();
for (const auto &roomId : m_joinedUsers.keys()) {
const auto &values = m_joinedUsers[roomId];
for (const auto &value : values) {
stateEvents[roomId] += QJsonObject{
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"join"_s}}},
{u"event_id"_s, generateEventId()},
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
{u"room_id"_s, roomId},
{u"sender"_s, u"@user:localhost:1234"_s},
{u"state_key"_s, value},
{u"type"_s, u"m.room.member"_s},
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
};
}
}
m_joinedUsers.clear();
QJsonObject rooms;
for (const auto &roomId : stateEvents.keys()) {
rooms[roomId] = QJsonObject{{u"state"_s, QJsonObject{{u"events"_s, stateEvents[roomId]}}}};
}
responder.write(QJsonDocument(QJsonObject{{u"rooms"_s, QJsonObject{{u"join"_s, rooms}}}}), QHttpServerResponder::StatusCode::Ok);
});
QSslConfiguration config;
QFile key(QStringLiteral(DATA_DIR) + u"/localhost.key"_s);
key.open(QFile::ReadOnly);
config.setPrivateKey(QSslKey(&key, QSsl::Rsa));
config.setLocalCertificate(QSslCertificate::fromPath(QStringLiteral(DATA_DIR) + u"/localhost.crt"_s).front());
m_sslServer.setSslConfiguration(config);
if (!m_sslServer.listen(QHostAddress::LocalHost, 1234) || !m_server.bind(&m_sslServer)) {
qFatal() << "Server failed to listen on a port.";
return;
} else {
qWarning() << "Server listening";
}
}
QString Server::createRoom(const QString &matrixId)
{
auto roomId = generateRoomId();
m_roomsToCreate += {roomId, matrixId};
return roomId;
}
void Server::inviteUser(const QString &roomId, const QString &matrixId)
{
m_invitedUsers[roomId] += matrixId;
}
void Server::banUser(const QString &roomId, const QString &matrixId)
{
m_bannedUsers[roomId] += matrixId;
}
void Server::joinUser(const QString &roomId, const QString &matrixId)
{
m_joinedUsers[roomId] += matrixId;
}

View File

@@ -1,33 +0,0 @@
// SPDX-FileCopyrightText: 2025 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include <QHttpServer>
#include <QSslServer>
class Server
{
public:
Server();
void start();
/**
* Create a room and place the user with id matrixId in it.
* Returns the room's id
*/
QString createRoom(const QString &matrixId);
void inviteUser(const QString &roomId, const QString &matrixId);
void banUser(const QString &roomId, const QString &matrixId);
void joinUser(const QString &roomId, const QString &matrixId);
private:
QHttpServer m_server;
QSslServer m_sslServer;
QHash<QString, QList<QString>> m_invitedUsers;
QHash<QString, QList<QString>> m_bannedUsers;
QHash<QString, QList<QString>> m_joinedUsers;
QList<std::pair<QString, QString>> m_roomsToCreate;
};

View File

@@ -264,8 +264,6 @@ void TextHandlerTest::sendCustomTags_data()
QTest::newRow("inside code block spoiler") << u"```||apple||```"_s << u"<code>||apple||</code>"_s;
QTest::newRow("outside code block spoiler") << u"||apple|| ```||banana||``` ||pear||"_s
<< u"<span data-mx-spoiler>apple</span> <code>||banana||</code> <span data-mx-spoiler>pear</span>"_s;
QTest::newRow("complex spoiler") << u"Between `formFactor == Horizontal||Vertical` and `location == top||left||bottom||right`"_s
<< u"Between <code>formFactor == Horizontal||Vertical</code> and <code>location == top||left||bottom||right</code>"_s;
// strikethrough
QTest::newRow("incomplete strikethrough") << u"~~test"_s << u"~~test"_s;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,6 @@ endif()
add_subdirectory(libneochat)
add_subdirectory(login)
add_subdirectory(rooms)
add_subdirectory(roominfo)
add_subdirectory(timeline)
add_subdirectory(spaces)
add_subdirectory(chatbar)

View File

@@ -8,6 +8,8 @@ add_library(neochat STATIC
controller.h
roommanager.cpp
roommanager.h
models/userfiltermodel.cpp
models/userfiltermodel.h
models/userdirectorylistmodel.cpp
models/userdirectorylistmodel.h
notificationsmanager.cpp
@@ -47,9 +49,6 @@ if(ANDROID OR WIN32)
set_source_files_properties(qml/ShareActionStub.qml PROPERTIES
QT_QML_SOURCE_TYPENAME ShareAction
)
set_source_files_properties(qml/GlobalMenuStub.qml PROPERTIES
QT_QML_SOURCE_TYPENAME GlobalMenu
)
endif()
ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
@@ -59,8 +58,10 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
qml/AccountMenu.qml
qml/CollapsedRoomDelegate.qml
qml/RoomPage.qml
qml/ExploreRoomsPage.qml
qml/ManualRoomDialog.qml
qml/ExplorerDelegate.qml
qml/InviteUserPage.qml
qml/ImageEditorPage.qml
qml/NeochatMaximizeComponent.qml
qml/TypingPane.qml
@@ -68,6 +69,7 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
qml/AttachmentPane.qml
qml/QuickFormatBar.qml
qml/UserDetailDialog.qml
qml/CreateRoomDialog.qml
qml/OpenFileDialog.qml
qml/KeyVerificationDialog.qml
qml/ConfirmLogoutDialog.qml
@@ -77,14 +79,26 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
qml/EmojiSas.qml
qml/VerificationCanceled.qml
qml/MessageSourceSheet.qml
qml/RoomSearchPage.qml
qml/RoomPinnedMessagesPage.qml
qml/LocationChooser.qml
qml/InvitationView.qml
qml/AvatarTabButton.qml
qml/OsmLocationPlugin.qml
qml/FullScreenMap.qml
qml/LocationsPage.qml
qml/LocationMapItem.qml
qml/RoomDrawer.qml
qml/RoomDrawerPage.qml
qml/DirectChatDrawerHeader.qml
qml/GroupChatDrawerHeader.qml
qml/RoomInformation.qml
qml/RoomMedia.qml
qml/ChooseRoomDialog.qml
qml/RemoveChildDialog.qml
qml/QrCodeMaximizeComponent.qml
qml/NotificationsView.qml
qml/SearchPage.qml
qml/ServerComboBox.qml
qml/UserSearchPage.qml
qml/ManualUserDialog.qml
@@ -104,16 +118,12 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
qml/AvatarNotification.qml
qml/ReasonDialog.qml
qml/NewPollDialog.qml
qml/UserMenu.qml
DEPENDENCIES
QtCore
QtQuick
org.kde.prison
org.kde.prison.scanner
IMPORTS
org.kde.neochat.libneochat
org.kde.neochat.rooms
org.kde.neochat.roominfo
org.kde.neochat.timeline
org.kde.neochat.spaces
org.kde.neochat.settings
@@ -129,10 +139,7 @@ if(NOT ANDROID AND NOT WIN32)
qml/EditMenu.qml
)
else()
qt_target_qml_sources(neochat QML_FILES
qml/ShareActionStub.qml
qml/GlobalMenuStub.qml
)
qt_target_qml_sources(neochat QML_FILES qml/ShareActionStub.qml)
endif()
if(WIN32)
@@ -182,7 +189,7 @@ else()
endif()
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models)
target_link_libraries(neochat PRIVATE Loginplugin Roomsplugin RoomInfoplugin Timelineplugin Spacesplugin Chatbarplugin Settingsplugin Devtoolsplugin)
target_link_libraries(neochat PRIVATE Loginplugin Roomsplugin Timelineplugin Spacesplugin Chatbarplugin Settingsplugin Devtoolsplugin)
target_link_libraries(neochat PUBLIC
LibNeoChat
Timeline

View File

@@ -32,7 +32,6 @@
#include "neochatroom.h"
#include "notificationsmanager.h"
#include "proxycontroller.h"
#include "roommanager.h"
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
#include "trayicon.h"
@@ -99,7 +98,6 @@ Controller::Controller(QObject *parent)
MessageModel::setHiddenFilter(hiddenEventFilter);
RoomListModel::setHiddenFilter(hiddenEventFilter);
RoomTreeModel::setHiddenFilter(hiddenEventFilter);
NeoChatRoom::setHiddenFilter(hiddenEventFilter);
MediaSizeHelper::setMaxSize(NeoChatConfig::mediaMaxWidth(), NeoChatConfig::mediaMaxHeight());
connect(NeoChatConfig::self(), &NeoChatConfig::MediaMaxWidthChanged, this, []() {
@@ -232,7 +230,6 @@ void Controller::initConnection(NeoChatConnection *connection)
m_notificationsManager.handleNotifications(connection);
});
connect(this, &Controller::globalUrlPreviewDefaultChanged, connection, &NeoChatConnection::globalUrlPreviewEnabledChanged);
connect(connection, &NeoChatConnection::roomAboutToBeLeft, &RoomManager::instance(), &RoomManager::roomLeft);
Q_EMIT connectionAdded(connection);
}

View File

@@ -276,7 +276,7 @@ void NotificationsManager::postInviteNotification(NeoChatRoom *rawRoom)
if (inAnyOfOurRooms) {
doPostInviteNotification(room);
} else {
room->forget();
room->leaveRoom();
}
}
});
@@ -330,14 +330,14 @@ void NotificationsManager::doPostInviteNotification(QPointer<NeoChatRoom> room)
if (!room) {
return;
}
room->forget();
RoomManager::instance().leaveRoom(room);
notification->close();
});
connect(rejectAndIgnoreAction, &KNotificationAction::activated, this, [room, notification]() {
if (!room) {
return;
}
room->forget();
RoomManager::instance().leaveRoom(room);
room->connection()->addToIgnoredUsers(room->invitingUserId());
notification->close();
});

View File

@@ -92,7 +92,7 @@ KirigamiComponents.ConvergentContextMenu {
const dialog = Qt.createComponent("org.kde.kirigami", "PromptDialog").createObject(QQC2.Overlay.overlay, {
title: i18nc("@title", "Verification Request Sent"),
subtitle: i18nc("@info:label", "To proceed, accept the verification request on another device."),
standardButtons: Kirigami.Dialog.Ok
standardButtons: QQC2.Dialog.Ok
})
dialog.open();
root.connection.onNewKeyVerificationSession.connect(() => {

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
@@ -11,7 +12,7 @@ import org.kde.kirigamiaddons.delegates as Delegates
import org.kde.neochat
Kirigami.Dialog {
QQC2.Dialog {
id: root
required property NeoChatConnection connection
@@ -23,7 +24,7 @@ Kirigami.Dialog {
topPadding: 0
bottomPadding: 0
standardButtons: Kirigami.Dialog.NoButton
standardButtons: QQC2.Dialog.NoButton
width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
title: i18nc("@title: dialog to switch between logged in accounts", "Switch Account")

View File

@@ -8,33 +8,30 @@ import org.kde.kirigami as Kirigami
import org.kde.neochat
Kirigami.Dialog {
Kirigami.PromptDialog {
id: root
required property var user
width: Math.min(Kirigami.Units.gridUnit * 24, QQC2.ApplicationWindow.window.width)
height: Kirigami.Units.gridUnit * 8
standardButtons: QQC2.Dialog.Close
title: i18nc("@title:dialog", "Start a chat")
subtitle: i18n("Do you want to start a chat with %1?", root.user.displayName)
dialogType: Kirigami.PromptDialog.Warning
contentItem: QQC2.Label {
text: i18n("Do you want to start a chat with %1?", root.user.displayName)
textFormat: Text.PlainText
wrapMode: Text.Wrap
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
onRejected: {
root.close();
}
customFooterActions: [
Kirigami.Action {
footer: QQC2.DialogButtonBox {
standardButtons: QQC2.Dialog.Cancel
QQC2.Button {
text: i18nc("@action:button", "Start Chat")
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole
icon.name: "im-user"
onTriggered: {
onClicked: {
root.user.requestDirectChat();
root.close();
}
}
]
}
}

View File

@@ -48,7 +48,6 @@ Delegates.RoundedItemDelegate {
TapHandler {
acceptedDevices: PointerDevice.TouchScreen
onTapped: root.selected()
onLongPressed: root.contextMenuRequested()
}

View File

@@ -28,7 +28,7 @@ Kirigami.PromptDialog {
text: i18nc("@action:button", "Leave Room")
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole
icon.name: "arrow-left-symbolic"
onClicked: root.room.forget();
onClicked: RoomManager.leaveRoom(root.room)
}
}
}

View File

@@ -8,32 +8,30 @@ import org.kde.kirigami as Kirigami
import org.kde.neochat
Kirigami.Dialog {
Kirigami.PromptDialog {
id: root
required property string url
width: Math.min(Kirigami.Units.gridUnit * 24, QQC2.ApplicationWindow.window.width)
height: Kirigami.Units.gridUnit * 8
leftPadding: Kirigami.Units.largeSpacing
rightPadding: Kirigami.Units.largeSpacing
title: i18nc("@title:dialog", "User Consent")
subtitle: i18nc("@info", "Your homeserver requires you to agree to its terms and conditions before being able to use it. Please click the button below to read them.")
dialogType: Kirigami.PromptDialog.Warning
contentItem: QQC2.Label {
text: i18nc("@info", "Your homeserver requires you to agree to its terms and conditions before being able to use it. Please click the button below to read them.")
wrapMode: Text.WordWrap
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
onRejected: {
root.close();
}
customFooterActions: [
Kirigami.Action {
footer: QQC2.DialogButtonBox {
standardButtons: QQC2.Dialog.Cancel
QQC2.Button {
text: i18nc("@action:button", "Open")
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole
icon.name: "internet-services"
onTriggered: {
onClicked: {
UrlHelper.openUrl(root.url);
root.close();
}
}
]
}
}

View File

@@ -9,36 +9,124 @@ import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.kirigamiaddons.labs.components as Components
import org.kde.neochat.libneochat
import org.kde.neochat
Kirigami.Dialog {
FormCard.FormCardPage {
id: root
property string parentId
property string parentId: ""
property bool isSpace: false
property bool showChildType: false
property bool showCreateChoice: false
required property NeoChatConnection connection
signal addChild(string childId, bool setChildParent, bool canonical)
signal newChild(string childName)
title: i18nc("@title", "Select Existing Room")
implicitWidth: Kirigami.Units.gridUnit * 20
standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel
title: isSpace ? i18nc("@title", "Create a Space") : i18nc("@title", "Create a Room")
onAccepted: root.addChild(chosenRoomDelegate.roomId, existingOfficialCheck.checked, makeCanonicalCheck.checked);
Component.onCompleted: roomNameField.forceActiveFocus()
Component.onCompleted: pickRoomDelegate.forceActiveFocus()
FormCard.FormHeader {
title: root.isSpace ? i18n("New Space Information") : i18n("New Room Information")
}
FormCard.FormCard {
FormCard.FormComboBoxDelegate {
id: roomTypeCombo
property bool isInitialising: true
ColumnLayout {
spacing: Kirigami.Units.largeSpacing
visible: root.showChildType
text: i18n("Select type")
model: ListModel {
id: roomTypeModel
}
textRole: "text"
valueRole: "isSpace"
Component.onCompleted: {
currentIndex = indexOfValue(root.isSpace);
roomTypeModel.append({
"text": i18n("Room"),
"isSpace": false
});
roomTypeModel.append({
"text": i18n("Space"),
"isSpace": true
});
roomTypeCombo.currentIndex = 0;
roomTypeCombo.isInitialising = false;
}
onCurrentValueChanged: {
if (!isInitialising) {
root.isSpace = currentValue;
}
}
}
FormCard.FormDelegateSeparator {
visible: root.showChildType
}
FormCard.FormTextFieldDelegate {
id: roomNameField
label: i18n("Name:")
onAccepted: if (roomNameField.text.length > 0) {
roomTopicField.forceActiveFocus();
}
}
FormCard.FormDelegateSeparator {}
FormCard.FormTextFieldDelegate {
id: roomTopicField
label: i18n("Topic:")
onAccepted: ok.clicked()
}
FormCard.FormDelegateSeparator {}
FormCard.FormCheckDelegate {
id: newOfficialCheck
visible: root.parentId.length > 0
text: i18nc("@option:check As in make the space from which this dialog was created an official parent.", "Make this parent official")
checked: true
}
FormCard.FormDelegateSeparator {
visible: root.parentId.length > 0
}
FormCard.FormButtonDelegate {
id: pickRoomDelegate
visible: !chosenRoomDelegate.visible
text: i18nc("@action:button", "Pick Room")
id: ok
text: root.isSpace ? i18nc("@action:button", "Create Space") : i18nc("@action:button", "Create Room")
enabled: roomNameField.text.length > 0
onClicked: {
let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.libneochat', 'ExploreRoomsPage'), {
if (root.isSpace) {
root.connection.createSpace(roomNameField.text, roomTopicField.text, root.parentId, newOfficialCheck.checked);
} else {
root.connection.createRoom(roomNameField.text, roomTopicField.text, root.parentId, newOfficialCheck.checked);
}
root.newChild(roomNameField.text);
root.closeDialog();
}
}
}
FormCard.FormHeader {
visible: root.showChildType
title: i18n("Select Existing Room")
}
FormCard.FormCard {
visible: root.showChildType
FormCard.FormButtonDelegate {
visible: !chosenRoomDelegate.visible
text: i18nc("@action:button", "Pick room")
onClicked: {
let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage'), {
connection: root.connection
}, {
title: i18nc("@title", "Explore Rooms")
@@ -139,15 +227,13 @@ Kirigami.Dialog {
}
}
FormCard.FormDelegateSeparator {
below: existingOfficialCheck
}
FormCard.FormDelegateSeparator {}
FormCard.FormCheckDelegate {
id: existingOfficialCheck
visible: root.parentId.length > 0
text: i18nc("@option:check As in make the space from which this dialog was created an official parent.", "Make this parent official")
description: enabled ? i18nc("@info:description", "You have the required privilege level in the child to set this state") : i18n("You do not have a high enough privilege level in the child to set this state")
description: enabled ? i18n("You have the required privilege level in the child to set this state") : i18n("You do not have a high enough privilege level in the child to set this state")
checked: enabled
enabled: {
@@ -164,17 +250,26 @@ Kirigami.Dialog {
}
FormCard.FormDelegateSeparator {
above: existingOfficialCheck
below: makeCanonicalCheck
visible: root.parentId.length > 0
}
FormCard.FormCheckDelegate {
id: makeCanonicalCheck
text: i18nc("@option:check The canonical parent is the default one if a room has multiple parent spaces.", "Make this space the canonical parent")
description: i18nc("@info:description", "The canonical parent is the default one if a room has multiple parent spaces.")
checked: enabled
enabled: existingOfficialCheck.enabled
}
FormCard.FormDelegateSeparator {}
FormCard.FormButtonDelegate {
text: i18nc("@action:button", "Ok")
enabled: chosenRoomDelegate.visible
onClicked: {
root.addChild(chosenRoomDelegate.roomId, existingOfficialCheck.checked, makeCanonicalCheck.checked);
root.closeDialog();
}
}
}
}

View File

@@ -8,7 +8,7 @@ import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
import org.kde.neochat.libneochat
import org.kde.neochat
ColumnLayout {
id: root
@@ -18,8 +18,6 @@ ColumnLayout {
*/
required property NeoChatRoom room
signal resolveResource(string idOrUri, string action)
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
spacing: 0
@@ -35,7 +33,7 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter
onClicked: {
root.resolveResource(root.room.directChatRemoteMember.uri, "")
RoomManager.resolveResource(root.room.directChatRemoteMember.uri)
}
contentItem: KirigamiComponents.Avatar {

View File

@@ -8,8 +8,6 @@ import QtPositioning
import org.kde.kirigami as Kirigami
import org.kde.neochat.libneochat
ApplicationWindow {
id: root

View File

@@ -15,7 +15,7 @@ import org.kde.neochat.settings
Labs.MenuBar {
id: root
required property NeoChatConnection connection
property NeoChatConnection connection
Labs.Menu {
title: i18nc("menu", "NeoChat")
@@ -38,31 +38,25 @@ Labs.MenuBar {
title: i18nc("menu", "File")
Labs.MenuItem {
icon.name: "list-add-user"
text: i18nc("@action:inmenu", "Find your Friends")
text: i18nc("menu", "Find your friends")
enabled: pageStack.layers.currentItem.title !== i18n("Find your friends") && AccountRegistry.accountCount > 0
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UserSearchPage'), {
onTriggered: pushReplaceLayer(Qt.createComponent('org.kde.neochat', 'UserSearchPage'), {
connection: root.connection
}, {
title: i18nc("@title", "Find your friends")
})
}
Labs.MenuItem {
icon.name: "system-users-symbolic"
text: i18nc("@action:inmenu", "Create a Room…")
text: i18nc("menu", "New Group…")
enabled: pageStack.layers.currentItem.title !== i18n("Find your friends") && AccountRegistry.accountCount > 0
shortcut: StandardKey.New
onTriggered: {
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'CreateRoomDialog'), {
connection: root.connection
}, {
title: i18nc("@title", "Create a Room")
});
const dialog = createRoomDialog.createObject(root.overlay);
dialog.open();
}
}
Labs.MenuItem {
icon.name: "compass-symbolic"
text: i18nc("@action:inmenu", "Explore Rooms")
text: i18nc("menu", "Browse Chats…")
onTriggered: {
let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage'), {
connection: root.connection
@@ -83,8 +77,7 @@ Labs.MenuBar {
title: i18nc("menu", "View")
Labs.MenuItem {
icon.name: "search-symbolic"
text: i18nc("@action:inmenu opens a UI element called the 'Quick Switcher', which offers a fast keyboard-based interface for switching in between chats.", "Search Rooms")
text: i18nc("menu item that opens a UI element called the 'Quick Switcher', which offers a fast keyboard-based interface for switching in between chats.", "Open Quick Switcher")
onTriggered: quickSwitcher.open()
}
}
@@ -92,7 +85,6 @@ Labs.MenuBar {
title: i18nc("menu", "Window")
Labs.MenuItem {
icon.name: "view-fullscreen-symbolic"
text: root.visibility === Window.FullScreen ? i18nc("menu", "Exit Full Screen") : i18nc("menu", "Enter Full Screen")
onTriggered: root.visibility === Window.FullScreen ? root.showNormal() : root.showFullScreen()
}
@@ -101,12 +93,14 @@ Labs.MenuBar {
title: i18nc("menu", "Help")
Labs.MenuItem {
icon.name: "help-about-symbolic"
text: i18nc("menu", "About Matrix")
onTriggered: UrlHelper.openUrl("https://matrix.org/docs/chat_basics/matrix-for-im/")
}
Labs.MenuItem {
text: i18nc("menu", "About NeoChat")
onTriggered: pageStack.pushDialogLayer(Qt.createComponent("org.kde.kirigamiaddons.formcard", "AboutPage"))
}
Labs.MenuItem {
icon.name: "kde-symbolic"
text: i18nc("menu", "About KDE")
onTriggered: pageStack.pushDialogLayer(Qt.createComponent("org.kde.kirigamiaddons.formcard", "AboutKDEPage"))
}

View File

@@ -1,10 +0,0 @@
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import org.kde.neochat
Item {
required property NeoChatConnection connection
}

View File

@@ -19,11 +19,6 @@ ColumnLayout {
*/
required property NeoChatRoom room
/**
* @brief The canonical alias of the room, if it exists. Otherwise falls back to the first available alias.
*/
readonly property var roomAlias: room.aliases[0]
Layout.fillWidth: true
RowLayout {
@@ -78,8 +73,8 @@ ColumnLayout {
Layout.fillWidth: true
font: Kirigami.Theme.smallFont
textFormat: TextEdit.PlainText
visible: root.room && root.roomAlias
text: root.room && root.roomAlias ? root.roomAlias : ""
visible: root.room && root.room.canonicalAlias
text: root.room && root.room.canonicalAlias ? root.room.canonicalAlias : ""
color: Kirigami.Theme.disabledTextColor
}
}

View File

@@ -108,7 +108,7 @@ ColumnLayout {
icon.name: "dialog-cancel-symbolic"
text: i18nc("@action:button Reject this invite", "Reject Invite")
onClicked: root.currentRoom.forget()
onClicked: RoomManager.leaveRoom(root.currentRoom)
}
}
@@ -123,7 +123,7 @@ ColumnLayout {
text: i18nc("@action:button Block the user", "Block %1", root.invitingMember.displayName)
onClicked: {
root.currentRoom.forget()
RoomManager.leaveRoom(root.currentRoom);
root.currentRoom.connection.addToIgnoredUsers(root.currentRoom.invitingUserId);
}
}

View File

@@ -12,7 +12,7 @@ import org.kde.prison
import org.kde.neochat
Kirigami.Dialog {
QQC2.Dialog {
id: root
required property string room
@@ -23,7 +23,7 @@ Kirigami.Dialog {
topPadding: 0
bottomPadding: 0
standardButtons: Kirigami.Dialog.NoButton
standardButtons: QQC2.Dialog.NoButton
width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
title: i18nc("@title:dialog", "Join Room")

View File

@@ -6,13 +6,12 @@ import QtLocation
import QtPositioning
import org.kde.kirigami as Kirigami
import org.kde.neochat.libneochat
import org.kde.neochat
Kirigami.Page {
id: root
required property NeoChatRoom room
required property var room
title: i18nc("Locations on a map", "Locations")

View File

@@ -80,8 +80,9 @@ Kirigami.ApplicationWindow {
Loader {
active: Kirigami.Settings.hasPlatformMenuBar && !Kirigami.Settings.isMobile
sourceComponent: GlobalMenu {
connection: root.connection
sourceComponent: Qt.createComponent("org.kde.neochat", "GlobalMenu")
onActiveChanged: if (active) {
item.connection = root.connection;
}
}
@@ -148,13 +149,9 @@ Kirigami.ApplicationWindow {
}
function openRoomDrawer() {
const page = pageStack.push(Qt.createComponent('org.kde.neochat', 'RoomDrawerPage'), {
connection: root.connection,
room: RoomManager.currentRoom,
userListModel: RoomManager.userListModel,
mediaMessageFilterModel: RoomManager.mediaMessageFilterModel
pageStack.push(Qt.createComponent('org.kde.neochat', 'RoomDrawerPage'), {
connection: root.connection
});
page.resolveResource.connect((idOrUri, action) => RoomManager.resolveResource(idOrUri, action))
}
contextDrawer: RoomDrawer {
@@ -164,18 +161,7 @@ Kirigami.ApplicationWindow {
// It is used to ensure that user choice is remembered when changing pages and expanding and contracting the window width
property bool drawerUserState: NeoChatConfig.autoRoomInfoDrawer
room: RoomManager.currentRoom
connection: root.connection
userListModel: RoomManager.userListModel
mediaMessageFilterModel: RoomManager.mediaMessageFilterModel
onResolveResource: (idOrUri, action) => RoomManager.resolveResource(idOrUri, action)
roomDrawerWidth: NeoChatConfig.roomDrawerWidth
onRoomDrawerWidthChanged: {
NeoChatConfig.roomDrawerWidth = actualWidth;
NeoChatConfig.save();
}
handleClosedIcon.source: "documentinfo-symbolic"
handleClosedToolTip: i18nc("@action:button", "Show Room Information")

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Window
import QtQuick.Layouts
@@ -10,7 +11,7 @@ import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.neochat
Kirigami.Dialog {
QQC2.Dialog {
id: root
/**
@@ -31,35 +32,41 @@ Kirigami.Dialog {
topPadding: 0
bottomPadding: 0
standardButtons: Kirigami.Dialog.Cancel
customFooterActions: [
Kirigami.Action {
footer: QQC2.DialogButtonBox {
QQC2.Button {
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole
enabled: roomIdAliasText.isValidText
text: i18n("OK")
icon.name: "dialog-ok"
onTriggered: {
// We don't necessarily have all the info so fill out the best we can.
let roomId = roomIdAliasText.isAlias() ? "" : roomIdAliasText.text;
let displayName = "";
let avatarUrl = "";
let alias = roomIdAliasText.isAlias() ? roomIdAliasText.text : "";
let topic = "";
let memberCount = -1;
let isJoined = false;
if (roomIdAliasText.room) {
roomId = roomIdAliasText.room.id;
displayName = roomIdAliasText.room.displayName;
avatarUrl = roomIdAliasText.room.avatarUrl.toString().length > 0 ? connection.makeMediaUrl(roomIdAliasText.room.avatarUrl) : "";
alias = roomIdAliasText.room.canonicalAlias;
topic = roomIdAliasText.room.topic;
memberCount = roomIdAliasText.room.joinedCount;
isJoined = true;
}
root.roomSelected(roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined);
root.close();
}
}
]
QQC2.Button {
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.RejectRole
text: i18n("Cancel")
icon.name: "dialog-cancel"
}
}
onAccepted: {
// We don't necessarily have all the info so fill out the best we can.
let roomId = roomIdAliasText.isAlias() ? "" : roomIdAliasText.text;
let displayName = "";
let avatarUrl = "";
let alias = roomIdAliasText.isAlias() ? roomIdAliasText.text : "";
let topic = "";
let memberCount = -1;
let isJoined = false;
if (roomIdAliasText.room) {
roomId = roomIdAliasText.room.id;
displayName = roomIdAliasText.room.displayName;
avatarUrl = roomIdAliasText.room.avatarUrl.toString().length > 0 ? connection.makeMediaUrl(roomIdAliasText.room.avatarUrl) : "";
alias = roomIdAliasText.room.canonicalAlias;
topic = roomIdAliasText.room.topic;
memberCount = roomIdAliasText.room.joinedCount;
isJoined = true;
}
root.roomSelected(roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined);
root.close();
}
contentItem: ColumnLayout {
spacing: 0

View File

@@ -10,7 +10,7 @@ import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.neochat
Kirigami.Dialog {
QQC2.Dialog {
id: root
/**
@@ -31,19 +31,6 @@ Kirigami.Dialog {
topPadding: 0
bottomPadding: 0
standardButtons: Kirigami.Dialog.Cancel
customFooterActions: [
Kirigami.Action {
enabled: userIdText.isValidText
text: i18n("OK")
icon.name: "dialog-ok"
onTriggered: {
root.connection.requestDirectChat(userIdText.text);
root.accept();
}
}
]
contentItem: ColumnLayout {
spacing: 0
FormCard.FormTextFieldDelegate {
@@ -75,6 +62,21 @@ Kirigami.Dialog {
}
}
footer: QQC2.DialogButtonBox {
standardButtons: QQC2.Dialog.Cancel
QQC2.Button {
enabled: userIdText.isValidText
text: i18n("OK")
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole
icon.name: "dialog-ok"
onClicked: {
root.connection.requestDirectChat(userIdText.text);
root.accept();
}
}
}
onVisibleChanged: {
userIdText.forceActiveFocus();
timer.restart();

View File

@@ -15,27 +15,18 @@ import Quotient
import org.kde.neochat
Kirigami.Dialog {
QQC2.Dialog {
id: root
required property NeoChatRoom room
standardButtons: Kirigami.Dialog.Cancel
customFooterActions: [
Kirigami.Action {
enabled: optionModel.allValuesSet && questionTextField.text.length > 0
text: i18nc("@action:button", "Send")
icon.name: "document-send"
onTriggered: {
root.room.postPoll(pollTypeCombo.currentValue, questionTextField.text, optionModel.values())
root.close()
}
}
]
title: i18nc("@title: create new poll in the room", "Create Poll")
width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
title: i18nc("@title: create new poll in the room", "Create Poll")
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
contentItem: ColumnLayout {
spacing: 0
@@ -153,4 +144,19 @@ Kirigami.Dialog {
onClicked: optionModel.append({optionText: ""})
}
}
footer: QQC2.DialogButtonBox {
standardButtons: QQC2.Dialog.Cancel
QQC2.Button {
enabled: optionModel.allValuesSet && questionTextField.text.length > 0
text: i18nc("@action:button", "Send")
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole
icon.name: "document-send"
onClicked: {
root.room.postPoll(pollTypeCombo.currentValue, questionTextField.text, optionModel.values())
root.close()
}
}
}
}

View File

@@ -10,13 +10,11 @@ import org.kde.kirigamiaddons.components
import org.kde.neochat
Kirigami.Dialog {
QQC2.Dialog {
id: root
property var connection
parent: applicationWindow().overlay
leftPadding: 0
rightPadding: 0
topPadding: 0

View File

@@ -2,14 +2,15 @@
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.neochat.libneochat
import org.kde.neochat
Kirigami.Dialog {
QQC2.Dialog {
id: root
required property NeoChatRoom parentRoom
@@ -28,7 +29,7 @@ Kirigami.Dialog {
width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel
standardButtons: QQC2.Dialog.Ok | QQC2.Dialog.Cancel
onAccepted: parentRoom.removeChild(root.roomId, removeOfficalCheck.checked)

View File

@@ -9,19 +9,14 @@ import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kitemmodels
import org.kde.neochat.libneochat
import org.kde.neochat.timeline as Timeline
import org.kde.neochat.settings as Settings
import org.kde.neochat
import org.kde.neochat.settings
Kirigami.OverlayDrawer {
id: root
required property NeoChatRoom room
readonly property NeoChatRoom room: RoomManager.currentRoom
required property NeoChatConnection connection
required property UserListModel userListModel
required property Timeline.MediaMessageFilterModel mediaMessageFilterModel
signal resolveResource(string idOrUri, string action)
width: actualWidth
interactive: modal
@@ -29,12 +24,11 @@ Kirigami.OverlayDrawer {
readonly property int minWidth: Kirigami.Units.gridUnit * 15
readonly property int maxWidth: Kirigami.Units.gridUnit * 25
readonly property int defaultWidth: Kirigami.Units.gridUnit * 20
property int roomDrawerWidth
property int actualWidth: {
if (root.roomDrawerWidth === -1) {
if (NeoChatConfig.roomDrawerWidth === -1) {
return Kirigami.Units.gridUnit * 20;
} else {
return root.roomDrawerWidth;
return NeoChatConfig.roomDrawerWidth;
}
}
@@ -52,7 +46,8 @@ Kirigami.OverlayDrawer {
visible: true
onPressed: _lastX = mapToGlobal(mouseX, mouseY).x
onReleased: {
root.roomDrawerWidth = root.actualWidth;
NeoChatConfig.roomDrawerWidth = root.actualWidth;
NeoChatConfig.save();
}
property real _lastX: -1
@@ -61,9 +56,9 @@ Kirigami.OverlayDrawer {
return;
}
if (Qt.application.layoutDirection === Qt.RightToLeft) {
root.actualWidth = Math.min(root.maxWidth, Math.max(root.minWidth, root.roomDrawerWidth - _lastX + mapToGlobal(mouseX, mouseY).x));
root.actualWidth = Math.min(root.maxWidth, Math.max(root.minWidth, NeoChatConfig.roomDrawerWidth - _lastX + mapToGlobal(mouseX, mouseY).x));
} else {
root.actualWidth = Math.min(root.maxWidth, Math.max(root.minWidth, root.roomDrawerWidth + _lastX - mapToGlobal(mouseX, mouseY).x));
root.actualWidth = Math.min(root.maxWidth, Math.max(root.minWidth, NeoChatConfig.roomDrawerWidth + _lastX - mapToGlobal(mouseX, mouseY).x));
}
}
}
@@ -126,7 +121,7 @@ Kirigami.OverlayDrawer {
QQC2.ToolTip.visible: hovered
onClicked: {
Settings.RoomSettingsView.openRoomSettings(root.room, Settings.RoomSettingsView.Room);
RoomSettingsView.openRoomSettings(root.room, RoomSettingsView.Room);
}
}
}
@@ -143,17 +138,15 @@ Kirigami.OverlayDrawer {
id: roomInformation
RoomInformation {
room: root.room
userListModel: root.userListModel
onResolveResource: (idOrUri, action) => root.resolveResource(idOrUri, action)
connection: root.connection
}
}
Component {
id: roomMedia
RoomMedia {
room: root.room
mediaMessageFilterModel: root.mediaMessageFilterModel
currentRoom: root.room
connection: root.connection
}
}

View File

@@ -7,8 +7,7 @@ import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kitemmodels
import org.kde.neochat.libneochat
import org.kde.neochat.timeline as Timeline
import org.kde.neochat
/**
* @brief Page for holding a room drawer component.
@@ -25,12 +24,8 @@ Kirigami.Page {
/**
* @brief The current room that user is viewing.
*/
required property NeoChatRoom room
readonly property NeoChatRoom room: RoomManager.currentRoom
required property NeoChatConnection connection
required property UserListModel userListModel
required property Timeline.MediaMessageFilterModel mediaMessageFilterModel
signal resolveResource(string idOrUri, string action)
title: drawerItemLoader.item ? drawerItemLoader.item.title : ""
@@ -66,17 +61,15 @@ Kirigami.Page {
id: roomInformation
RoomInformation {
room: root.room
userListModel: root.userListModel
onResolveResource: (idOrUri, action) => root.resolveResource(idOrUri, action)
connection: root.connection
}
}
Component {
id: roomMedia
RoomMedia {
room: root.room
mediaMessageFilterModel: root.mediaMessageFilterModel
currentRoom: root.room
connection: root.connection
}
}

View File

@@ -12,7 +12,7 @@ import org.kde.kirigamiaddons.delegates as Delegates
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
import org.kde.kitemmodels
import org.kde.neochat.libneochat
import org.kde.neochat
/**
* @brief Component for visualising the room information.
@@ -34,15 +34,13 @@ QQC2.ScrollView {
*/
required property NeoChatRoom room
required property UserListModel userListModel
required property NeoChatConnection connection
/**
* @brief The title that should be displayed for this component if available.
*/
readonly property string title: root.room.isSpace ? i18nc("@action:title", "Space Members") : i18nc("@action:title", "Room Information")
signal resolveResource(string idOrUri, string action)
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
@@ -218,7 +216,7 @@ QQC2.ScrollView {
UserFilterModel {
id: userFilterModel
sourceModel: root.userListModel
sourceModel: RoomManager.userListModel
allowEmpty: true
}
@@ -251,7 +249,7 @@ QQC2.ScrollView {
KeyNavigation.backtab: index === 0 ? userList.headerItem.userListSearchField : null
onClicked: {
root.resolveResource(userDelegate.userId, "mention");
RoomManager.resolveResource(userDelegate.userId, "mention");
}
contentItem: RowLayout {
@@ -288,8 +286,6 @@ QQC2.ScrollView {
id: directChatDrawerHeader
DirectChatDrawerHeader {
room: root.room
onResolveResource: (idOrUri, action) => root.resolveResource(idOrUri, action)
}
}

View File

@@ -6,8 +6,8 @@ import QtQuick.Controls as QQC2
import QtQuick.Layouts
import Qt.labs.qmlmodels
import org.kde.neochat.libneochat
import org.kde.neochat.timeline as Timeline
import org.kde.neochat
import org.kde.neochat.timeline
/**
* @brief Component for visualising the loaded media items in the room.
@@ -31,9 +31,9 @@ QQC2.ScrollView {
/**
* @brief The current room that user is viewing.
*/
required property NeoChatRoom room
required property NeoChatRoom currentRoom
required property Timeline.MediaMessageFilterModel mediaMessageFilterModel
required property NeoChatConnection connection
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
@@ -42,26 +42,26 @@ QQC2.ScrollView {
clip: true
verticalLayoutDirection: ListView.BottomToTop
model: root.mediaMessageFilterModel
model: RoomManager.mediaMessageFilterModel
delegate: DelegateChooser {
role: "type"
DelegateChoice {
roleValue: Timeline.MediaMessageFilterModel.Image
delegate: Timeline.MessageDelegate {
roleValue: MediaMessageFilterModel.Image
delegate: MessageDelegate {
alwaysFillWidth: true
cardBackground: false
room: root.room
room: root.currentRoom
}
}
DelegateChoice {
roleValue: Timeline.MediaMessageFilterModel.Video
delegate: Timeline.MessageDelegate {
roleValue: MediaMessageFilterModel.Video
delegate: MessageDelegate {
alwaysFillWidth: true
cardBackground: false
room: root.room
room: root.currentRoom
}
}
}

View File

@@ -137,9 +137,7 @@ Kirigami.Page {
id: spaceLoader
active: root.currentRoom && root.currentRoom.isSpace
anchors.fill: parent
sourceComponent: SpaceHomePage {
room: root.currentRoom
}
sourceComponent: SpaceHomePage {}
}
Loader {
@@ -266,7 +264,6 @@ Kirigami.Page {
plainText: plainText,
mimeType: mimeType,
progressInfo: progressInfo,
messageComponentType: messageComponentType,
});
contextMenu.popup();
}

View File

@@ -6,7 +6,7 @@ import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.neochat.libneochat
import org.kde.neochat
import org.kde.neochat.timeline
/**

View File

@@ -3,7 +3,7 @@
import QtQuick
import org.kde.neochat.libneochat
import org.kde.neochat
import org.kde.neochat.timeline
/**

View File

@@ -102,7 +102,7 @@ QQC2.ComboBox {
}
}
Kirigami.Dialog {
QQC2.Dialog {
id: addServerSheet
width: Math.min(Kirigami.Units.gridUnit * 15, QQC2.ApplicationWindow.window.width)
@@ -182,15 +182,19 @@ QQC2.ComboBox {
}
}
customFooterActions: Kirigami.Action {
text: i18nc("@action:button", "Ok")
enabled: serverUrlField.acceptableInput && serverUrlField.isValidServer
onTriggered: {
serverListModel.addServer(serverUrlField.text);
root.currentIndex = root.indexOfValue(serverUrlField.text);
root.server = root.currentValue;
serverUrlField.text = "";
addServerSheet.close();
footer: QQC2.DialogButtonBox {
QQC2.Button {
enabled: serverUrlField.acceptableInput && serverUrlField.isValidServer
text: i18nc("@action:button", "Ok")
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole
icon.name: "dialog-ok"
onClicked: {
serverListModel.addServer(serverUrlField.text);
root.currentIndex = root.indexOfValue(serverUrlField.text);
root.server = root.currentValue;
serverUrlField.text = "";
addServerSheet.close();
}
}
}
}

View File

@@ -13,7 +13,7 @@ import org.kde.prison
import org.kde.neochat
Kirigami.Dialog {
QQC2.Dialog {
id: root
// This dialog is sometimes used outside the context of a room, e.g., when scanning a user's QR code.
@@ -28,7 +28,7 @@ Kirigami.Dialog {
topPadding: 0
bottomPadding: 0
standardButtons: Kirigami.Dialog.NoButton
standardButtons: QQC2.Dialog.NoButton
width: Math.min(QQC2.ApplicationWindow.window.width, Kirigami.Units.gridUnit * 24)
title: i18nc("@title:menu Account details dialog", "Account Details")

View File

@@ -1,74 +0,0 @@
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.components as KirigamiComponents
import org.kde.neochat
import org.kde.neochat.settings
import org.kde.neochat.devtools
KirigamiComponents.ConvergentContextMenu {
id: root
required property Kirigami.ApplicationWindow window
required property var author
headerContentItem: RowLayout {
id: detailRow
spacing: Kirigami.Units.largeSpacing
KirigamiComponents.Avatar {
id: avatar
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
name: root.author.displayName
source: root.author.avatarUrl
color: root.author.color
}
ColumnLayout {
Layout.fillWidth: true
spacing: 0
Kirigami.Heading {
level: 1
Layout.fillWidth: true
font.bold: true
elide: Text.ElideRight
wrapMode: Text.NoWrap
text: root.author.displayName
textFormat: Text.PlainText
}
QQC2.Label {
id: idLabel
textFormat: TextEdit.PlainText
text: root.author.id
elide: Qt.ElideRight
}
}
}
QQC2.Action {
text: i18nc("@action:button", "Open Profile")
icon.name: "im-user-symbolic"
onTriggered: RoomManager.resolveResource(root.author.uri)
}
QQC2.Action {
text: i18nc("@action:button", "Mention")
icon.name: "username-copy-symbolic"
onTriggered: {
RoomManager.currentRoom.mainCache.mentionAdded(root.author.id);
}
}
}

View File

@@ -303,12 +303,7 @@ void RoomManager::loadInitialRoom()
}
if (m_isMobile) {
QString lastSpace = m_lastSpaceConfig.readEntry(m_connection->userId(), QString());
// We can't have empty keys in KConfig, so we stored it as "Home"
if (lastSpace == u"Home"_s) {
lastSpace.clear();
}
setCurrentSpace(lastSpace, false);
setCurrentSpace(m_lastSpaceConfig.readEntry(m_connection->userId(), QString()), false);
// We don't want to open a room on startup on mobile
return;
}
@@ -330,7 +325,14 @@ void RoomManager::openRoomForActiveConnection()
setCurrentSpace({}, false);
return;
}
setCurrentSpace(m_lastSpaceConfig.readEntry(m_connection->userId(), QString()), true);
setCurrentSpace(m_lastSpaceConfig.readEntry(m_connection->userId(), QString()), false);
const auto &lastRoom = m_lastRoomConfig.readEntry(m_connection->userId(), QString());
if (lastRoom.isEmpty() || !m_connection->room(lastRoom)) {
setCurrentRoom({});
} else {
m_currentRoom = nullptr;
resolveResource(lastRoom);
}
}
UriResolveResult RoomManager::visitUser(User *user, const QString &action)
@@ -446,27 +448,29 @@ void RoomManager::knockRoom(NeoChatConnection *account, const QString &roomAlias
Qt::SingleShotConnection);
}
void RoomManager::roomLeft(const QString &id)
{
if (id.isEmpty()) {
return;
}
if (m_currentRoom && m_currentRoom->id() == id) {
setCurrentRoom({});
}
if (m_currentSpaceId == id) {
setCurrentSpace({});
}
}
bool RoomManager::visitNonMatrix(const QUrl &url)
{
UrlHelper().openUrl(url);
return true;
}
void RoomManager::leaveRoom(NeoChatRoom *room)
{
if (!room) {
return;
}
if (m_currentRoom && m_currentRoom->id() == room->id()) {
setCurrentRoom({});
}
if (m_currentSpaceId == room->id()) {
setCurrentSpace({});
}
room->forget();
}
ChatDocumentHandler *RoomManager::chatDocumentHandler() const
{
return m_chatDocumentHandler;
@@ -519,32 +523,18 @@ void RoomManager::setCurrentSpace(const QString &spaceId, bool setRoom)
Q_EMIT currentSpaceChanged();
if (m_connection) {
if (spaceId.isEmpty()) {
m_lastSpaceConfig.writeEntry(m_connection->userId(), u"Home"_s);
} else {
m_lastSpaceConfig.writeEntry(m_connection->userId(), spaceId);
}
m_lastSpaceConfig.writeEntry(m_connection->userId(), spaceId);
}
if (!setRoom) {
return;
}
// We intentionally don't want to open the last room on mobile
if (!m_isMobile) {
QString configSpaceId = spaceId;
// We can't have empty keys in KConfig, so it's stored as "Home"
if (spaceId.isEmpty()) {
configSpaceId = u"Home"_s;
}
const auto &lastRoom = m_lastRoomConfig.readEntry(configSpaceId, QString());
if (lastRoom.isEmpty()) {
if (spaceId != u"DM"_s && spaceId != u"Home"_s) {
resolveResource(spaceId, "no_join"_L1);
}
if (spaceId.length() > 3) {
resolveResource(spaceId, "no_join"_L1);
} else {
resolveResource(lastRoom, "no_join"_L1);
visitRoom({}, {});
}
}
}
@@ -567,16 +557,7 @@ void RoomManager::setCurrentRoom(const QString &roomId)
Q_EMIT currentRoomChanged();
if (m_connection) {
if (roomId.isEmpty()) {
m_lastRoomConfig.deleteEntry(m_currentSpaceId);
} else {
// We can't have empty keys in KConfig, so name it "Home"
if (m_currentSpaceId.isEmpty()) {
m_lastRoomConfig.writeEntry(u"Home"_s, roomId);
} else {
m_lastRoomConfig.writeEntry(m_currentSpaceId, roomId);
}
}
m_lastRoomConfig.writeEntry(m_connection->userId(), roomId);
}
if (roomId.isEmpty()) {
return;

View File

@@ -195,6 +195,11 @@ public:
*/
Q_INVOKABLE void loadInitialRoom();
/**
* @brief Leave the room and close it if it is open.
*/
Q_INVOKABLE void leaveRoom(NeoChatRoom *room);
/**
* @brief Knock a room.
*
@@ -203,13 +208,6 @@ public:
*/
void knockRoom(NeoChatConnection *account, const QString &roomAliasOrId, const QString &reason, const QStringList &viaServers);
/**
* @brief Cleanup after the given room is left.
*
* This ensures that the current room and space are not set to the left room.
*/
void roomLeft(const QString &id);
/**
* @brief Show a media item maximized.
*

View File

@@ -55,19 +55,6 @@ QQC2.Control {
}
}
Connections {
target: currentRoom.mainCache
function onMentionAdded(mention: string): void {
// add mention text
textField.append(mention + " ");
// move cursor to the end
textField.cursorPosition = textField.text.length;
// move the focus back to the chat bar
textField.forceActiveFocus(Qt.OtherFocusReason);
}
}
/**
* @brief The list of actions in the ChatBar.
*
@@ -136,7 +123,7 @@ QQC2.Control {
displayHint: QQC2.AbstractButton.IconOnly
onTriggered: {
newPollDialog.createObject(QQC2.Overlay.overlay, {
newPollDialog.createObject(root.QQC2.Overlay.overlay, {
room: root.currentRoom
}).open();
}
@@ -355,13 +342,11 @@ QQC2.Control {
icon.name: modelData.isBusy ? "" : (modelData.icon.name.length > 0 ? modelData.icon.name : modelData.icon.source)
onClicked: modelData.trigger()
padding: Kirigami.Units.smallSpacing
QQC2.ToolTip.visible: hovered
QQC2.ToolTip.text: modelData.tooltip
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
contentItem: PieProgressBar {
PieProgressBar {
visible: modelData.isBusy
progress: root.currentRoom.fileUploadingProgress
}

View File

@@ -41,26 +41,12 @@ target_sources(LibNeoChat PRIVATE
models/locationsmodel.cpp
models/roomlistmodel.cpp
models/stickermodel.cpp
models/userfiltermodel.cpp
models/userlistmodel.cpp
)
ecm_add_qml_module(LibNeoChat GENERATE_PLUGIN_SOURCE
URI org.kde.neochat.libneochat
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/libneochat
DEPENDENCIES
QtCore
QtQuick
org.kde.prison
org.kde.prison.scanner
QML_FILES
qml/GroupChatDrawerHeader.qml
qml/LocationMapItem.qml
qml/InviteUserPage.qml
qml/ExploreRoomsPage.qml
qml/SearchPage.qml
qml/CreateRoomDialog.qml
qml/CreateSpaceDialog.qml
)
ecm_qt_declare_logging_category(LibNeoChat

View File

@@ -23,10 +23,12 @@ AccountManager::AccountManager(bool testMode, QObject *parent)
loadAccountsFromCache();
});
} else {
auto c = new NeoChatConnection(QUrl(u"https://localhost:1234"_s), this);
auto c = new NeoChatConnection(this);
c->assumeIdentity(u"@user:localhost:1234"_s, u"device_1234"_s, u"token_1234"_s);
m_accountRegistry->add(c);
c->syncLoop();
connect(c, &NeoChatConnection::connected, this, [c, this]() {
m_accountRegistry->add(c);
c->syncLoop();
});
}
}

View File

@@ -195,7 +195,6 @@ Q_SIGNALS:
void relationIdChanged(const QString &oldEventId, const QString &newEventId);
void threadIdChanged(const QString &oldThreadId, const QString &newThreadId);
void attachmentPathChanged();
void mentionAdded(const QString &mention);
private:
QString m_text = QString();

Some files were not shown because too many files have changed in this diff Show More