Compare commits
217 Commits
work/nvrwh
...
release/24
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cedbbd6c56 | ||
|
|
3215224528 | ||
|
|
2f6d7308c5 | ||
|
|
6450f0dbe8 | ||
|
|
3d7a964889 | ||
|
|
2ab2570d94 | ||
|
|
bf81e743f4 | ||
|
|
a85af258fe | ||
|
|
063e7393ac | ||
|
|
dafd02790f | ||
|
|
29dd6b12ec | ||
|
|
9c7a49bf62 | ||
|
|
e3cb592cd1 | ||
|
|
2dc54441ac | ||
|
|
9259e7bb52 | ||
|
|
9212cf3f45 | ||
|
|
49a99058b7 | ||
|
|
b7c9ec9bd2 | ||
|
|
683d1f3487 | ||
|
|
e7563870bf | ||
|
|
59a865e1d3 | ||
|
|
75b6444c1e | ||
|
|
180e927f82 | ||
|
|
fcb32b1974 | ||
|
|
bf9d1e5261 | ||
|
|
d6eb91c476 | ||
|
|
96b03082e3 | ||
|
|
962063e58a | ||
|
|
06a09700a0 | ||
|
|
12657abd03 | ||
|
|
3752c3b872 | ||
|
|
acf3f7030d | ||
|
|
63ed48f283 | ||
|
|
25f2693710 | ||
|
|
2efcc1041b | ||
|
|
1f5823cec0 | ||
|
|
efd18fa2d6 | ||
|
|
98dc2cf41a | ||
|
|
97f27a1ae0 | ||
|
|
46b9566242 | ||
|
|
70ab0374ec | ||
|
|
2b2e991bb8 | ||
|
|
fbf4dfbe35 | ||
|
|
1344e46201 | ||
|
|
728d133b7c | ||
|
|
a41be9e19b | ||
|
|
aa5ece8bfb | ||
|
|
21f5ee74ba | ||
|
|
2e6cf03c15 | ||
|
|
9332910bcb | ||
|
|
157c098af3 | ||
|
|
9fe134e7f0 | ||
|
|
46aaab3fb0 | ||
|
|
a5b37a78a0 | ||
|
|
59699abb94 | ||
|
|
3cc0d89ee5 | ||
|
|
96e83fc71b | ||
|
|
d89019d752 | ||
|
|
51565dfdd2 | ||
|
|
e1d09171d5 | ||
|
|
f86572f880 | ||
|
|
d7451834f3 | ||
|
|
a4767cea7d | ||
|
|
4c43869fd4 | ||
|
|
e603664521 | ||
|
|
369242ab31 | ||
|
|
013773d465 | ||
|
|
20b17a58d3 | ||
|
|
1c4bb79347 | ||
|
|
6d2b49f3eb | ||
|
|
e3d5867da6 | ||
|
|
a046e3ed27 | ||
|
|
5b935c1d33 | ||
|
|
fe6bc5a36e | ||
|
|
c085be4f6e | ||
|
|
1f73a9dc90 | ||
|
|
63206ef1dd | ||
|
|
0d286db0c2 | ||
|
|
7d3f478a74 | ||
|
|
6df2ebd1eb | ||
|
|
252fb6eb21 | ||
|
|
5873092356 | ||
|
|
30822003d1 | ||
|
|
52ae237eb7 | ||
|
|
ee02abfe37 | ||
|
|
f0de235f37 | ||
|
|
9e9fe6d275 | ||
|
|
f4ca5f0f34 | ||
|
|
5f240fa05c | ||
|
|
1e29eca59a | ||
|
|
1f71ec3bf8 | ||
|
|
64c5ad88f6 | ||
|
|
fb5a3c1c5c | ||
|
|
4a5a83f94a | ||
|
|
133edc249f | ||
|
|
da30e66127 | ||
|
|
4516e1e0f4 | ||
|
|
bd80f65163 | ||
|
|
f828ecf282 | ||
|
|
a0483167c5 | ||
|
|
87288f508c | ||
|
|
dc184ed2fd | ||
|
|
49e1bf9ab1 | ||
|
|
74acf3f9dc | ||
|
|
38205d2791 | ||
|
|
81da926d4f | ||
|
|
1018fe5d3f | ||
|
|
58b32dd50f | ||
|
|
82184b895a | ||
|
|
da0f6f78a4 | ||
|
|
cfd06d064c | ||
|
|
a90e9ae92a | ||
|
|
8ab0002057 | ||
|
|
e1840be234 | ||
|
|
d6ecaaa344 | ||
|
|
0c08c2ab89 | ||
|
|
39ff11e059 | ||
|
|
93254431c5 | ||
|
|
fc14a8eac8 | ||
|
|
50759bb3ca | ||
|
|
23134d8e72 | ||
|
|
7cd095f76a | ||
|
|
d5c3054da4 | ||
|
|
ae12c838bd | ||
|
|
51727dd345 | ||
|
|
5611b000fb | ||
|
|
461896e228 | ||
|
|
e388536a03 | ||
|
|
61f22edd86 | ||
|
|
9e368691d6 | ||
|
|
dad2b3ec8f | ||
|
|
8821c37ff8 | ||
|
|
c105170eca | ||
|
|
b07c04eddc | ||
|
|
7aa0f68b10 | ||
|
|
cbdae4c312 | ||
|
|
9210940556 | ||
|
|
d8489527b4 | ||
|
|
64c9cd97de | ||
|
|
119a9890b1 | ||
|
|
69be6b5939 | ||
|
|
112152f2df | ||
|
|
2ef634a6cb | ||
|
|
fd31b4fb74 | ||
|
|
3b12520fa2 | ||
|
|
24718a5f72 | ||
|
|
db62bacc7e | ||
|
|
6500669b67 | ||
|
|
557d151ed4 | ||
|
|
95ffd485b4 | ||
|
|
ebd38fb435 | ||
|
|
a5b999e682 | ||
|
|
41d34fc0e4 | ||
|
|
b51194f90f | ||
|
|
80ac9e1ba7 | ||
|
|
e3874c824a | ||
|
|
6599c6b609 | ||
|
|
13d522221c | ||
|
|
dd8f926f32 | ||
|
|
258312e798 | ||
|
|
43d40c7e75 | ||
|
|
cbcc9a6514 | ||
|
|
625048610b | ||
|
|
fa47b67e3d | ||
|
|
9347a66acf | ||
|
|
317df56ffa | ||
|
|
fed9197716 | ||
|
|
1e892599e9 | ||
|
|
b7229ca0cf | ||
|
|
953b711823 | ||
|
|
01d903efd3 | ||
|
|
241dd81932 | ||
|
|
f6dfe0cbcf | ||
|
|
f10b97139c | ||
|
|
385c5b3405 | ||
|
|
7bc6f906f8 | ||
|
|
b8b1434a95 | ||
|
|
85c7a4bcb3 | ||
|
|
84b698a7e8 | ||
|
|
7b249e9fa6 | ||
|
|
46593ef68f | ||
|
|
b70f73c7d6 | ||
|
|
ece5e34fa2 | ||
|
|
74b400288d | ||
|
|
83f19b0631 | ||
|
|
c905d2d6fb | ||
|
|
d0c1eb2f04 | ||
|
|
d7d9d29c1d | ||
|
|
2a3f019ec6 | ||
|
|
d384d50b0d | ||
|
|
be8cb12bba | ||
|
|
a1aa2918be | ||
|
|
d7536bccb3 | ||
|
|
6b677355e1 | ||
|
|
85d625f6ac | ||
|
|
f5d6f87afe | ||
|
|
31d755f407 | ||
|
|
ebfc20d4b4 | ||
|
|
b83d42103f | ||
|
|
c9856347fe | ||
|
|
7224c92caf | ||
|
|
a84f98c96f | ||
|
|
a40bccc29d | ||
|
|
171897161c | ||
|
|
013ad49e2b | ||
|
|
b357586164 | ||
|
|
5642f3416a | ||
|
|
9bbb1710df | ||
|
|
e2b7679252 | ||
|
|
5c353cd4b5 | ||
|
|
cba6dc994f | ||
|
|
b0c4b7fc2a | ||
|
|
ed7aff1f24 | ||
|
|
33f4be0d88 | ||
|
|
1178cafef0 | ||
|
|
1be97e65b4 | ||
|
|
634cefc694 |
@@ -110,7 +110,7 @@
|
|||||||
{
|
{
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/quotient-im/libQuotient.git",
|
"url": "https://github.com/quotient-im/libQuotient.git",
|
||||||
"branch": "0.8.x",
|
"tag": "0.9.2",
|
||||||
"disable-submodules": true
|
"disable-submodules": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ Dependencies:
|
|||||||
'frameworks/kquickcharts': '@latest-kf6'
|
'frameworks/kquickcharts': '@latest-kf6'
|
||||||
'frameworks/knotifications': '@latest-kf6'
|
'frameworks/knotifications': '@latest-kf6'
|
||||||
'frameworks/kcolorscheme': '@latest-kf6'
|
'frameworks/kcolorscheme': '@latest-kf6'
|
||||||
|
'frameworks/kiconthemes': '@latest-kf6'
|
||||||
'libraries/kquickimageeditor': '@latest-kf6'
|
'libraries/kquickimageeditor': '@latest-kf6'
|
||||||
'frameworks/sonnet': '@latest-kf6'
|
'frameworks/sonnet': '@latest-kf6'
|
||||||
'frameworks/prison': '@latest-kf6'
|
'frameworks/prison': '@latest-kf6'
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ cmake_minimum_required(VERSION 3.16)
|
|||||||
|
|
||||||
# KDE Applications version, managed by release script.
|
# KDE Applications version, managed by release script.
|
||||||
set(RELEASE_SERVICE_VERSION_MAJOR "24")
|
set(RELEASE_SERVICE_VERSION_MAJOR "24")
|
||||||
set(RELEASE_SERVICE_VERSION_MINOR "11")
|
set(RELEASE_SERVICE_VERSION_MINOR "12")
|
||||||
set(RELEASE_SERVICE_VERSION_MICRO "70")
|
set(RELEASE_SERVICE_VERSION_MICRO "3")
|
||||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||||
|
|
||||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||||
@@ -66,7 +66,7 @@ if (QT_KNOWN_POLICY_QTP0004)
|
|||||||
qt_policy(SET QTP0004 NEW)
|
qt_policy(SET QTP0004 NEW)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels ColorScheme)
|
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels IconThemes ColorScheme)
|
||||||
set_package_properties(KF6 PROPERTIES
|
set_package_properties(KF6 PROPERTIES
|
||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
PURPOSE "Basic application components"
|
PURPOSE "Basic application components"
|
||||||
@@ -107,7 +107,7 @@ if (NOT ANDROID AND NOT WIN32 AND NOT APPLE AND NOT HAIKU)
|
|||||||
find_package(KF6DBusAddons ${KF_MIN_VERSION} REQUIRED)
|
find_package(KF6DBusAddons ${KF_MIN_VERSION} REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(QuotientQt6 0.8.2)
|
find_package(QuotientQt6 0.9)
|
||||||
set_package_properties(QuotientQt6 PROPERTIES
|
set_package_properties(QuotientQt6 PROPERTIES
|
||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
DESCRIPTION "Qt wrapper around Matrix API"
|
DESCRIPTION "Qt wrapper around Matrix API"
|
||||||
@@ -115,11 +115,6 @@ set_package_properties(QuotientQt6 PROPERTIES
|
|||||||
PURPOSE "Talk with matrix server"
|
PURPOSE "Talk with matrix server"
|
||||||
)
|
)
|
||||||
|
|
||||||
if (NOT TARGET Olm::Olm)
|
|
||||||
message(FATAL_ERROR "NeoChat requires Quotient with the E2EE feature enabled")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
find_package(cmark)
|
find_package(cmark)
|
||||||
set_package_properties(cmark PROPERTIES
|
set_package_properties(cmark PROPERTIES
|
||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ A Qt/QML based Matrix client.
|
|||||||
|
|
||||||
<a href='https://matrix.org'><img src='https://matrix.org/docs/legacy/made-for-matrix.png' alt='Made for Matrix' height=64 target=_blank /></a>
|
<a href='https://matrix.org'><img src='https://matrix.org/docs/legacy/made-for-matrix.png' alt='Made for Matrix' height=64 target=_blank /></a>
|
||||||
<a href='https://flathub.org/apps/details/org.kde.neochat'><img width='190px' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-i-en.png'/></a>
|
<a href='https://flathub.org/apps/details/org.kde.neochat'><img width='190px' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-i-en.png'/></a>
|
||||||
<a href='https://snapcraft.io/neochat'><img width='190px' alt='Download on the Snap Store' src='https://snapcraft.io/static/images/badges/en/snap-store-black.svg'/></a>
|
<a href='https://snapcraft.io/neochat'><img width='190px' alt='Download on the Snap Store' src='https://apps.kde.org/store_badges/snapstore/en.svg'/></a>
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.4.1'
|
classpath 'com.android.tools.build:gradle:8.6.0'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,26 @@
|
|||||||
"!room_id_1234:localhost:1234": {
|
"!room_id_1234:localhost:1234": {
|
||||||
"state": {
|
"state": {
|
||||||
"events": [
|
"events": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"m.federate": true,
|
||||||
|
"predecessor": {
|
||||||
|
"event_id": "$something:example.org",
|
||||||
|
"room_id": "!oldroom:example.org"
|
||||||
|
},
|
||||||
|
"room_version": "11"
|
||||||
|
},
|
||||||
|
"event_id": "$143273582443PhrSn:example.org",
|
||||||
|
"origin_server_ts": 1432735824653,
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||||
|
"sender": "@example:example.org",
|
||||||
|
"state_key": "",
|
||||||
|
"type": "m.room.create",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 1234,
|
||||||
|
"membership": "join"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "m.room.member",
|
"type": "m.room.member",
|
||||||
"state_key": "@user:localhost:1234",
|
"state_key": "@user:localhost:1234",
|
||||||
@@ -26,6 +46,26 @@
|
|||||||
},
|
},
|
||||||
"timeline": {
|
"timeline": {
|
||||||
"events": [
|
"events": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"m.federate": true,
|
||||||
|
"predecessor": {
|
||||||
|
"event_id": "$something:example.org",
|
||||||
|
"room_id": "!oldroom:example.org"
|
||||||
|
},
|
||||||
|
"room_version": "11"
|
||||||
|
},
|
||||||
|
"event_id": "$143273582443PhrSn:example.org",
|
||||||
|
"origin_server_ts": 1432735824653,
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||||
|
"sender": "@example:example.org",
|
||||||
|
"state_key": "",
|
||||||
|
"type": "m.room.create",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 1234,
|
||||||
|
"membership": "join"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "m.room.message",
|
"type": "m.room.message",
|
||||||
"sender": "@user:localhost:1234",
|
"sender": "@user:localhost:1234",
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ ecm_add_test(
|
|||||||
TEST_NAME neochatroomtest
|
TEST_NAME neochatroomtest
|
||||||
)
|
)
|
||||||
|
|
||||||
ecm_add_test(
|
# ecm_add_test(
|
||||||
texthandlertest.cpp
|
# texthandlertest.cpp
|
||||||
LINK_LIBRARIES neochat Qt::Test
|
# LINK_LIBRARIES neochat Qt::Test
|
||||||
TEST_NAME texthandlertest
|
# TEST_NAME texthandlertest
|
||||||
)
|
# )
|
||||||
|
|
||||||
ecm_add_test(
|
ecm_add_test(
|
||||||
delegatesizehelpertest.cpp
|
delegatesizehelpertest.cpp
|
||||||
@@ -53,12 +53,6 @@ ecm_add_test(
|
|||||||
TEST_NAME messageeventmodeltest
|
TEST_NAME messageeventmodeltest
|
||||||
)
|
)
|
||||||
|
|
||||||
ecm_add_test(
|
|
||||||
actionshandlertest.cpp
|
|
||||||
LINK_LIBRARIES neochat Qt::Test
|
|
||||||
TEST_NAME actionshandlertest
|
|
||||||
)
|
|
||||||
|
|
||||||
ecm_add_test(
|
ecm_add_test(
|
||||||
windowcontrollertest.cpp
|
windowcontrollertest.cpp
|
||||||
LINK_LIBRARIES neochat Qt::Test
|
LINK_LIBRARIES neochat Qt::Test
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
|
||||||
|
|
||||||
#include <QTest>
|
|
||||||
|
|
||||||
#include "actionshandler.h"
|
|
||||||
#include "chatbarcache.h"
|
|
||||||
|
|
||||||
#include "testutils.h"
|
|
||||||
|
|
||||||
class ActionsHandlerTest : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
private:
|
|
||||||
Quotient::Connection *connection = Quotient::Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void nullObject();
|
|
||||||
};
|
|
||||||
|
|
||||||
void ActionsHandlerTest::nullObject()
|
|
||||||
{
|
|
||||||
QTest::ignoreMessage(QtWarningMsg, "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
|
|
||||||
ActionsHandler::handleMessageEvent(nullptr, nullptr);
|
|
||||||
|
|
||||||
auto chatBarCache = new ChatBarCache(this);
|
|
||||||
QTest::ignoreMessage(QtWarningMsg, "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
|
|
||||||
ActionsHandler::handleMessageEvent(nullptr, chatBarCache);
|
|
||||||
|
|
||||||
auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"));
|
|
||||||
QTest::ignoreMessage(QtWarningMsg, "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
|
|
||||||
ActionsHandler::handleMessageEvent(room, nullptr);
|
|
||||||
|
|
||||||
// The final one should throw no warning so we make sure.
|
|
||||||
QTest::failOnWarning("ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
|
|
||||||
ActionsHandler::handleMessageEvent(room, chatBarCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
QTEST_GUILESS_MAIN(ActionsHandlerTest)
|
|
||||||
#include "actionshandlertest.moc"
|
|
||||||
@@ -63,6 +63,7 @@ private Q_SLOTS:
|
|||||||
void receiveRichEdited();
|
void receiveRichEdited();
|
||||||
void receiveLineSeparator();
|
void receiveLineSeparator();
|
||||||
void receiveRichCodeUrl();
|
void receiveRichCodeUrl();
|
||||||
|
void receiveRichColor();
|
||||||
|
|
||||||
void componentOutput_data();
|
void componentOutput_data();
|
||||||
void componentOutput();
|
void componentOutput();
|
||||||
@@ -520,6 +521,25 @@ void TextHandlerTest::receiveRichCodeUrl()
|
|||||||
QCOMPARE(testTextHandler.handleRecieveRichText(), input);
|
QCOMPARE(testTextHandler.handleRecieveRichText(), input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextHandlerTest::receiveRichColor()
|
||||||
|
{
|
||||||
|
const QString testInputString = QStringLiteral(
|
||||||
|
"<span data-mx-color=\"#ff00be\">¯</span><span data-mx-color=\"#ff3b1d\">\\</span><span data-mx-color=\"#ffa600\">_</span><span "
|
||||||
|
"data-mx-color=\"#64d200\">(</span><span data-mx-color=\"#00e261\">ツ</span><span data-mx-color=\"#00e7ff\">)</span><span "
|
||||||
|
"data-mx-color=\"#00e1ff\">_</span><span data-mx-color=\"#00bdff\">/</span><span data-mx-color=\"#ff60ff\">¯</span>");
|
||||||
|
const QString testOutputString = QStringLiteral(
|
||||||
|
"<span style=\"color: #ff00be;\">¯</span><span style=\"color: #ff3b1d;\">\\</span><span style=\"color: #ffa600;\">_</span><span style=\"color: "
|
||||||
|
"#64d200;\">(</span><span style=\"color: #00e261;\">ツ</span><span style=\"color: #00e7ff;\">)</span><span style=\"color: #00e1ff;\">_</span><span "
|
||||||
|
"style=\"color: #00bdff;\">/</span><span style=\"color: #ff60ff;\">¯</span>");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
qInfo() << testTextHandler.handleRecieveRichText();
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||||
|
}
|
||||||
|
|
||||||
void TextHandlerTest::componentOutput_data()
|
void TextHandlerTest::componentOutput_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<QString>("testInputString");
|
QTest::addColumn<QString>("testInputString");
|
||||||
@@ -535,7 +555,7 @@ void TextHandlerTest::componentOutput_data()
|
|||||||
QVariantMap{{QStringLiteral("class"), QStringLiteral("html")}}}};
|
QVariantMap{{QStringLiteral("class"), QStringLiteral("html")}}}};
|
||||||
QTest::newRow("quote") << QStringLiteral("<p>Text</p>\n<blockquote>\n<p>blockquote</p>\n</blockquote>")
|
QTest::newRow("quote") << QStringLiteral("<p>Text</p>\n<blockquote>\n<p>blockquote</p>\n</blockquote>")
|
||||||
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}},
|
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}},
|
||||||
MessageComponent{MessageComponentType::Quote, QStringLiteral("\"blockquote\""), {}}};
|
MessageComponent{MessageComponentType::Quote, QStringLiteral("“blockquote”"), {}}};
|
||||||
QTest::newRow("no tag first paragraph") << QStringLiteral("Text\n<p>Text</p>")
|
QTest::newRow("no tag first paragraph") << QStringLiteral("Text\n<p>Text</p>")
|
||||||
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}},
|
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}},
|
||||||
MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}}};
|
MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}}};
|
||||||
|
|||||||
@@ -50,38 +50,33 @@
|
|||||||
<name xml:lang="x-test">xxNeoChatxx</name>
|
<name xml:lang="x-test">xxNeoChatxx</name>
|
||||||
<name xml:lang="zh-CN">NeoChat</name>
|
<name xml:lang="zh-CN">NeoChat</name>
|
||||||
<name xml:lang="zh-TW">NeoChat</name>
|
<name xml:lang="zh-TW">NeoChat</name>
|
||||||
<summary>Chat with your friends on matrix</summary>
|
<summary>Chat on Matrix</summary>
|
||||||
<summary xml:lang="ar">دردش مع أصدقائك على ماتركس</summary>
|
<summary xml:lang="ar">دردش على ماتركس</summary>
|
||||||
<summary xml:lang="ca">Xategeu amb els vostres amics a Matrix</summary>
|
<summary xml:lang="ca">Xat a Matrix</summary>
|
||||||
<summary xml:lang="ca-valencia">Xategeu amb els vostres amics a Matrix</summary>
|
<summary xml:lang="ca-valencia">Xat a Matrix</summary>
|
||||||
<summary xml:lang="cs">Mluvte se svými přáteli na Matrixu</summary>
|
<summary xml:lang="de">Über Matrix unterhalten</summary>
|
||||||
<summary xml:lang="de">Mit den Freunden auf Matrix unterhalten</summary>
|
<summary xml:lang="en-GB">Chat on Matrix</summary>
|
||||||
<summary xml:lang="el">Συνομιλήστε με τους φίλους σας στο matrix</summary>
|
<summary xml:lang="eo">Babilo en Matrix</summary>
|
||||||
<summary xml:lang="en-GB">Chat with your friends on matrix</summary>
|
<summary xml:lang="es">Charle en Matrix</summary>
|
||||||
<summary xml:lang="eo">Babilu kun viaj amikoj sur matrix</summary>
|
<summary xml:lang="eu">Berriketa Matrix-en</summary>
|
||||||
<summary xml:lang="es">Charle con sus amigos en matrix</summary>
|
<summary xml:lang="fi">Keskustelu Matrixissä</summary>
|
||||||
<summary xml:lang="eu">Berriketan jardun zure lagunekin «Matrix»en</summary>
|
<summary xml:lang="fr">Discuter sur Matrix</summary>
|
||||||
<summary xml:lang="fi">Keskustelu ystäviesi kanssa Matrixissa</summary>
|
<summary xml:lang="gl">Charlar en Matrix</summary>
|
||||||
<summary xml:lang="fr">Discuter avec vos ami(e)s sur le réseau Matrix</summary>
|
<summary xml:lang="he">התכתבות דרך Matrix</summary>
|
||||||
<summary xml:lang="gl">Charle coas súas amizades en Matrix.</summary>
|
<summary xml:lang="hu">Csevegés Matrixon</summary>
|
||||||
<summary xml:lang="he">התכתבות עם החברים שלך ב־matrix</summary>
|
<summary xml:lang="ia">Conversation en ditecto sur Matrix</summary>
|
||||||
<summary xml:lang="hu">Csevegjen barátaival a matrixon</summary>
|
<summary xml:lang="it">Chat su Matrix</summary>
|
||||||
<summary xml:lang="ia">Starta Conversation con tu amicos sur matrix</summary>
|
<summary xml:lang="ka">ისაუბრეთ Matrix-ზე</summary>
|
||||||
<summary xml:lang="it">Conversa con i tuoi contatti su matrix</summary>
|
<summary xml:lang="nl">Chat op Matrix</summary>
|
||||||
<summary xml:lang="ka">ესაუბრეთ მეგობრებს Matrix-ზე</summary>
|
<summary xml:lang="nn">Prat med via Matrix</summary>
|
||||||
<summary xml:lang="ko">Matrix를 사용하여 친구들과 대화하기</summary>
|
<summary xml:lang="pl">Rozmawiaj na Matriksie</summary>
|
||||||
<summary xml:lang="lv">Tērzējiet ar saviem draugiem „Matrix“ tīklā</summary>
|
<summary xml:lang="sl">Klepet na Matrixu</summary>
|
||||||
<summary xml:lang="nl">Met uw vrienden chatten op matrix</summary>
|
<summary xml:lang="sv">Chatta på Matrix</summary>
|
||||||
<summary xml:lang="nn">Prat med vennar på Matrix</summary>
|
<summary xml:lang="ta">மேட்ரிக்ஸுக்கான உரையாடல் செயலி</summary>
|
||||||
<summary xml:lang="pl">Rozmawiaj ze swoimi znajomymi w Matriksie</summary>
|
<summary xml:lang="tr">Matrix Üzerinde Sohbet</summary>
|
||||||
<summary xml:lang="sl">Klepet z vašimi prijatelji na matrixu</summary>
|
<summary xml:lang="uk">Спілкування у Matrix</summary>
|
||||||
<summary xml:lang="sv">Chatta med dina vänner på Matrix</summary>
|
<summary xml:lang="x-test">xxChat on Matrixxx</summary>
|
||||||
<summary xml:lang="ta">மேட்ரிக்ஸு மூலம் உங்கள் நண்பர்களிடம் பேசலாம்</summary>
|
<summary xml:lang="zh-TW">在 Matrix 上聊天</summary>
|
||||||
<summary xml:lang="tr">Matrix’te arkadaşlarınızla sohbet edin</summary>
|
|
||||||
<summary xml:lang="uk">Спілкуйтеся з вашими друзями у matrix</summary>
|
|
||||||
<summary xml:lang="x-test">xxChat with your friends on matrixxx</summary>
|
|
||||||
<summary xml:lang="zh-CN">在 Matrix 上与朋友聊天</summary>
|
|
||||||
<summary xml:lang="zh-TW">在 Matrix 上與您的朋友聊天</summary>
|
|
||||||
<description>
|
<description>
|
||||||
<p>NeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.</p>
|
<p>NeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.</p>
|
||||||
<p xml:lang="ar">نيوتشات هو تطبيق دردشة يتيح لك الاستفادة الكاملة من شبكة Matrix. فهو يوفر لك طريقة آمنة لإرسال الرسائل النصية ومقاطع الفيديو والملفات الصوتية إلى عائلتك وزملائك وأصدقائك.</p>
|
<p xml:lang="ar">نيوتشات هو تطبيق دردشة يتيح لك الاستفادة الكاملة من شبكة Matrix. فهو يوفر لك طريقة آمنة لإرسال الرسائل النصية ومقاطع الفيديو والملفات الصوتية إلى عائلتك وزملائك وأصدقائك.</p>
|
||||||
@@ -105,6 +100,7 @@
|
|||||||
<p xml:lang="nl">NeoChat is een chat-toepassing die u het volledige voordeel van het Matrix-netwerk laat genieten. Het levert u op een veilige manier tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden te verzenden.</p>
|
<p xml:lang="nl">NeoChat is een chat-toepassing die u het volledige voordeel van het Matrix-netwerk laat genieten. Het levert u op een veilige manier tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden te verzenden.</p>
|
||||||
<p xml:lang="nn">NeoChat er ein prateapp som lèt deg bruka all funksjonalitet i Matrix-nettverket. Du kan utveksla tekst, lyd og videoar med vennar, familie og kollegaar på ein trygg måte.</p>
|
<p xml:lang="nn">NeoChat er ein prateapp som lèt deg bruka all funksjonalitet i Matrix-nettverket. Du kan utveksla tekst, lyd og videoar med vennar, familie og kollegaar på ein trygg måte.</p>
|
||||||
<p xml:lang="pl">NoeChat to aplikacja do rozmów, która umożliwia wykorzystanie wszystkich możliwości Matriksa. Umożliwia wysyłanie wiadomości tekstowych, filmów i dźwięków w bezpieczny sposób do twojej rodziny, kolegów i przyjaciół.</p>
|
<p xml:lang="pl">NoeChat to aplikacja do rozmów, która umożliwia wykorzystanie wszystkich możliwości Matriksa. Umożliwia wysyłanie wiadomości tekstowych, filmów i dźwięków w bezpieczny sposób do twojej rodziny, kolegów i przyjaciół.</p>
|
||||||
|
<p xml:lang="ru">NeoChat — приложение для общения, предоставляющее все преимущества сети Matrix. С его помощью можно безопасно отправлять текстовые сообщения, видеозаписи и звуковые файлы родственникам, коллегам и друзьям.</p>
|
||||||
<p xml:lang="sl">NeoChat je aplikacija za klepet, ki vam omogoča, da v celoti izkoristite omrežje Matrix. Zagotavlja vam varen način za pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, sodelavcem in prijateljem.</p>
|
<p xml:lang="sl">NeoChat je aplikacija za klepet, ki vam omogoča, da v celoti izkoristite omrežje Matrix. Zagotavlja vam varen način za pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, sodelavcem in prijateljem.</p>
|
||||||
<p xml:lang="sv">NeoChat är ett chattprogram som låter dig dra full nytta av Matrix-nätverket. Det ger dig ett säkert sätt att skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner.</p>
|
<p xml:lang="sv">NeoChat är ett chattprogram som låter dig dra full nytta av Matrix-nätverket. Det ger dig ett säkert sätt att skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner.</p>
|
||||||
<p xml:lang="tr">NeoChat, Matrix ağının tüm özelliklerini kullanan bir sohbet uygulamasıdır. Ailenize, arkadaşlarınıza ve iş arkadaşlarınıza metin iletileri, ses ve video dosyaları göndermenin kolay bir yolunu sunar.</p>
|
<p xml:lang="tr">NeoChat, Matrix ağının tüm özelliklerini kullanan bir sohbet uygulamasıdır. Ailenize, arkadaşlarınıza ve iş arkadaşlarınıza metin iletileri, ses ve video dosyaları göndermenin kolay bir yolunu sunar.</p>
|
||||||
@@ -123,7 +119,7 @@
|
|||||||
<p xml:lang="eu">«NeoChat»ek «Matrix» zehaztapenaren ezaugarri guztiak eskaintzen dituen aplikazio bat izan nahi du. Beraz, egungo zehaztapen egonkorrean dagoen guztiaren euskarria du, VoIP, hariak eta muturren artean zifratzeko salbuespen nabarmenekin. Badira beste ez-betetze txikiago batzuk, «Matrix»en zehaztapena etengabe eboluzioan dagoelako, baina azken helburua zehaztapen osoaren euskarria ematea izaten jarraitzen du.</p>
|
<p xml:lang="eu">«NeoChat»ek «Matrix» zehaztapenaren ezaugarri guztiak eskaintzen dituen aplikazio bat izan nahi du. Beraz, egungo zehaztapen egonkorrean dagoen guztiaren euskarria du, VoIP, hariak eta muturren artean zifratzeko salbuespen nabarmenekin. Badira beste ez-betetze txikiago batzuk, «Matrix»en zehaztapena etengabe eboluzioan dagoelako, baina azken helburua zehaztapen osoaren euskarria ematea izaten jarraitzen du.</p>
|
||||||
<p xml:lang="fi">NeoChat pyrkii olemaan Matrix-määritelmän täysominaisuuksinen sovellus, joten se tukee kaikkea nykyisessä vakaassa määritelmässä muutamaa huomattavaa poikkeusta lukuun ottamatta (VoIP, säikeet ja jotkin piirteet päästä päähän -salauksessa). Joitakin pienempiäkin puutteita on Matrix-määritelmän jatkuvan kehityksen vuoksi, mutta lopputavoitteena on tarjota määritelmän täysi tuki.</p>
|
<p xml:lang="fi">NeoChat pyrkii olemaan Matrix-määritelmän täysominaisuuksinen sovellus, joten se tukee kaikkea nykyisessä vakaassa määritelmässä muutamaa huomattavaa poikkeusta lukuun ottamatta (VoIP, säikeet ja jotkin piirteet päästä päähän -salauksessa). Joitakin pienempiäkin puutteita on Matrix-määritelmän jatkuvan kehityksen vuoksi, mutta lopputavoitteena on tarjota määritelmän täysi tuki.</p>
|
||||||
<p xml:lang="fr">L'objectif de NeoChat est d'être une application complète pour le protocole Matrix. En tant que tel, tout dans la spécification stable actuelle avec les exceptions notables de VoIP, les processus et certains aspects du chiffrement de bout en bout sont pris en charge. Il y a quelques autres petites omissions en raison du fait que la spécification du protocole Matrix est en constante évolution. Cependant, l'objectif reste de fournir un soutien éventuel pour l'ensemble de la spécification.</p>
|
<p xml:lang="fr">L'objectif de NeoChat est d'être une application complète pour le protocole Matrix. En tant que tel, tout dans la spécification stable actuelle avec les exceptions notables de VoIP, les processus et certains aspects du chiffrement de bout en bout sont pris en charge. Il y a quelques autres petites omissions en raison du fait que la spécification du protocole Matrix est en constante évolution. Cependant, l'objectif reste de fournir un soutien éventuel pour l'ensemble de la spécification.</p>
|
||||||
<p xml:lang="gl">NeoChat pretende ser unha aplicación completa para a especificación de Matrix. Coas excepcións de VoIP, conversas fiadas e algúns aspectos da cifraxe de extremo a extremo, a versión estábel segue as especificacións. Existen algunhas outras pequenas omisións debido ao feito de que Matrix está en continua evolución pero a intención é implementar a especificación completa.</p>
|
<p xml:lang="gl">NeoChat pretende ser unha aplicación completa para a especificación de Matrix. Coas excepcións de VoIP, conversas fiadas e algúns aspectos da cifraxe de extremo a extremo, a versión estábel segue as especificacións. Existen algunhas outras pequenas omisións debido ao feito de que Matrix está en continua evolución pero a intención é fornecer compatibilidade coa especificación completa.</p>
|
||||||
<p xml:lang="he">NeoChat מתיימר להיות יישום עתיר יכולות לפי מפרט Matrix. כיוון שזה ייעודו, כל מה שבמפרט היציב עם חריגות משמעותיות כגון VoIP, שרשורים ועוד מגוון היבטים של הצפנה מקצה לקצה נתמכים גם הם. יש מספר השמטות קטן עקב העובדה שהמפרט של Matrix ממשיך להתפתח אך המטרה היא להמשיך לספק תמיכה בסופו של דבר לכל המפרט.</p>
|
<p xml:lang="he">NeoChat מתיימר להיות יישום עתיר יכולות לפי מפרט Matrix. כיוון שזה ייעודו, כל מה שבמפרט היציב עם חריגות משמעותיות כגון VoIP, שרשורים ועוד מגוון היבטים של הצפנה מקצה לקצה נתמכים גם הם. יש מספר השמטות קטן עקב העובדה שהמפרט של Matrix ממשיך להתפתח אך המטרה היא להמשיך לספק תמיכה בסופו של דבר לכל המפרט.</p>
|
||||||
<p xml:lang="hu">A NeoChat célja, hogy a Matrix specifikációnak megfelelő teljes funkcionalitású alkalmazás legyen. Mint ilyen, a jelenlegi stabil specifikáció támogatott a VoIP, a szálak és a végpontok közötti titkosítás egyes elemeinek kivételével. Van még néhány kisebb hiányosság annak köszönhetően, hogy a Matrix specifikáció folyamatosan fejlődik, de végső cél a teljes specifikáció megvalósítása.</p>
|
<p xml:lang="hu">A NeoChat célja, hogy a Matrix specifikációnak megfelelő teljes funkcionalitású alkalmazás legyen. Mint ilyen, a jelenlegi stabil specifikáció támogatott a VoIP, a szálak és a végpontok közötti titkosítás egyes elemeinek kivételével. Van még néhány kisebb hiányosság annak köszönhetően, hogy a Matrix specifikáció folyamatosan fejlődik, de végső cél a teljes specifikáció megvalósítása.</p>
|
||||||
<p xml:lang="ia">NeoChat aspira a esser un application plenmente eminente per le specification de Matrix. Tal como omne cosas in le specification currentemente stabile con le exceptiones notabile de VOIP, threads e alcun aspectos del cryptation End-to-End es supportate. Il ha ltere pauc omissiones, debite al facto que le specification de Matrix es in evolution constante ma le aspiration remane a fornir supporto eventual per le integre specification.</p>
|
<p xml:lang="ia">NeoChat aspira a esser un application plenmente eminente per le specification de Matrix. Tal como omne cosas in le specification currentemente stabile con le exceptiones notabile de VOIP, threads e alcun aspectos del cryptation End-to-End es supportate. Il ha ltere pauc omissiones, debite al facto que le specification de Matrix es in evolution constante ma le aspiration remane a fornir supporto eventual per le integre specification.</p>
|
||||||
@@ -198,6 +194,7 @@
|
|||||||
<li xml:lang="nn">Avstemmingar – MSC3381</li>
|
<li xml:lang="nn">Avstemmingar – MSC3381</li>
|
||||||
<li xml:lang="pl">Ankiety - MSC3381</li>
|
<li xml:lang="pl">Ankiety - MSC3381</li>
|
||||||
<li xml:lang="pt">Inquéritos - MSC3381</li>
|
<li xml:lang="pt">Inquéritos - MSC3381</li>
|
||||||
|
<li xml:lang="ru">Голосования — MSC3381</li>
|
||||||
<li xml:lang="sl">Polls - MSC3381</li>
|
<li xml:lang="sl">Polls - MSC3381</li>
|
||||||
<li xml:lang="sv">Polls - MSC3381</li>
|
<li xml:lang="sv">Polls - MSC3381</li>
|
||||||
<li xml:lang="ta">வாக்கெடுப்புகள் - MSC3381</li>
|
<li xml:lang="ta">வாக்கெடுப்புகள் - MSC3381</li>
|
||||||
@@ -358,8 +355,10 @@
|
|||||||
<caption xml:lang="nl">Ontdek nieuwe gemeenschappen met Matrix-ruimten</caption>
|
<caption xml:lang="nl">Ontdek nieuwe gemeenschappen met Matrix-ruimten</caption>
|
||||||
<caption xml:lang="nn">Oppdag nye fellesskap med Matrix Spaces</caption>
|
<caption xml:lang="nn">Oppdag nye fellesskap med Matrix Spaces</caption>
|
||||||
<caption xml:lang="pl">Odkrywaj nowe społeczności w Przestrzeniach Matriksa</caption>
|
<caption xml:lang="pl">Odkrywaj nowe społeczności w Przestrzeniach Matriksa</caption>
|
||||||
|
<caption xml:lang="ru">Поиск новых сообществ с помощью Matrix Spaces</caption>
|
||||||
<caption xml:lang="sl">Odkrijte nove skupnosti z Matrix Spaces</caption>
|
<caption xml:lang="sl">Odkrijte nove skupnosti z Matrix Spaces</caption>
|
||||||
<caption xml:lang="sv">Upptäck nya gemenskaper med Matrix Spaces</caption>
|
<caption xml:lang="sv">Upptäck nya gemenskaper med Matrix Spaces</caption>
|
||||||
|
<caption xml:lang="ta">மேட்ரிக்ஸு இடங்களின் மூலம் புதிய சமூகங்களைக் கண்டுபிடிக்கலாம்</caption>
|
||||||
<caption xml:lang="tr">Matrix Alanlar ile yeni topluluklar keşfedin</caption>
|
<caption xml:lang="tr">Matrix Alanlar ile yeni topluluklar keşfedin</caption>
|
||||||
<caption xml:lang="uk">Пошук нових спільнот за допомогою Matrix Spaces</caption>
|
<caption xml:lang="uk">Пошук нових спільнот за допомогою Matrix Spaces</caption>
|
||||||
<caption xml:lang="x-test">xxDiscover new communities with Matrix Spacesxx</caption>
|
<caption xml:lang="x-test">xxDiscover new communities with Matrix Spacesxx</caption>
|
||||||
@@ -448,6 +447,11 @@
|
|||||||
<content_attribute id="social-chat">intense</content_attribute>
|
<content_attribute id="social-chat">intense</content_attribute>
|
||||||
</content_rating>
|
</content_rating>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="24.12.3" date="2025-03-06"/>
|
||||||
|
<release version="24.12.2" date="2025-02-06"/>
|
||||||
|
<release version="24.12.1" date="2025-01-09"/>
|
||||||
|
<release version="24.12.0" date="2024-12-12"/>
|
||||||
|
<release version="24.08.3" date="2024-11-07"/>
|
||||||
<release version="24.08.2" date="2024-10-10"/>
|
<release version="24.08.2" date="2024-10-10"/>
|
||||||
<release version="24.08.1" date="2024-09-12"/>
|
<release version="24.08.1" date="2024-09-12"/>
|
||||||
<release version="24.08.0" date="2024-08-22"/>
|
<release version="24.08.0" date="2024-08-22"/>
|
||||||
|
|||||||
@@ -87,47 +87,33 @@ GenericName[uk]=Клієнт Matrix
|
|||||||
GenericName[x-test]=xxMatrix Clientxx
|
GenericName[x-test]=xxMatrix Clientxx
|
||||||
GenericName[zh_CN]=Matrix 客户端
|
GenericName[zh_CN]=Matrix 客户端
|
||||||
GenericName[zh_TW]=Matrix 用戶端
|
GenericName[zh_TW]=Matrix 用戶端
|
||||||
Comment=Client for the Matrix protocol
|
Comment=Chat on Matrix
|
||||||
Comment[ar]=عميل لميفاق ماتركس
|
Comment[ar]=دردش على ماتركس
|
||||||
Comment[az]=Matrix protokolu üçün müştəri
|
Comment[ca]=Xat a Matrix
|
||||||
Comment[ca]=Client per al protocol Matrix
|
Comment[ca@valencia]=Xat a Matrix
|
||||||
Comment[ca@valencia]=Client per al protocol Matrix
|
Comment[de]=Über Matrix unterhalten
|
||||||
Comment[de]=Programm für das Matrix-Protokoll
|
Comment[en_GB]=Chat on Matrix
|
||||||
Comment[el]=Πελάτης για το πρωτόκολλο Matrix
|
Comment[eo]=Babilo en Matrix
|
||||||
Comment[en_GB]=Client for the Matrix protocol
|
Comment[es]=Chat en Matrix
|
||||||
Comment[eo]=Kliento por la Matrix-protokolo
|
Comment[eu]=Berriketa Matrix-en
|
||||||
Comment[es]=Cliente para el protocolo Matrix
|
Comment[fi]=Keskustele Matrixissä
|
||||||
Comment[eu]=Matrix protokolorako bezeroa
|
Comment[fr]=Clavarder sur Matrix
|
||||||
Comment[fi]=Asiakas Matrix-yhteyskäytännölle
|
Comment[gl]=Charle en Matrix
|
||||||
Comment[fr]=Client pour le protocole « Matrix »
|
Comment[he]=התכתבות דרך Matrix
|
||||||
Comment[gl]=Cliente para o protocolo Matrix.
|
Comment[hu]=Csevegés Matrixon
|
||||||
Comment[he]=לקוח לפרוטוקול Matrix
|
Comment[ia]=Conversation en ditecto sur Matrix
|
||||||
Comment[hu]=Kliens a Matrix protokollhoz
|
Comment[it]= su Matrix
|
||||||
Comment[ia]=Cliente per le protocollo de Matrix
|
Comment[ka]=საუბარი Matrix-ზე
|
||||||
Comment[id]=Klien untuk protokol Matrix
|
Comment[nl]=Chat op Matrix
|
||||||
Comment[ie]=Un cliente del protocol Matrix
|
Comment[pl]=Rozmawiaj na Matriksie
|
||||||
Comment[it]=Client per il protocollo Matrix
|
Comment[pt_BR]=Bate papo na Matrix
|
||||||
Comment[ka]=კლიენტი Matrix-ის პროტოკოლისთვის
|
Comment[sl]=Klepet na Matrixu
|
||||||
Comment[ko]=Matrix 프로토콜용 클라이언트
|
Comment[sv]=Chatta på Matrix
|
||||||
Comment[lt]=Matrix protokolo kliento programa
|
Comment[ta]=மேட்ரிக்ஸில் உரையாட உதவும்
|
||||||
Comment[lv]=Klients „Matrix“ protokolam
|
Comment[tr]=Matrix üzerinde sohbet edin
|
||||||
Comment[nl]=Client voor het Matrix-protocol
|
Comment[uk]=Спілкування у Matrix
|
||||||
Comment[nn]=Klient for Matrix-protokollen
|
Comment[zh_CN]=在 Matrix 上聊天
|
||||||
Comment[pa]=ਮੈਟਰਿਕਸ ਪਰੋਟੋਕਾਲ ਲਈ ਕਲਾਈਂਟ ਹੈ
|
Comment[zh_TW]=在 Matrix 上聊天
|
||||||
Comment[pl]=Program obsługi protokołu Matriksa
|
|
||||||
Comment[pt]=Cliente para o protocolo Matrix
|
|
||||||
Comment[pt_BR]=Cliente para o protocolo Matrix
|
|
||||||
Comment[ro]=Client pentru protocolul Matrix
|
|
||||||
Comment[ru]=Клиент для протокола Matrix
|
|
||||||
Comment[sk]=Klient protokolu Matrix
|
|
||||||
Comment[sl]=Odjemalec za protokol Matrix
|
|
||||||
Comment[sv]=Klient för protokollet Matrix
|
|
||||||
Comment[ta]=Matrix நெறிமுறைக்கான வாங்கி
|
|
||||||
Comment[tr]=Matrix protokolü için istemci
|
|
||||||
Comment[uk]=Клієнт протоколу Matrix
|
|
||||||
Comment[x-test]=xxClient for the Matrix protocolxx
|
|
||||||
Comment[zh_CN]=为 Matrix 协议打造的客户端
|
|
||||||
Comment[zh_TW]=Matrix 通訊協定的用戶端
|
|
||||||
MimeType=x-scheme-handler/matrix;
|
MimeType=x-scheme-handler/matrix;
|
||||||
Exec=neochat %u
|
Exec=neochat %u
|
||||||
Terminal=false
|
Terminal=false
|
||||||
|
|||||||
886
po/ar/neochat.po
886
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
885
po/az/neochat.po
885
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
1170
po/ca/neochat.po
1170
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
874
po/cs/neochat.po
874
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
884
po/da/neochat.po
884
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
1236
po/de/neochat.po
1236
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
885
po/el/neochat.po
885
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
966
po/eo/neochat.po
966
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
884
po/es/neochat.po
884
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
946
po/eu/neochat.po
946
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
2161
po/fi/neochat.po
2161
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
896
po/fr/neochat.po
896
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
983
po/gl/neochat.po
983
po/gl/neochat.po
File diff suppressed because it is too large
Load Diff
932
po/hu/neochat.po
932
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
882
po/ia/neochat.po
882
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
885
po/id/neochat.po
885
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
885
po/ie/neochat.po
885
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
888
po/it/neochat.po
888
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
865
po/ja/neochat.po
865
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
882
po/ka/neochat.po
882
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
883
po/ko/neochat.po
883
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
865
po/lt/neochat.po
865
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
883
po/lv/neochat.po
883
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
890
po/nl/neochat.po
890
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
999
po/nn/neochat.po
999
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
885
po/pa/neochat.po
885
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
892
po/pl/neochat.po
892
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
885
po/pt/neochat.po
885
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2032
po/ru/neochat.po
2032
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
885
po/sk/neochat.po
885
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
882
po/sl/neochat.po
882
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
885
po/sv/neochat.po
885
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
1231
po/ta/neochat.po
1231
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1110
po/tr/neochat.po
1110
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
884
po/uk/neochat.po
884
po/uk/neochat.po
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
@@ -3,7 +3,7 @@
|
|||||||
# SPDX-License-Identifier: CC0-1.0
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
---
|
---
|
||||||
name: neochat
|
name: neochat
|
||||||
base: core22
|
base: core24
|
||||||
adopt-info: neochat
|
adopt-info: neochat
|
||||||
grade: stable
|
grade: stable
|
||||||
confinement: strict
|
confinement: strict
|
||||||
@@ -27,6 +27,10 @@ apps:
|
|||||||
|
|
||||||
compression: lzo
|
compression: lzo
|
||||||
|
|
||||||
|
package-repositories:
|
||||||
|
- type: apt
|
||||||
|
ppa: ubuntu-toolchain-r/test
|
||||||
|
|
||||||
slots:
|
slots:
|
||||||
session-dbus-interface:
|
session-dbus-interface:
|
||||||
interface: dbus
|
interface: dbus
|
||||||
@@ -76,6 +80,7 @@ parts:
|
|||||||
source-depth: 1
|
source-depth: 1
|
||||||
plugin: cmake
|
plugin: cmake
|
||||||
build-environment:
|
build-environment:
|
||||||
|
- PATH: /snap/bin:${PATH}
|
||||||
- PKG_CONFIG_PATH: $CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET/pkgconfig:$PKG_CONFIG_PATH
|
- PKG_CONFIG_PATH: $CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET/pkgconfig:$PKG_CONFIG_PATH
|
||||||
cmake-parameters:
|
cmake-parameters:
|
||||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
@@ -92,9 +97,13 @@ parts:
|
|||||||
- olm
|
- olm
|
||||||
- qtkeychain
|
- qtkeychain
|
||||||
source: https://github.com/quotient-im/libQuotient.git
|
source: https://github.com/quotient-im/libQuotient.git
|
||||||
source-tag: 0.8.2
|
source-tag: 0.9.1
|
||||||
source-depth: 1
|
source-depth: 1
|
||||||
plugin: cmake
|
plugin: cmake
|
||||||
|
build-environment:
|
||||||
|
- PATH: /snap/bin:${PATH}
|
||||||
|
build-snaps:
|
||||||
|
- cmake
|
||||||
build-packages:
|
build-packages:
|
||||||
- libssl-dev
|
- libssl-dev
|
||||||
cmake-parameters:
|
cmake-parameters:
|
||||||
@@ -113,6 +122,10 @@ parts:
|
|||||||
source-tag: 'v0.3.0'
|
source-tag: 'v0.3.0'
|
||||||
source-depth: 1
|
source-depth: 1
|
||||||
plugin: cmake
|
plugin: cmake
|
||||||
|
build-environment:
|
||||||
|
- PATH: /snap/bin:${PATH}
|
||||||
|
- PYTHONPATH: ${CRAFT_STAGE}/lib/python3.12/site-packages:${CRAFT_STAGE}/usr/lib/python3/dist-packages
|
||||||
|
- LD_LIBRARY_PATH: "/snap/mesa-2404/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/kde-qt6-core24-sdk/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libproxy:$LD_LIBRARY_PATH"
|
||||||
cmake-parameters:
|
cmake-parameters:
|
||||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
- -DCMAKE_BUILD_TYPE=Release
|
- -DCMAKE_BUILD_TYPE=Release
|
||||||
@@ -130,9 +143,12 @@ parts:
|
|||||||
- kquickimageeditor
|
- kquickimageeditor
|
||||||
parse-info:
|
parse-info:
|
||||||
- usr/share/metainfo/org.kde.neochat.appdata.xml
|
- usr/share/metainfo/org.kde.neochat.appdata.xml
|
||||||
source: https://invent.kde.org/network/neochat.git
|
source: .
|
||||||
source-tag: 'v24.08.1'
|
|
||||||
plugin: cmake
|
plugin: cmake
|
||||||
|
build-environment:
|
||||||
|
- PATH: /snap/bin:${PATH}
|
||||||
|
- PYTHONPATH: ${CRAFT_STAGE}/lib/python3.12/site-packages:${CRAFT_STAGE}/usr/lib/python3/dist-packages
|
||||||
|
- LD_LIBRARY_PATH: "/snap/mesa-2404/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/kde-qt6-core24-sdk/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libproxy:$LD_LIBRARY_PATH"
|
||||||
build-packages:
|
build-packages:
|
||||||
- cmark
|
- cmark
|
||||||
- libcmark-dev
|
- libcmark-dev
|
||||||
@@ -156,3 +172,12 @@ parts:
|
|||||||
prime:
|
prime:
|
||||||
- usr/lib/*/libcmark.so*
|
- usr/lib/*/libcmark.so*
|
||||||
|
|
||||||
|
gpu-2404:
|
||||||
|
after: [neochat]
|
||||||
|
source: https://github.com/canonical/gpu-snap.git
|
||||||
|
plugin: dump
|
||||||
|
override-prime: |
|
||||||
|
craftctl default
|
||||||
|
${CRAFT_PART_SRC}/bin/gpu-2404-cleanup mesa-2404
|
||||||
|
prime:
|
||||||
|
- bin/gpu-2404-wrapper
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ endif()
|
|||||||
add_library(neochat STATIC
|
add_library(neochat STATIC
|
||||||
controller.cpp
|
controller.cpp
|
||||||
controller.h
|
controller.h
|
||||||
actionshandler.cpp
|
|
||||||
actionshandler.h
|
|
||||||
models/emojimodel.cpp
|
models/emojimodel.cpp
|
||||||
models/emojimodel.h
|
models/emojimodel.h
|
||||||
emojitones.cpp
|
emojitones.cpp
|
||||||
@@ -202,6 +200,12 @@ set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
|||||||
QT_QML_SINGLETON_TYPE TRUE
|
QT_QML_SINGLETON_TYPE TRUE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(ANDROID OR WIN32)
|
||||||
|
set_source_files_properties(qml/ShareActionStub.qml PROPERTIES
|
||||||
|
QT_QML_SOURCE_TYPENAME ShareAction
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
||||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat
|
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat
|
||||||
QML_FILES
|
QML_FILES
|
||||||
@@ -313,13 +317,9 @@ if(NOT ANDROID AND NOT WIN32)
|
|||||||
qml/EditMenu.qml
|
qml/EditMenu.qml
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
set_source_files_properties(qml/ShareActionStub.qml PROPERTIES
|
|
||||||
QT_RESOURCE_ALIAS qml/ShareAction.qml
|
|
||||||
)
|
|
||||||
qt_target_qml_sources(neochat QML_FILES qml/ShareActionStub.qml)
|
qt_target_qml_sources(neochat QML_FILES qml/ShareActionStub.qml)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
configure_file(config-neochat.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-neochat.h)
|
configure_file(config-neochat.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-neochat.h)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
@@ -418,6 +418,7 @@ target_link_libraries(neochat PUBLIC
|
|||||||
KF6::ConfigGui
|
KF6::ConfigGui
|
||||||
KF6::CoreAddons
|
KF6::CoreAddons
|
||||||
KF6::SonnetCore
|
KF6::SonnetCore
|
||||||
|
KF6::IconThemes
|
||||||
KF6::ColorScheme
|
KF6::ColorScheme
|
||||||
KF6::ItemModels
|
KF6::ItemModels
|
||||||
QuotientQt6
|
QuotientQt6
|
||||||
@@ -491,6 +492,7 @@ if(ANDROID)
|
|||||||
"network-connect"
|
"network-connect"
|
||||||
"list-remove-user"
|
"list-remove-user"
|
||||||
"org.kde.neochat"
|
"org.kde.neochat"
|
||||||
|
"org.kde.neochat.tray"
|
||||||
"preferences-system-users"
|
"preferences-system-users"
|
||||||
"preferences-desktop-theme-global"
|
"preferences-desktop-theme-global"
|
||||||
"notifications"
|
"notifications"
|
||||||
@@ -528,11 +530,13 @@ if(ANDROID)
|
|||||||
"object-rotate-left"
|
"object-rotate-left"
|
||||||
"object-rotate-right"
|
"object-rotate-right"
|
||||||
"add-subtitle"
|
"add-subtitle"
|
||||||
|
"security-high"
|
||||||
"security-low"
|
"security-low"
|
||||||
"security-low-symbolic"
|
"security-low-symbolic"
|
||||||
"kde"
|
"kde"
|
||||||
"list-remove-symbolic"
|
"list-remove-symbolic"
|
||||||
"edit-delete"
|
"edit-delete"
|
||||||
|
"user-home-symbolic"
|
||||||
)
|
)
|
||||||
ecm_add_android_apk(neochat-app ANDROID_DIR ${CMAKE_SOURCE_DIR}/android)
|
ecm_add_android_apk(neochat-app ANDROID_DIR ${CMAKE_SOURCE_DIR}/android)
|
||||||
else()
|
else()
|
||||||
|
|||||||
@@ -1,140 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
|
|
||||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include "actionshandler.h"
|
|
||||||
|
|
||||||
#include "chatbarcache.h"
|
|
||||||
#include "models/actionsmodel.h"
|
|
||||||
#include "neochatconfig.h"
|
|
||||||
#include "texthandler.h"
|
|
||||||
|
|
||||||
using namespace Quotient;
|
|
||||||
using namespace Qt::StringLiterals;
|
|
||||||
|
|
||||||
void ActionsHandler::handleMessageEvent(NeoChatRoom *room, ChatBarCache *chatBarCache)
|
|
||||||
{
|
|
||||||
if (room == nullptr || chatBarCache == nullptr) {
|
|
||||||
qWarning() << "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!chatBarCache->attachmentPath().isEmpty()) {
|
|
||||||
QUrl url(chatBarCache->attachmentPath());
|
|
||||||
auto path = url.isLocalFile() ? url.toLocalFile() : url.toString();
|
|
||||||
room->uploadFile(QUrl(path), chatBarCache->text().isEmpty() ? path.mid(path.lastIndexOf(u'/') + 1) : chatBarCache->text());
|
|
||||||
chatBarCache->setAttachmentPath({});
|
|
||||||
chatBarCache->setText({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto handledText = handleMentions(chatBarCache);
|
|
||||||
const auto result = handleQuickEdit(room, handledText);
|
|
||||||
if (!result) {
|
|
||||||
handleMessage(room, handledText, chatBarCache);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ActionsHandler::handleMentions(ChatBarCache *chatBarCache)
|
|
||||||
{
|
|
||||||
const auto mentions = chatBarCache->mentions();
|
|
||||||
std::sort(mentions->begin(), mentions->end(), [](const auto &a, const auto &b) -> bool {
|
|
||||||
return a.cursor.anchor() > b.cursor.anchor();
|
|
||||||
});
|
|
||||||
|
|
||||||
auto handledText = chatBarCache->text();
|
|
||||||
for (const auto &mention : *mentions) {
|
|
||||||
if (mention.text.isEmpty() || mention.id.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
handledText = handledText.replace(mention.cursor.anchor(),
|
|
||||||
mention.cursor.position() - mention.cursor.anchor(),
|
|
||||||
QStringLiteral("[%1](https://matrix.to/#/%2)").arg(mention.text.toHtmlEscaped(), mention.id));
|
|
||||||
}
|
|
||||||
mentions->clear();
|
|
||||||
|
|
||||||
return handledText;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ActionsHandler::handleQuickEdit(NeoChatRoom *room, const QString &handledText)
|
|
||||||
{
|
|
||||||
if (room == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NeoChatConfig::allowQuickEdit()) {
|
|
||||||
QRegularExpression sed(QStringLiteral("^s/([^/]*)/([^/]*)(/g)?$"));
|
|
||||||
auto match = sed.match(handledText);
|
|
||||||
if (match.hasMatch()) {
|
|
||||||
const QString regex = match.captured(1);
|
|
||||||
const QString replacement = match.captured(2).toHtmlEscaped();
|
|
||||||
const QString flags = match.captured(3);
|
|
||||||
|
|
||||||
for (auto it = room->messageEvents().crbegin(); it != room->messageEvents().crend(); it++) {
|
|
||||||
if (const auto event = eventCast<const RoomMessageEvent>(&**it)) {
|
|
||||||
if (event->senderId() == room->localMember().id() && event->hasTextContent()) {
|
|
||||||
QString originalString;
|
|
||||||
if (event->content()) {
|
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
originalString = static_cast<const Quotient::EventContent::TextContent *>(event->content().get())->body;
|
|
||||||
#else
|
|
||||||
originalString = static_cast<const Quotient::EventContent::TextContent *>(event->content())->body;
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
originalString = event->plainBody();
|
|
||||||
}
|
|
||||||
if (flags == "/g"_L1) {
|
|
||||||
room->postHtmlMessage(handledText, originalString.replace(regex, replacement), event->msgtype(), {}, event->id());
|
|
||||||
} else {
|
|
||||||
room->postHtmlMessage(handledText,
|
|
||||||
originalString.replace(originalString.indexOf(regex), regex.size(), replacement),
|
|
||||||
event->msgtype(),
|
|
||||||
{},
|
|
||||||
event->id());
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ActionsHandler::handleMessage(NeoChatRoom *room, QString handledText, ChatBarCache *chatBarCache)
|
|
||||||
{
|
|
||||||
if (room == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto messageType = RoomMessageEvent::MsgType::Text;
|
|
||||||
|
|
||||||
if (handledText.startsWith(QLatin1Char('/'))) {
|
|
||||||
for (const auto &action : ActionsModel::instance().allActions()) {
|
|
||||||
if (handledText.indexOf(action.prefix) == 1
|
|
||||||
&& (handledText.indexOf(" "_ls) == action.prefix.length() + 1 || handledText.length() == action.prefix.length() + 1)) {
|
|
||||||
handledText = action.handle(handledText.mid(action.prefix.length() + 1).trimmed(), room, chatBarCache);
|
|
||||||
if (action.messageType.has_value()) {
|
|
||||||
messageType = *action.messageType;
|
|
||||||
}
|
|
||||||
if (action.messageAction) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TextHandler textHandler;
|
|
||||||
textHandler.setData(handledText);
|
|
||||||
handledText = textHandler.handleSendText();
|
|
||||||
|
|
||||||
if (handledText.length() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
room->postMessage(chatBarCache->text(), handledText, messageType, chatBarCache->replyId(), chatBarCache->editId(), chatBarCache->threadId());
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "moc_actionshandler.cpp"
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
|
|
||||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
class ChatBarCache;
|
|
||||||
class NeoChatRoom;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class ActionsHandler
|
|
||||||
*
|
|
||||||
* This class contains functions to handle chat messages ready for posting to a room.
|
|
||||||
*
|
|
||||||
* Everything that needs to be done to prepare the message for posting in a room
|
|
||||||
* including:
|
|
||||||
* - File handling
|
|
||||||
* - User mentions
|
|
||||||
* - Quick edits
|
|
||||||
* - Chat actions
|
|
||||||
* - Custom emojis
|
|
||||||
*
|
|
||||||
* @note A chat action is a message starting with /, resulting in something other
|
|
||||||
* than a normal message being sent (e.g. /me, /join).
|
|
||||||
*
|
|
||||||
* @sa ActionsModel, NeoChatRoom
|
|
||||||
*/
|
|
||||||
class ActionsHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Pre-process text and send message event.
|
|
||||||
*/
|
|
||||||
static void handleMessageEvent(NeoChatRoom *room, ChatBarCache *chatBarCache);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static QString handleMentions(ChatBarCache *chatBarCache);
|
|
||||||
static bool handleQuickEdit(NeoChatRoom *room, const QString &handledText);
|
|
||||||
|
|
||||||
static void handleMessage(NeoChatRoom *room, QString handledText, ChatBarCache *chatBarCache);
|
|
||||||
};
|
|
||||||
@@ -176,13 +176,14 @@ QQC2.Control {
|
|||||||
RowLayout {
|
RowLayout {
|
||||||
QQC2.ScrollView {
|
QQC2.ScrollView {
|
||||||
id: chatBarScrollView
|
id: chatBarScrollView
|
||||||
|
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||||
|
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
||||||
|
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||||
|
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.maximumHeight: Kirigami.Units.gridUnit * 8
|
Layout.maximumHeight: Kirigami.Units.gridUnit * 8
|
||||||
|
Layout.minimumHeight: Kirigami.Units.gridUnit * 3
|
||||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
|
||||||
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
|
||||||
Layout.minimumHeight: Kirigami.Units.gridUnit * 2
|
|
||||||
|
|
||||||
// HACK: This is to stop the ScrollBar flickering on and off as the height is increased
|
// HACK: This is to stop the ScrollBar flickering on and off as the height is increased
|
||||||
QQC2.ScrollBar.vertical.policy: chatBarHeightAnimation.running && implicitHeight <= height ? QQC2.ScrollBar.AlwaysOff : QQC2.ScrollBar.AsNeeded
|
QQC2.ScrollBar.vertical.policy: chatBarHeightAnimation.running && implicitHeight <= height ? QQC2.ScrollBar.AlwaysOff : QQC2.ScrollBar.AsNeeded
|
||||||
@@ -251,20 +252,22 @@ QQC2.Control {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Keys.onEnterPressed: event => {
|
Keys.onEnterPressed: event => {
|
||||||
|
const controlIsPressed = event.modifiers & Qt.ControlModifier;
|
||||||
if (completionMenu.visible) {
|
if (completionMenu.visible) {
|
||||||
completionMenu.complete();
|
completionMenu.complete();
|
||||||
} else if (event.modifiers & Qt.ShiftModifier || Kirigami.Settings.isMobile) {
|
} else if (event.modifiers & Qt.ShiftModifier || Kirigami.Settings.isMobile || NeoChatConfig.sendMessageWith === 1 && !controlIsPressed || NeoChatConfig.sendMessageWith === 0 && controlIsPressed) {
|
||||||
textField.insert(cursorPosition, "\n");
|
textField.insert(cursorPosition, "\n");
|
||||||
} else {
|
} else if (NeoChatConfig.sendMessageWith === 0 && !controlIsPressed || NeoChatConfig.sendMessageWith === 1 && controlIsPressed) {
|
||||||
_private.postMessage();
|
_private.postMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Keys.onReturnPressed: event => {
|
Keys.onReturnPressed: event => {
|
||||||
|
const controlIsPressed = event.modifiers & Qt.ControlModifier;
|
||||||
if (completionMenu.visible) {
|
if (completionMenu.visible) {
|
||||||
completionMenu.complete();
|
completionMenu.complete();
|
||||||
} else if (event.modifiers & Qt.ShiftModifier || Kirigami.Settings.isMobile) {
|
} else if (event.modifiers & Qt.ShiftModifier || Kirigami.Settings.isMobile || NeoChatConfig.sendMessageWith === 1 && !controlIsPressed || NeoChatConfig.sendMessageWith === 0 && controlIsPressed) {
|
||||||
textField.insert(cursorPosition, "\n");
|
textField.insert(cursorPosition, "\n");
|
||||||
} else {
|
} else if (NeoChatConfig.sendMessageWith === 0 && !controlIsPressed || NeoChatConfig.sendMessageWith === 1 && controlIsPressed) {
|
||||||
_private.postMessage();
|
_private.postMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -318,12 +321,11 @@ QQC2.Control {
|
|||||||
id: actionsRow
|
id: actionsRow
|
||||||
spacing: 0
|
spacing: 0
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
Layout.bottomMargin: Kirigami.Units.smallSpacing * 1.5
|
Layout.bottomMargin: Kirigami.Units.smallSpacing * 4
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: root.actions
|
model: root.actions
|
||||||
delegate: QQC2.ToolButton {
|
delegate: QQC2.ToolButton {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
icon.name: modelData.isBusy ? "" : (modelData.icon.name.length > 0 ? modelData.icon.name : modelData.icon.source)
|
icon.name: modelData.isBusy ? "" : (modelData.icon.name.length > 0 ? modelData.icon.name : modelData.icon.source)
|
||||||
onClicked: modelData.trigger()
|
onClicked: modelData.trigger()
|
||||||
|
|
||||||
@@ -340,7 +342,6 @@ QQC2.Control {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateSizeHelper {
|
DelegateSizeHelper {
|
||||||
id: chatBarSizeHelper
|
id: chatBarSizeHelper
|
||||||
startBreakpoint: Kirigami.Units.gridUnit * 46
|
startBreakpoint: Kirigami.Units.gridUnit * 46
|
||||||
@@ -405,7 +406,6 @@ QQC2.Control {
|
|||||||
repeatTimer.stop();
|
repeatTimer.stop();
|
||||||
root.currentRoom.markAllMessagesAsRead();
|
root.currentRoom.markAllMessagesAsRead();
|
||||||
textField.clear();
|
textField.clear();
|
||||||
_private.chatBarCache.clearRelations();
|
|
||||||
messageSent();
|
messageSent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ QQC2.ItemDelegate {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: root.emoji.startsWith("mxc") || root.isImage
|
visible: root.emoji.startsWith("mxc") || root.isImage
|
||||||
source: visible ? root.emoji : ""
|
source: visible ? root.emoji : ""
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
sourceSize.width: width
|
||||||
|
sourceSize.height: height
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ QQC2.ScrollView {
|
|||||||
|
|
||||||
Kirigami.PlaceholderMessage {
|
Kirigami.PlaceholderMessage {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
icon.name: root.stickers ? "stickers" : "preferences-desktop-emoticons"
|
||||||
text: root.stickers ? i18n("No stickers") : i18n("No emojis")
|
text: root.stickers ? i18n("No stickers") : i18n("No emojis")
|
||||||
visible: emojis.count === 0
|
visible: emojis.count === 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ ColumnLayout {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: root.categoryIconSize + QQC2.ScrollBar.horizontal.height
|
Layout.preferredHeight: root.categoryIconSize + QQC2.ScrollBar.horizontal.height
|
||||||
QQC2.ScrollBar.horizontal.height: QQC2.ScrollBar.horizontal.visible ? QQC2.ScrollBar.horizontal.implicitHeight : 0
|
QQC2.ScrollBar.horizontal.height: QQC2.ScrollBar.horizontal.visible ? QQC2.ScrollBar.horizontal.implicitHeight : 0
|
||||||
|
visible: categories.count !== 0
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: categories
|
id: categories
|
||||||
@@ -201,8 +202,13 @@ ColumnLayout {
|
|||||||
width: root.categoryIconSize
|
width: root.categoryIconSize
|
||||||
height: width
|
height: width
|
||||||
checked: stickerModel.packIndex === model.index
|
checked: stickerModel.packIndex === model.index
|
||||||
|
padding: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
contentItem: Image {
|
contentItem: Image {
|
||||||
source: model.avatarUrl
|
source: model.avatarUrl
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
sourceSize.width: width
|
||||||
|
sourceSize.height: height
|
||||||
}
|
}
|
||||||
QQC2.ToolTip.text: model.name
|
QQC2.ToolTip.text: model.name
|
||||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
|
|||||||
@@ -5,10 +5,11 @@
|
|||||||
|
|
||||||
#include <Quotient/roommember.h>
|
#include <Quotient/roommember.h>
|
||||||
|
|
||||||
#include "actionshandler.h"
|
|
||||||
#include "chatdocumenthandler.h"
|
#include "chatdocumenthandler.h"
|
||||||
#include "eventhandler.h"
|
#include "eventhandler.h"
|
||||||
|
#include "models/actionsmodel.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
|
#include "texthandler.h"
|
||||||
|
|
||||||
ChatBarCache::ChatBarCache(QObject *parent)
|
ChatBarCache::ChatBarCache(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
@@ -29,6 +30,37 @@ void ChatBarCache::setText(const QString &text)
|
|||||||
Q_EMIT textChanged();
|
Q_EMIT textChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString ChatBarCache::sendText() const
|
||||||
|
{
|
||||||
|
if (!attachmentPath().isEmpty()) {
|
||||||
|
QUrl url(attachmentPath());
|
||||||
|
auto path = url.isLocalFile() ? url.toLocalFile() : url.toString();
|
||||||
|
return text().isEmpty() ? path.mid(path.lastIndexOf(u'/') + 1) : text();
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatMentions();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatBarCache::formatMentions() const
|
||||||
|
{
|
||||||
|
auto mentions = m_mentions;
|
||||||
|
std::sort(mentions.begin(), mentions.end(), [](const auto &a, const auto &b) {
|
||||||
|
return a.cursor.anchor() > b.cursor.anchor();
|
||||||
|
});
|
||||||
|
|
||||||
|
auto formattedText = text();
|
||||||
|
for (const auto &mention : mentions) {
|
||||||
|
if (mention.text.isEmpty() || mention.id.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
formattedText = formattedText.replace(mention.cursor.anchor(),
|
||||||
|
mention.cursor.position() - mention.cursor.anchor(),
|
||||||
|
QStringLiteral("[%1](https://matrix.to/#/%2)").arg(mention.text.toHtmlEscaped(), mention.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return formattedText;
|
||||||
|
}
|
||||||
|
|
||||||
bool ChatBarCache::isReplying() const
|
bool ChatBarCache::isReplying() const
|
||||||
{
|
{
|
||||||
return m_relationType == Reply && !m_relationId.isEmpty();
|
return m_relationType == Reply && !m_relationId.isEmpty();
|
||||||
@@ -268,7 +300,36 @@ void ChatBarCache::postMessage()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionsHandler::handleMessageEvent(room, this);
|
if (!attachmentPath().isEmpty()) {
|
||||||
|
room->uploadFile(QUrl(attachmentPath()), sendText());
|
||||||
|
clearCache();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = ActionsModel::handleAction(room, this);
|
||||||
|
if (!result.first.has_value()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextHandler textHandler;
|
||||||
|
textHandler.setData(*std::get<std::optional<QString>>(result));
|
||||||
|
const auto sendText = textHandler.handleSendText();
|
||||||
|
|
||||||
|
if (sendText.length() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto type = std::get<std::optional<Quotient::RoomMessageEvent::MsgType>>(result);
|
||||||
|
room->postMessage(text(), sendText, type ? *type : Quotient::RoomMessageEvent::MsgType::Text, replyId(), editId(), threadId());
|
||||||
|
clearCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatBarCache::clearCache()
|
||||||
|
{
|
||||||
|
setText({});
|
||||||
|
m_mentions.clear();
|
||||||
|
m_savedText = QString();
|
||||||
|
clearRelations();
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "moc_chatbarcache.cpp"
|
#include "moc_chatbarcache.cpp"
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ public:
|
|||||||
explicit ChatBarCache(QObject *parent = nullptr);
|
explicit ChatBarCache(QObject *parent = nullptr);
|
||||||
|
|
||||||
QString text() const;
|
QString text() const;
|
||||||
|
QString sendText() const;
|
||||||
void setText(const QString &text);
|
void setText(const QString &text);
|
||||||
|
|
||||||
bool isReplying() const;
|
bool isReplying() const;
|
||||||
@@ -215,6 +216,8 @@ Q_SIGNALS:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_text = QString();
|
QString m_text = QString();
|
||||||
|
QString formatMentions() const;
|
||||||
|
|
||||||
QString m_relationId = QString();
|
QString m_relationId = QString();
|
||||||
RelationType m_relationType = RelationType::None;
|
RelationType m_relationType = RelationType::None;
|
||||||
QString m_threadId = QString();
|
QString m_threadId = QString();
|
||||||
@@ -223,4 +226,6 @@ private:
|
|||||||
QString m_savedText;
|
QString m_savedText;
|
||||||
|
|
||||||
QPointer<MessageContentModel> m_relationContentModel;
|
QPointer<MessageContentModel> m_relationContentModel;
|
||||||
|
|
||||||
|
void clearCache();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -63,11 +63,7 @@ Controller::Controller(QObject *parent)
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
auto c = new NeoChatConnection(this);
|
auto c = new NeoChatConnection(this);
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
c->assumeIdentity(QStringLiteral("@user:localhost:1234"), QStringLiteral("device_1234"), QStringLiteral("token_1234"));
|
c->assumeIdentity(QStringLiteral("@user:localhost:1234"), QStringLiteral("device_1234"), QStringLiteral("token_1234"));
|
||||||
#else
|
|
||||||
c->assumeIdentity(QStringLiteral("@user:localhost:1234"), QStringLiteral("token_1234"));
|
|
||||||
#endif
|
|
||||||
connect(c, &Connection::connected, this, [c, this]() {
|
connect(c, &Connection::connected, this, [c, this]() {
|
||||||
m_accountRegistry.add(c);
|
m_accountRegistry.add(c);
|
||||||
c->syncLoop();
|
c->syncLoop();
|
||||||
@@ -172,7 +168,6 @@ void Controller::addConnection(NeoChatConnection *c)
|
|||||||
connect(c, &NeoChatConnection::syncDone, this, [this, c]() {
|
connect(c, &NeoChatConnection::syncDone, this, [this, c]() {
|
||||||
m_notificationsManager.handleNotifications(c);
|
m_notificationsManager.handleNotifications(c);
|
||||||
});
|
});
|
||||||
connect(c, &NeoChatConnection::showInviteNotification, &m_notificationsManager, &NotificationsManager::postInviteNotification);
|
|
||||||
|
|
||||||
c->sync();
|
c->sync();
|
||||||
|
|
||||||
@@ -230,14 +225,7 @@ void Controller::invokeLogin()
|
|||||||
Qt::SingleShotConnection);
|
Qt::SingleShotConnection);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connect(connection, &NeoChatConnection::networkError, this, [this](const QString &error, const QString &, int, int) {
|
|
||||||
Q_EMIT errorOccured(i18n("Network Error: %1", error));
|
|
||||||
});
|
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
connection->assumeIdentity(account.userId(), account.deviceId(), accessToken);
|
connection->assumeIdentity(account.userId(), account.deviceId(), accessToken);
|
||||||
#else
|
|
||||||
connection->assumeIdentity(account.userId(), accessToken);
|
|
||||||
#endif
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,7 +294,7 @@ bool Controller::supportSystemTray() const
|
|||||||
void Controller::setQuitOnLastWindowClosed()
|
void Controller::setQuitOnLastWindowClosed()
|
||||||
{
|
{
|
||||||
#ifndef Q_OS_ANDROID
|
#ifndef Q_OS_ANDROID
|
||||||
if (NeoChatConfig::self()->systemTray()) {
|
if (supportSystemTray() && NeoChatConfig::self()->systemTray()) {
|
||||||
m_trayIcon = new TrayIcon(this);
|
m_trayIcon = new TrayIcon(this);
|
||||||
m_trayIcon->show();
|
m_trayIcon->show();
|
||||||
} else {
|
} else {
|
||||||
@@ -434,21 +422,21 @@ void Controller::setTestMode(bool test)
|
|||||||
|
|
||||||
void Controller::removeConnection(const QString &userId)
|
void Controller::removeConnection(const QString &userId)
|
||||||
{
|
{
|
||||||
if (m_connectionsLoading.contains(userId) && m_connectionsLoading[userId]) {
|
// When loadAccessTokenFromKeyChain() fails m_connectionsLoading won't have an
|
||||||
auto connection = m_connectionsLoading[userId];
|
// entry for it so we need to check both separately.
|
||||||
|
if (m_accountsLoading.contains(userId)) {
|
||||||
m_accountsLoading.removeAll(userId);
|
m_accountsLoading.removeAll(userId);
|
||||||
Q_EMIT accountsLoadingChanged();
|
Q_EMIT accountsLoadingChanged();
|
||||||
|
}
|
||||||
|
if (m_connectionsLoading.contains(userId) && m_connectionsLoading[userId]) {
|
||||||
|
auto connection = m_connectionsLoading[userId];
|
||||||
SettingsGroup("Accounts"_ls).remove(userId);
|
SettingsGroup("Accounts"_ls).remove(userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Controller::csSupported() const
|
bool Controller::csSupported() const
|
||||||
{
|
{
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
return true;
|
return true;
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::revertToDefaultConfig()
|
void Controller::revertToDefaultConfig()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls as QQC2
|
import QtQuick.Controls as QQC2
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Window
|
||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kirigamiaddons.formcard as FormCard
|
import org.kde.kirigamiaddons.formcard as FormCard
|
||||||
@@ -23,7 +24,7 @@ ColumnLayout {
|
|||||||
model: root.connection.accountDataEventTypes
|
model: root.connection.accountDataEventTypes
|
||||||
delegate: FormCard.FormButtonDelegate {
|
delegate: FormCard.FormButtonDelegate {
|
||||||
text: modelData
|
text: modelData
|
||||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
onClicked: root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||||
sourceText: root.connection.accountDataJsonString(modelData)
|
sourceText: root.connection.accountDataJsonString(modelData)
|
||||||
}, {
|
}, {
|
||||||
title: i18nc("@title:window", "Event Source"),
|
title: i18nc("@title:window", "Event Source"),
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Window
|
||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kirigamiaddons.formcard as FormCard
|
import org.kde.kirigamiaddons.formcard as FormCard
|
||||||
@@ -47,7 +48,7 @@ ColumnLayout {
|
|||||||
model: root.room.accountDataEventTypes
|
model: root.room.accountDataEventTypes
|
||||||
delegate: FormCard.FormButtonDelegate {
|
delegate: FormCard.FormButtonDelegate {
|
||||||
text: modelData
|
text: modelData
|
||||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
onClicked: root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||||
sourceText: root.room.roomAcountDataJson(text)
|
sourceText: root.room.roomAcountDataJson(text)
|
||||||
}, {
|
}, {
|
||||||
title: i18n("Event Source"),
|
title: i18n("Event Source"),
|
||||||
@@ -77,7 +78,7 @@ ColumnLayout {
|
|||||||
if (model.eventCount === 1) {
|
if (model.eventCount === 1) {
|
||||||
openEventSource(model.type, model.stateKey);
|
openEventSource(model.type, model.stateKey);
|
||||||
} else {
|
} else {
|
||||||
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'StateKeys'), {
|
root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'StateKeys'), {
|
||||||
room: root.room,
|
room: root.room,
|
||||||
eventType: model.type
|
eventType: model.type
|
||||||
}, {
|
}, {
|
||||||
@@ -89,7 +90,7 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
function openEventSource(type: string, stateKey: string): void {
|
function openEventSource(type: string, stateKey: string): void {
|
||||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
onClicked: root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||||
model: stateModel,
|
model: stateModel,
|
||||||
allowEdit: true,
|
allowEdit: true,
|
||||||
room: root.room,
|
room: root.room,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Window
|
||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kirigamiaddons.formcard as FormCard
|
import org.kde.kirigamiaddons.formcard as FormCard
|
||||||
@@ -37,7 +38,7 @@ FormCard.FormCardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openEventSource(stateKey: string): void {
|
function openEventSource(stateKey: string): void {
|
||||||
applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||||
model: stateKeysModel,
|
model: stateKeysModel,
|
||||||
allowEdit: true,
|
allowEdit: true,
|
||||||
room: root.room,
|
room: root.room,
|
||||||
|
|||||||
@@ -225,14 +225,10 @@ QString EventHandler::rawMessageBody(const Quotient::RoomMessageEvent &event)
|
|||||||
{
|
{
|
||||||
QString body;
|
QString body;
|
||||||
|
|
||||||
if (event.hasFileContent()) {
|
if (event.has<EventContent::FileContent>()) {
|
||||||
// if filename is given or body is equal to filename,
|
// if filename is given or body is equal to filename,
|
||||||
// then body is a caption
|
// then body is a caption
|
||||||
#if Quotient_VERSION_MINOR > 8
|
QString filename = event.get<EventContent::FileContent>()->originalName;
|
||||||
QString filename = event.fileContent()->originalName;
|
|
||||||
#else
|
|
||||||
QString filename = event.content()->fileInfo()->originalName;
|
|
||||||
#endif
|
|
||||||
QString body = event.plainBody();
|
QString body = event.plainBody();
|
||||||
if (filename.isEmpty() || filename == body) {
|
if (filename.isEmpty() || filename == body) {
|
||||||
return QString();
|
return QString();
|
||||||
@@ -240,12 +236,8 @@ QString EventHandler::rawMessageBody(const Quotient::RoomMessageEvent &event)
|
|||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.hasTextContent() && event.content()) {
|
if (event.has<EventContent::TextContent>() && event.content()) {
|
||||||
#if Quotient_VERSION_MINOR > 8
|
body = event.get<EventContent::TextContent>()->body;
|
||||||
body = event.richTextContent()->body;
|
|
||||||
#else
|
|
||||||
body = static_cast<const EventContent::TextContent *>(event.content())->body;
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
body = event.plainBody();
|
body = event.plainBody();
|
||||||
}
|
}
|
||||||
@@ -472,12 +464,8 @@ QString EventHandler::getMessageBody(const NeoChatRoom *room, const RoomMessageE
|
|||||||
{
|
{
|
||||||
TextHandler textHandler;
|
TextHandler textHandler;
|
||||||
|
|
||||||
if (event.hasFileContent()) {
|
if (event.has<EventContent::FileContent>()) {
|
||||||
#if Quotient_VERSION_MINOR > 8
|
QString fileCaption = event.get<EventContent::FileContent>()->originalName;
|
||||||
QString fileCaption = event.fileContent()->originalName;
|
|
||||||
#else
|
|
||||||
QString fileCaption = event.content()->fileInfo()->originalName;
|
|
||||||
#endif
|
|
||||||
if (fileCaption.isEmpty()) {
|
if (fileCaption.isEmpty()) {
|
||||||
fileCaption = event.plainBody();
|
fileCaption = event.plainBody();
|
||||||
} else if (fileCaption != event.plainBody()) {
|
} else if (fileCaption != event.plainBody()) {
|
||||||
@@ -488,12 +476,8 @@ QString EventHandler::getMessageBody(const NeoChatRoom *room, const RoomMessageE
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString body;
|
QString body;
|
||||||
if (event.hasTextContent() && event.content()) {
|
if (event.has<EventContent::TextContent>() && event.content()) {
|
||||||
#if Quotient_VERSION_MINOR > 8
|
body = event.get<EventContent::TextContent>()->body;
|
||||||
body = event.richTextContent()->body;
|
|
||||||
#else
|
|
||||||
body = static_cast<const EventContent::TextContent *>(event.content())->body;
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
body = event.plainBody();
|
body = event.plainBody();
|
||||||
}
|
}
|
||||||
@@ -708,25 +692,15 @@ QVariantMap EventHandler::getMediaInfoForEvent(const NeoChatRoom *room, const Qu
|
|||||||
// Get the file info for the event.
|
// Get the file info for the event.
|
||||||
if (event->is<RoomMessageEvent>()) {
|
if (event->is<RoomMessageEvent>()) {
|
||||||
auto roomMessageEvent = eventCast<const RoomMessageEvent>(event);
|
auto roomMessageEvent = eventCast<const RoomMessageEvent>(event);
|
||||||
if (!roomMessageEvent->hasFileContent()) {
|
if (!roomMessageEvent->has<EventContent::FileContentBase>()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
#if Quotient_VERSION_MINOR > 8
|
const auto content = roomMessageEvent->get<EventContent::FileContentBase>();
|
||||||
const auto content = roomMessageEvent->content();
|
QVariantMap mediaInfo = getMediaInfoFromFileInfo(room, content.get(), eventId, false, false);
|
||||||
QVariantMap mediaInfo = getMediaInfoFromFileInfo(room, static_cast<EventContent::FileContent *>(content.get()), eventId, false, false);
|
|
||||||
#else
|
|
||||||
const auto content = static_cast<const EventContent::FileContent *>(roomMessageEvent->content());
|
|
||||||
QVariantMap mediaInfo = getMediaInfoFromFileInfo(room, content, eventId, false, false);
|
|
||||||
#endif
|
|
||||||
// if filename isn't specifically given, it is in body
|
// if filename isn't specifically given, it is in body
|
||||||
// https://spec.matrix.org/latest/client-server-api/#mfile
|
// https://spec.matrix.org/latest/client-server-api/#mfile
|
||||||
#if Quotient_VERSION_MINOR > 8
|
mediaInfo["filename"_ls] = content->commonInfo().originalName.isEmpty() ? roomMessageEvent->plainBody() : content->commonInfo().originalName;
|
||||||
mediaInfo["filename"_ls] =
|
|
||||||
(roomMessageEvent->fileContent()->originalName.isEmpty()) ? roomMessageEvent->plainBody() : roomMessageEvent->fileContent()->originalName;
|
|
||||||
#else
|
|
||||||
mediaInfo["filename"_ls] = (content->fileInfo()->originalName.isEmpty()) ? roomMessageEvent->plainBody() : content->fileInfo()->originalName;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return mediaInfo;
|
return mediaInfo;
|
||||||
} else if (event->is<StickerEvent>()) {
|
} else if (event->is<StickerEvent>()) {
|
||||||
@@ -740,11 +714,7 @@ QVariantMap EventHandler::getMediaInfoForEvent(const NeoChatRoom *room, const Qu
|
|||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap EventHandler::getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
QVariantMap EventHandler::getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
const Quotient::EventContent::FileContentBase *fileContent,
|
const Quotient::EventContent::FileContentBase *fileContent,
|
||||||
#else
|
|
||||||
const Quotient::EventContent::TypedBase *fileContent,
|
|
||||||
#endif
|
|
||||||
const QString &eventId,
|
const QString &eventId,
|
||||||
bool isThumbnail,
|
bool isThumbnail,
|
||||||
bool isSticker)
|
bool isSticker)
|
||||||
@@ -752,18 +722,10 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
|||||||
QVariantMap mediaInfo;
|
QVariantMap mediaInfo;
|
||||||
|
|
||||||
// Get the mxc URL for the media.
|
// Get the mxc URL for the media.
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
if (!fileContent->url().isValid() || fileContent->url().scheme() != QStringLiteral("mxc") || eventId.isEmpty()) {
|
if (!fileContent->url().isValid() || fileContent->url().scheme() != QStringLiteral("mxc") || eventId.isEmpty()) {
|
||||||
#else
|
|
||||||
if (!fileContent->fileInfo()->url().isValid() || fileContent->fileInfo()->url().scheme() != QStringLiteral("mxc") || eventId.isEmpty()) {
|
|
||||||
#endif
|
|
||||||
mediaInfo["source"_ls] = QUrl();
|
mediaInfo["source"_ls] = QUrl();
|
||||||
} else {
|
} else {
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
QUrl source = room->makeMediaUrl(eventId, fileContent->url());
|
QUrl source = room->makeMediaUrl(eventId, fileContent->url());
|
||||||
#else
|
|
||||||
QUrl source = room->makeMediaUrl(eventId, fileContent->fileInfo()->url());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (source.isValid()) {
|
if (source.isValid()) {
|
||||||
mediaInfo["source"_ls] = source;
|
mediaInfo["source"_ls] = source;
|
||||||
@@ -780,25 +742,15 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
|||||||
mediaInfo["mimeIcon"_ls] = mimeType.iconName();
|
mediaInfo["mimeIcon"_ls] = mimeType.iconName();
|
||||||
|
|
||||||
// Add media size if available.
|
// Add media size if available.
|
||||||
#if Quotient_VERSION_MINOR > 8
|
mediaInfo["size"_ls] = fileContent->commonInfo().payloadSize;
|
||||||
mediaInfo["size"_ls] = static_cast<const EventContent::FileContent *>(fileContent)->payloadSize;
|
|
||||||
#else
|
|
||||||
mediaInfo["size"_ls] = static_cast<const EventContent::FileContent *>(fileContent)->fileInfo()->payloadSize;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
mediaInfo["isSticker"_ls] = isSticker;
|
mediaInfo["isSticker"_ls] = isSticker;
|
||||||
|
|
||||||
// Add parameter depending on media type.
|
// Add parameter depending on media type.
|
||||||
if (mimeType.name().contains(QStringLiteral("image"))) {
|
if (mimeType.name().contains(QStringLiteral("image"))) {
|
||||||
if (auto castInfo = static_cast<const EventContent::ImageContent *>(fileContent)) {
|
if (auto castInfo = static_cast<const EventContent::ImageContent *>(fileContent)) {
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
mediaInfo["width"_ls] = castInfo->imageSize.width();
|
mediaInfo["width"_ls] = castInfo->imageSize.width();
|
||||||
mediaInfo["height"_ls] = castInfo->imageSize.height();
|
mediaInfo["height"_ls] = castInfo->imageSize.height();
|
||||||
#else
|
|
||||||
const auto imageInfo = static_cast<const EventContent::ImageInfo *>(castInfo->fileInfo());
|
|
||||||
mediaInfo["width"_ls] = imageInfo->imageSize.width();
|
|
||||||
mediaInfo["height"_ls] = imageInfo->imageSize.height();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// TODO: Images in certain formats (e.g. WebP) will be erroneously marked as animated, even if they are static.
|
// TODO: Images in certain formats (e.g. WebP) will be erroneously marked as animated, even if they are static.
|
||||||
mediaInfo["animated"_ls] = QMovie::supportedFormats().contains(mimeType.preferredSuffix().toUtf8());
|
mediaInfo["animated"_ls] = QMovie::supportedFormats().contains(mimeType.preferredSuffix().toUtf8());
|
||||||
|
|||||||
@@ -290,11 +290,7 @@ private:
|
|||||||
|
|
||||||
static QVariantMap getMediaInfoForEvent(const NeoChatRoom *room, const Quotient::RoomEvent *event);
|
static QVariantMap getMediaInfoForEvent(const NeoChatRoom *room, const Quotient::RoomEvent *event);
|
||||||
QVariantMap static getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
QVariantMap static getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
const Quotient::EventContent::FileContentBase *fileContent,
|
const Quotient::EventContent::FileContentBase *fileContent,
|
||||||
#else
|
|
||||||
const Quotient::EventContent::TypedBase *fileContent,
|
|
||||||
#endif
|
|
||||||
const QString &eventId,
|
const QString &eventId,
|
||||||
bool isThumbnail = false,
|
bool isThumbnail = false,
|
||||||
bool isSticker = false);
|
bool isSticker = false);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include "imagepackevent.h"
|
#include "imagepackevent.h"
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <Quotient/omittable.h>
|
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
@@ -11,10 +10,10 @@ ImagePackEventContent::ImagePackEventContent(const QJsonObject &json)
|
|||||||
{
|
{
|
||||||
if (json.contains(QStringLiteral("pack"))) {
|
if (json.contains(QStringLiteral("pack"))) {
|
||||||
pack = ImagePackEventContent::Pack{
|
pack = ImagePackEventContent::Pack{
|
||||||
fromJson<Omittable<QString>>(json["pack"_ls].toObject()["display_name"_ls]),
|
fromJson<std::optional<QString>>(json["pack"_ls].toObject()["display_name"_ls]),
|
||||||
fromJson<Omittable<QUrl>>(json["pack"_ls].toObject()["avatar_url"_ls]),
|
fromJson<std::optional<QUrl>>(json["pack"_ls].toObject()["avatar_url"_ls]),
|
||||||
fromJson<Omittable<QStringList>>(json["pack"_ls].toObject()["usage"_ls]),
|
fromJson<std::optional<QStringList>>(json["pack"_ls].toObject()["usage"_ls]),
|
||||||
fromJson<Omittable<QString>>(json["pack"_ls].toObject()["attribution"_ls]),
|
fromJson<std::optional<QString>>(json["pack"_ls].toObject()["attribution"_ls]),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
pack = std::nullopt;
|
pack = std::nullopt;
|
||||||
@@ -31,9 +30,9 @@ ImagePackEventContent::ImagePackEventContent(const QJsonObject &json)
|
|||||||
images += ImagePackImage{
|
images += ImagePackImage{
|
||||||
k,
|
k,
|
||||||
fromJson<QUrl>(json["images"_ls][k]["url"_ls].toString()),
|
fromJson<QUrl>(json["images"_ls][k]["url"_ls].toString()),
|
||||||
fromJson<Omittable<QString>>(json["images"_ls][k]["body"_ls]),
|
fromJson<std::optional<QString>>(json["images"_ls][k]["body"_ls]),
|
||||||
info,
|
info,
|
||||||
fromJson<Omittable<QStringList>>(json["images"_ls][k]["usage"_ls]),
|
fromJson<std::optional<QStringList>>(json["images"_ls][k]["usage"_ls]),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,10 @@
|
|||||||
|
|
||||||
#include <Quotient/accountregistry.h>
|
#include <Quotient/accountregistry.h>
|
||||||
#include <Quotient/e2ee/sssshandler.h>
|
#include <Quotient/e2ee/sssshandler.h>
|
||||||
|
#include <Quotient/keyimport.h>
|
||||||
#include <Quotient/keyverificationsession.h>
|
#include <Quotient/keyverificationsession.h>
|
||||||
#include <Quotient/roommember.h>
|
#include <Quotient/roommember.h>
|
||||||
|
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
#include <Quotient/keyimport.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
|
|
||||||
@@ -43,11 +40,9 @@ struct ForeignSSSSHandler {
|
|||||||
QML_NAMED_ELEMENT(SSSSHandler)
|
QML_NAMED_ELEMENT(SSSSHandler)
|
||||||
};
|
};
|
||||||
|
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
struct ForeignKeyImport {
|
struct ForeignKeyImport {
|
||||||
Q_GADGET
|
Q_GADGET
|
||||||
QML_SINGLETON
|
QML_SINGLETON
|
||||||
QML_FOREIGN(Quotient::KeyImport)
|
QML_FOREIGN(Quotient::KeyImport)
|
||||||
QML_NAMED_ELEMENT(KeyImport)
|
QML_NAMED_ELEMENT(KeyImport)
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
NeochatAdd3PIdJob::NeochatAdd3PIdJob(const QString &clientSecret, const QString &sid, const Omittable<QJsonObject> &auth)
|
NeochatAdd3PIdJob::NeochatAdd3PIdJob(const QString &clientSecret, const QString &sid, const std::optional<QJsonObject> &auth)
|
||||||
: BaseJob(HttpVerb::Post, QStringLiteral("Add3PIDJob"), makePath("/_matrix/client/v3", "/account/3pid/add"))
|
: BaseJob(HttpVerb::Post, QStringLiteral("Add3PIDJob"), makePath("/_matrix/client/v3", "/account/3pid/add"))
|
||||||
{
|
{
|
||||||
QJsonObject _dataJson;
|
QJsonObject _dataJson;
|
||||||
|
|||||||
@@ -4,10 +4,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Quotient/jobs/basejob.h>
|
#include <Quotient/jobs/basejob.h>
|
||||||
#include <Quotient/omittable.h>
|
|
||||||
|
|
||||||
class NeochatAdd3PIdJob : public Quotient::BaseJob
|
class NeochatAdd3PIdJob : public Quotient::BaseJob
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit NeochatAdd3PIdJob(const QString &clientSecret, const QString &sid, const Quotient::Omittable<QJsonObject> &auth = {});
|
explicit NeochatAdd3PIdJob(const QString &clientSecret, const QString &sid, const std::optional<QJsonObject> &auth = {});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
NeochatChangePasswordJob::NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const Omittable<QJsonObject> &auth)
|
NeochatChangePasswordJob::NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const std::optional<QJsonObject> &auth)
|
||||||
: BaseJob(HttpVerb::Post, QStringLiteral("ChangePasswordJob"), "/_matrix/client/r0/account/password")
|
: BaseJob(HttpVerb::Post, QStringLiteral("ChangePasswordJob"), "/_matrix/client/r0/account/password")
|
||||||
{
|
{
|
||||||
QJsonObject _data;
|
QJsonObject _data;
|
||||||
|
|||||||
@@ -5,10 +5,8 @@
|
|||||||
|
|
||||||
#include <Quotient/jobs/basejob.h>
|
#include <Quotient/jobs/basejob.h>
|
||||||
|
|
||||||
#include <Quotient/omittable.h>
|
|
||||||
|
|
||||||
class NeochatChangePasswordJob : public Quotient::BaseJob
|
class NeochatChangePasswordJob : public Quotient::BaseJob
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const Quotient::Omittable<QJsonObject> &auth = {});
|
explicit NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const std::optional<QJsonObject> &auth = {});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
NeoChatDeactivateAccountJob::NeoChatDeactivateAccountJob(const Omittable<QJsonObject> &auth)
|
NeoChatDeactivateAccountJob::NeoChatDeactivateAccountJob(const std::optional<QJsonObject> &auth)
|
||||||
: BaseJob(HttpVerb::Post, QStringLiteral("DisableDeviceJob"), "_matrix/client/v3/account/deactivate")
|
: BaseJob(HttpVerb::Post, QStringLiteral("DisableDeviceJob"), "_matrix/client/v3/account/deactivate")
|
||||||
{
|
{
|
||||||
QJsonObject data;
|
QJsonObject data;
|
||||||
|
|||||||
@@ -4,10 +4,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Quotient/jobs/basejob.h>
|
#include <Quotient/jobs/basejob.h>
|
||||||
#include <Quotient/omittable.h>
|
|
||||||
|
|
||||||
class NeoChatDeactivateAccountJob : public Quotient::BaseJob
|
class NeoChatDeactivateAccountJob : public Quotient::BaseJob
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit NeoChatDeactivateAccountJob(const Quotient::Omittable<QJsonObject> &auth = {});
|
explicit NeoChatDeactivateAccountJob(const std::optional<QJsonObject> &auth = {});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
NeochatDeleteDeviceJob::NeochatDeleteDeviceJob(const QString &deviceId, const Omittable<QJsonObject> &auth)
|
NeochatDeleteDeviceJob::NeochatDeleteDeviceJob(const QString &deviceId, const std::optional<QJsonObject> &auth)
|
||||||
: BaseJob(HttpVerb::Delete, QStringLiteral("DeleteDeviceJob"), QStringLiteral("/_matrix/client/r0/devices/%1").arg(deviceId).toLatin1())
|
: BaseJob(HttpVerb::Delete, QStringLiteral("DeleteDeviceJob"), QStringLiteral("/_matrix/client/r0/devices/%1").arg(deviceId).toLatin1())
|
||||||
{
|
{
|
||||||
QJsonObject _data;
|
QJsonObject _data;
|
||||||
|
|||||||
@@ -4,10 +4,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Quotient/jobs/basejob.h>
|
#include <Quotient/jobs/basejob.h>
|
||||||
#include <Quotient/omittable.h>
|
|
||||||
|
|
||||||
class NeochatDeleteDeviceJob : public Quotient::BaseJob
|
class NeochatDeleteDeviceJob : public Quotient::BaseJob
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit NeochatDeleteDeviceJob(const QString &deviceId, const Quotient::Omittable<QJsonObject> &auth = {});
|
explicit NeochatDeleteDeviceJob(const QString &deviceId, const std::optional<QJsonObject> &auth = {});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Quotient/jobs/basejob.h>
|
#include <Quotient/jobs/basejob.h>
|
||||||
#include <Quotient/omittable.h>
|
|
||||||
|
|
||||||
// TODO: Upstream to libQuotient
|
// TODO: Upstream to libQuotient
|
||||||
class NeochatGetCommonRoomsJob : public Quotient::BaseJob
|
class NeochatGetCommonRoomsJob : public Quotient::BaseJob
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ void LoginHelper::init()
|
|||||||
m_connection = new NeoChatConnection();
|
m_connection = new NeoChatConnection();
|
||||||
m_matrixId = QString();
|
m_matrixId = QString();
|
||||||
m_password = QString();
|
m_password = QString();
|
||||||
m_deviceName = QStringLiteral("NeoChat %1 %2 %3 %4")
|
m_deviceName = QStringLiteral("NeoChat");
|
||||||
.arg(QSysInfo::machineHostName(), QSysInfo::productType(), QSysInfo::productVersion(), QSysInfo::currentCpuArchitecture());
|
|
||||||
m_supportsSso = false;
|
m_supportsSso = false;
|
||||||
m_supportsPassword = false;
|
m_supportsPassword = false;
|
||||||
m_ssoUrl = QUrl();
|
m_ssoUrl = QUrl();
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ LoginStep {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
FormCard.FormTextDelegate {
|
FormCard.FormTextDelegate {
|
||||||
|
textItem.wrapMode: Text.Wrap
|
||||||
text: i18n("Please wait while your messages are loaded from the server. This might take a little while.")
|
text: i18n("Please wait while your messages are loaded from the server. This might take a little while.")
|
||||||
}
|
}
|
||||||
FormCard.AbstractFormDelegate {
|
FormCard.AbstractFormDelegate {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import QtQuick.Layouts
|
|||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kirigamiaddons.formcard as FormCard
|
import org.kde.kirigamiaddons.formcard as FormCard
|
||||||
|
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||||
|
|
||||||
import org.kde.neochat
|
import org.kde.neochat
|
||||||
import org.kde.neochat.settings
|
import org.kde.neochat.settings
|
||||||
@@ -16,6 +17,7 @@ Kirigami.Page {
|
|||||||
|
|
||||||
property bool showExisting: false
|
property bool showExisting: false
|
||||||
property bool _showExisting: showExisting && root.currentStepString === root.initialStep
|
property bool _showExisting: showExisting && root.currentStepString === root.initialStep
|
||||||
|
property bool showSettings: true
|
||||||
property alias currentStep: module.item
|
property alias currentStep: module.item
|
||||||
property string currentStepString: initialStep
|
property string currentStepString: initialStep
|
||||||
property string initialStep: "LoginRegister"
|
property string initialStep: "LoginRegister"
|
||||||
@@ -90,11 +92,27 @@ Kirigami.Page {
|
|||||||
id: loadedAccounts
|
id: loadedAccounts
|
||||||
model: AccountRegistry
|
model: AccountRegistry
|
||||||
delegate: FormCard.FormButtonDelegate {
|
delegate: FormCard.FormButtonDelegate {
|
||||||
text: model.userId
|
id: delegate
|
||||||
|
|
||||||
|
required property string userId
|
||||||
|
required property NeoChatConnection connection
|
||||||
|
|
||||||
|
text: QmlUtils.escapeString(connection.localUser.displayName)
|
||||||
|
description: connection.localUser.id
|
||||||
|
leadingPadding: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Controller.activeConnection = model.connection;
|
Controller.activeConnection = delegate.connection;
|
||||||
root.connectionChosen();
|
root.connectionChosen();
|
||||||
}
|
}
|
||||||
|
leading: KirigamiComponents.Avatar {
|
||||||
|
id: avatar
|
||||||
|
name: delegate.text
|
||||||
|
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl.
|
||||||
|
source: delegate.connection.localUser.avatarUrl.toString().length > 0 ? delegate.connection.makeMediaUrl(delegate.connection.localUser.avatarUrl) : ""
|
||||||
|
implicitWidth: Kirigami.Units.iconSizes.medium
|
||||||
|
implicitHeight: Kirigami.Units.iconSizes.medium
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Repeater {
|
Repeater {
|
||||||
@@ -248,6 +266,7 @@ Kirigami.Page {
|
|||||||
FormCard.FormCard {
|
FormCard.FormCard {
|
||||||
Layout.topMargin: Kirigami.Units.largeSpacing * 2
|
Layout.topMargin: Kirigami.Units.largeSpacing * 2
|
||||||
maximumWidth: Kirigami.Units.gridUnit * 20
|
maximumWidth: Kirigami.Units.gridUnit * 20
|
||||||
|
visible: root.showSettings
|
||||||
FormCard.FormButtonDelegate {
|
FormCard.FormButtonDelegate {
|
||||||
text: i18nc("@action:button", "Settings")
|
text: i18nc("@action:button", "Settings")
|
||||||
icon.name: "settings-configure"
|
icon.name: "settings-configure"
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
#include <KCrash>
|
#include <KCrash>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <KIconTheme>
|
||||||
#include <KLocalizedContext>
|
#include <KLocalizedContext>
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
|
|
||||||
@@ -101,6 +102,7 @@ Q_DECL_EXPORT
|
|||||||
#endif
|
#endif
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
KIconTheme::initTheme();
|
||||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||||
|
|
||||||
#ifdef HAVE_WEBVIEW
|
#ifdef HAVE_WEBVIEW
|
||||||
@@ -140,7 +142,7 @@ int main(int argc, char *argv[])
|
|||||||
KAboutData about(QStringLiteral("neochat"),
|
KAboutData about(QStringLiteral("neochat"),
|
||||||
i18n("NeoChat"),
|
i18n("NeoChat"),
|
||||||
QStringLiteral(NEOCHAT_VERSION_STRING),
|
QStringLiteral(NEOCHAT_VERSION_STRING),
|
||||||
i18n("Matrix client"),
|
i18n("Chat on Matrix"),
|
||||||
KAboutLicense::GPL_V3,
|
KAboutLicense::GPL_V3,
|
||||||
i18n("© 2018-2020 Black Hat, 2020-2024 KDE Community"));
|
i18n("© 2018-2020 Black Hat, 2020-2024 KDE Community"));
|
||||||
about.addAuthor(i18n("Carl Schwan"),
|
about.addAuthor(i18n("Carl Schwan"),
|
||||||
|
|||||||
@@ -163,11 +163,7 @@ void AccountEmoticonModel::setEmoticonImage(int index, const QUrl &source)
|
|||||||
QCoro::Task<void> AccountEmoticonModel::doSetEmoticonImage(int index, QUrl source)
|
QCoro::Task<void> AccountEmoticonModel::doSetEmoticonImage(int index, QUrl source)
|
||||||
{
|
{
|
||||||
auto job = m_connection->uploadFile(source.isLocalFile() ? source.toLocalFile() : source.toString());
|
auto job = m_connection->uploadFile(source.isLocalFile() ? source.toLocalFile() : source.toString());
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
co_await qCoro(job.get(), &BaseJob::finished);
|
co_await qCoro(job.get(), &BaseJob::finished);
|
||||||
#else
|
|
||||||
co_await qCoro(job, &BaseJob::finished);
|
|
||||||
#endif
|
|
||||||
if (job->error() != BaseJob::NoError) {
|
if (job->error() != BaseJob::NoError) {
|
||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
@@ -189,11 +185,7 @@ QCoro::Task<void> AccountEmoticonModel::doSetEmoticonImage(int index, QUrl sourc
|
|||||||
QCoro::Task<void> AccountEmoticonModel::doAddEmoticon(QUrl source, QString shortcode, QString description, QString type)
|
QCoro::Task<void> AccountEmoticonModel::doAddEmoticon(QUrl source, QString shortcode, QString description, QString type)
|
||||||
{
|
{
|
||||||
auto job = m_connection->uploadFile(source.isLocalFile() ? source.toLocalFile() : source.toString());
|
auto job = m_connection->uploadFile(source.isLocalFile() ? source.toLocalFile() : source.toString());
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
co_await qCoro(job.get(), &BaseJob::finished);
|
co_await qCoro(job.get(), &BaseJob::finished);
|
||||||
#else
|
|
||||||
co_await qCoro(job, &BaseJob::finished);
|
|
||||||
#endif
|
|
||||||
if (job->error() != BaseJob::NoError) {
|
if (job->error() != BaseJob::NoError) {
|
||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,11 @@
|
|||||||
|
|
||||||
#include "chatbarcache.h"
|
#include "chatbarcache.h"
|
||||||
#include "enums/messagetype.h"
|
#include "enums/messagetype.h"
|
||||||
|
#include "neochatconfig.h"
|
||||||
#include "neochatconnection.h"
|
#include "neochatconnection.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
#include "roommanager.h"
|
#include "roommanager.h"
|
||||||
|
#include <Quotient/events/eventcontent.h>
|
||||||
#include <Quotient/events/roommemberevent.h>
|
#include <Quotient/events/roommemberevent.h>
|
||||||
#include <Quotient/events/roompowerlevelsevent.h>
|
#include <Quotient/events/roompowerlevelsevent.h>
|
||||||
#include <Quotient/user.h>
|
#include <Quotient/user.h>
|
||||||
@@ -16,6 +18,7 @@
|
|||||||
|
|
||||||
using Action = ActionsModel::Action;
|
using Action = ActionsModel::Action;
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
QStringList rainbowColors{"#ff2b00"_ls, "#ff5500"_ls, "#ff8000"_ls, "#ffaa00"_ls, "#ffd500"_ls, "#ffff00"_ls, "#d4ff00"_ls, "#aaff00"_ls, "#80ff00"_ls,
|
QStringList rainbowColors{"#ff2b00"_ls, "#ff5500"_ls, "#ff8000"_ls, "#ffaa00"_ls, "#ffd500"_ls, "#ffff00"_ls, "#d4ff00"_ls, "#aaff00"_ls, "#80ff00"_ls,
|
||||||
"#55ff00"_ls, "#2bff00"_ls, "#00ff00"_ls, "#00ff2b"_ls, "#00ff55"_ls, "#00ff80"_ls, "#00ffaa"_ls, "#00ffd5"_ls, "#00ffff"_ls,
|
"#55ff00"_ls, "#2bff00"_ls, "#00ff00"_ls, "#00ff2b"_ls, "#00ff55"_ls, "#00ff80"_ls, "#00ffaa"_ls, "#00ffd5"_ls, "#00ffff"_ls,
|
||||||
@@ -573,3 +576,78 @@ QList<Action> &ActionsModel::allActions() const
|
|||||||
{
|
{
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ActionsModel::handleQuickEditAction(NeoChatRoom *room, const QString &messageText)
|
||||||
|
{
|
||||||
|
if (room == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NeoChatConfig::allowQuickEdit()) {
|
||||||
|
QRegularExpression sed(QStringLiteral("^s/([^/]*)/([^/]*)(/g)?$"));
|
||||||
|
auto match = sed.match(messageText);
|
||||||
|
if (match.hasMatch()) {
|
||||||
|
const QString regex = match.captured(1);
|
||||||
|
const QString replacement = match.captured(2).toHtmlEscaped();
|
||||||
|
const QString flags = match.captured(3);
|
||||||
|
|
||||||
|
for (auto it = room->messageEvents().crbegin(); it != room->messageEvents().crend(); it++) {
|
||||||
|
if (const auto event = eventCast<const RoomMessageEvent>(&**it)) {
|
||||||
|
if (event->senderId() == room->localMember().id() && event->has<EventContent::TextContent>()) {
|
||||||
|
QString originalString;
|
||||||
|
if (event->content()) {
|
||||||
|
originalString = static_cast<const Quotient::EventContent::TextContent *>(event->content().get())->body;
|
||||||
|
} else {
|
||||||
|
originalString = event->plainBody();
|
||||||
|
}
|
||||||
|
QString replaceId = event->id();
|
||||||
|
const auto eventRelation = event->relatesTo();
|
||||||
|
if (eventRelation && eventRelation->type == "m.replace"_L1) {
|
||||||
|
replaceId = eventRelation->eventId;
|
||||||
|
}
|
||||||
|
if (flags == "/g"_L1) {
|
||||||
|
room->postHtmlMessage(messageText, originalString.replace(regex, replacement), event->msgtype(), {}, replaceId);
|
||||||
|
} else {
|
||||||
|
room->postHtmlMessage(messageText,
|
||||||
|
originalString.replace(originalString.indexOf(regex), regex.size(), replacement),
|
||||||
|
event->msgtype(),
|
||||||
|
{},
|
||||||
|
replaceId);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::optional<QString>, std::optional<Quotient::RoomMessageEvent::MsgType>> ActionsModel::handleAction(NeoChatRoom *room, ChatBarCache *chatBarCache)
|
||||||
|
{
|
||||||
|
auto sendText = chatBarCache->sendText();
|
||||||
|
const auto edited = handleQuickEditAction(room, sendText);
|
||||||
|
if (edited) {
|
||||||
|
return std::make_pair(std::nullopt, std::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Quotient::RoomMessageEvent::MsgType> messageType = std::nullopt;
|
||||||
|
if (sendText.startsWith(QLatin1Char('/'))) {
|
||||||
|
for (const auto &action : ActionsModel::instance().allActions()) {
|
||||||
|
if (sendText.indexOf(action.prefix) == 1
|
||||||
|
&& (sendText.indexOf(" "_ls) == action.prefix.length() + 1 || sendText.length() == action.prefix.length() + 1)) {
|
||||||
|
sendText = action.handle(sendText.mid(action.prefix.length() + 1).trimmed(), room, chatBarCache);
|
||||||
|
if (action.messageType.has_value()) {
|
||||||
|
messageType = action.messageType;
|
||||||
|
}
|
||||||
|
if (action.messageAction) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
return std::make_pair(std::nullopt, std::nullopt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(sendText, messageType);
|
||||||
|
}
|
||||||
|
|||||||
@@ -90,6 +90,21 @@ public:
|
|||||||
*/
|
*/
|
||||||
QList<Action> &allActions() const;
|
QList<Action> &allActions() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle special sed style edit action.
|
||||||
|
*
|
||||||
|
* @return True if the message has a sed edit which was actioned. False otherwise.
|
||||||
|
*/
|
||||||
|
static bool handleQuickEditAction(NeoChatRoom *room, const QString &messageText);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle any action within the message contained in the given ChatBarCache.
|
||||||
|
*
|
||||||
|
* @return A modified or unmodified string that needs to be sent or an empty string if
|
||||||
|
* the handled action replaces sending a normal message.
|
||||||
|
*/
|
||||||
|
static std::pair<std::optional<QString>, std::optional<Quotient::RoomMessageEvent::MsgType>> handleAction(NeoChatRoom *room, ChatBarCache *chatBarCache);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ActionsModel() = default;
|
ActionsModel() = default;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -85,13 +85,7 @@ QVariant CompletionModel::data(const QModelIndex &index, int role) const
|
|||||||
return m_filterModel->data(filterIndex, RoomListModel::CanonicalAliasRole);
|
return m_filterModel->data(filterIndex, RoomListModel::CanonicalAliasRole);
|
||||||
}
|
}
|
||||||
if (role == IconNameRole) {
|
if (role == IconNameRole) {
|
||||||
auto mediaId = m_filterModel->data(filterIndex, RoomListModel::AvatarRole).toString();
|
return m_filterModel->data(filterIndex, RoomListModel::AvatarRole).toString();
|
||||||
if (mediaId.isEmpty()) {
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
if (m_room) {
|
|
||||||
return m_room->connection()->makeMediaUrl(QUrl(QStringLiteral("mxc://%1").arg(mediaId)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_autoCompletionType == Emoji) {
|
if (m_autoCompletionType == Emoji) {
|
||||||
|
|||||||
@@ -129,11 +129,7 @@ void DevicesModel::logout(const QString &deviceId, const QString &password)
|
|||||||
QJsonObject identifier = {{"type"_ls, "m.id.user"_ls}, {"user"_ls, m_connection->user()->id()}};
|
QJsonObject identifier = {{"type"_ls, "m.id.user"_ls}, {"user"_ls, m_connection->user()->id()}};
|
||||||
authData["identifier"_ls] = identifier;
|
authData["identifier"_ls] = identifier;
|
||||||
auto innerJob = m_connection->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId, authData);
|
auto innerJob = m_connection->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId, authData);
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
connect(innerJob.get(), &BaseJob::success, this, onSuccess);
|
connect(innerJob.get(), &BaseJob::success, this, onSuccess);
|
||||||
#else
|
|
||||||
connect(innerJob, &BaseJob::success, this, onSuccess);
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
onSuccess();
|
onSuccess();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <QImageReader>
|
#include <QImageReader>
|
||||||
|
|
||||||
|
#include <Quotient/events/eventcontent.h>
|
||||||
#include <Quotient/events/redactionevent.h>
|
#include <Quotient/events/redactionevent.h>
|
||||||
#include <Quotient/events/roommessageevent.h>
|
#include <Quotient/events/roommessageevent.h>
|
||||||
#include <Quotient/events/stickerevent.h>
|
#include <Quotient/events/stickerevent.h>
|
||||||
@@ -521,18 +522,10 @@ QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentT
|
|||||||
auto fileTransferInfo = m_room->cachedFileTransferInfo(event);
|
auto fileTransferInfo = m_room->cachedFileTransferInfo(event);
|
||||||
|
|
||||||
#ifndef Q_OS_ANDROID
|
#ifndef Q_OS_ANDROID
|
||||||
Q_ASSERT(roomMessageEvent->content() != nullptr && roomMessageEvent->hasFileContent());
|
Q_ASSERT(roomMessageEvent->content() != nullptr && roomMessageEvent->has<EventContent::FileContent>());
|
||||||
#if Quotient_VERSION_MINOR > 8
|
const QMimeType mimeType = roomMessageEvent->get<EventContent::FileContent>()->mimeType;
|
||||||
const QMimeType mimeType = roomMessageEvent->fileContent()->mimeType;
|
|
||||||
#else
|
|
||||||
const QMimeType mimeType = roomMessageEvent->content()->fileInfo()->mimeType;
|
|
||||||
#endif
|
|
||||||
if (mimeType.name() == QStringLiteral("text/plain") || mimeType.parentMimeTypes().contains(QStringLiteral("text/plain"))) {
|
if (mimeType.name() == QStringLiteral("text/plain") || mimeType.parentMimeTypes().contains(QStringLiteral("text/plain"))) {
|
||||||
#if Quotient_VERSION_MINOR > 8
|
QString originalName = roomMessageEvent->get<EventContent::FileContent>()->originalName;
|
||||||
QString originalName = roomMessageEvent->fileContent()->originalName;
|
|
||||||
#else
|
|
||||||
QString originalName = roomMessageEvent->content()->fileInfo()->originalName;
|
|
||||||
#endif
|
|
||||||
if (originalName.isEmpty()) {
|
if (originalName.isEmpty()) {
|
||||||
originalName = roomMessageEvent->plainBody();
|
originalName = roomMessageEvent->plainBody();
|
||||||
}
|
}
|
||||||
@@ -576,15 +569,22 @@ QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentT
|
|||||||
case MessageComponentType::Video: {
|
case MessageComponentType::Video: {
|
||||||
if (!event->is<StickerEvent>()) {
|
if (!event->is<StickerEvent>()) {
|
||||||
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event);
|
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event);
|
||||||
QList<MessageComponent> components;
|
const auto fileContent = roomMessageEvent->get<EventContent::FileContentBase>();
|
||||||
components += MessageComponent{type, QString(), {}};
|
if (fileContent != nullptr) {
|
||||||
auto body = EventHandler::rawMessageBody(*roomMessageEvent);
|
const auto fileInfo = fileContent->commonInfo();
|
||||||
components += TextHandler().textComponents(body,
|
const auto body = EventHandler::rawMessageBody(*roomMessageEvent);
|
||||||
EventHandler::messageBodyInputFormat(*roomMessageEvent),
|
// Do not attach the description to the image, if it's the same as the original filename.
|
||||||
m_room,
|
if (fileInfo.originalName != body) {
|
||||||
roomMessageEvent,
|
QList<MessageComponent> components;
|
||||||
roomMessageEvent->isReplaced());
|
components += MessageComponent{type, QString(), {}};
|
||||||
return components;
|
components += TextHandler().textComponents(body,
|
||||||
|
EventHandler::messageBodyInputFormat(*roomMessageEvent),
|
||||||
|
m_room,
|
||||||
|
roomMessageEvent,
|
||||||
|
roomMessageEvent->isReplaced());
|
||||||
|
return components;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -642,7 +642,7 @@ QList<MessageComponent> MessageContentModel::addLinkPreviews(QList<MessageCompon
|
|||||||
|
|
||||||
void MessageContentModel::closeLinkPreview(int row)
|
void MessageContentModel::closeLinkPreview(int row)
|
||||||
{
|
{
|
||||||
if (row < 0 || row > m_components.size()) {
|
if (row < 0 || row >= m_components.size()) {
|
||||||
qWarning() << "closeLinkPreview() called with row" << row << "which does not exist. m_components.size() =" << m_components.size();
|
qWarning() << "closeLinkPreview() called with row" << row << "which does not exist. m_components.size() =" << m_components.size();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -652,6 +652,7 @@ void MessageContentModel::closeLinkPreview(int row)
|
|||||||
m_removedLinkPreviews += m_components[row].attributes["link"_ls].toUrl();
|
m_removedLinkPreviews += m_components[row].attributes["link"_ls].toUrl();
|
||||||
m_components.remove(row);
|
m_components.remove(row);
|
||||||
m_components.squeeze();
|
m_components.squeeze();
|
||||||
|
endResetModel();
|
||||||
resetContent();
|
resetContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -664,7 +665,7 @@ void MessageContentModel::updateItineraryModel()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event)) {
|
if (auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event)) {
|
||||||
if (roomMessageEvent->hasFileContent()) {
|
if (roomMessageEvent->has<EventContent::FileContent>()) {
|
||||||
auto filePath = m_room->cachedFileTransferInfo(event).localPath;
|
auto filePath = m_room->cachedFileTransferInfo(event).localPath;
|
||||||
if (filePath.isEmpty() && m_itineraryModel != nullptr) {
|
if (filePath.isEmpty() && m_itineraryModel != nullptr) {
|
||||||
delete m_itineraryModel;
|
delete m_itineraryModel;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
|
|
||||||
#include <Quotient/csapi/rooms.h>
|
#include <Quotient/csapi/rooms.h>
|
||||||
|
#include <Quotient/events/eventcontent.h>
|
||||||
#include <Quotient/events/redactionevent.h>
|
#include <Quotient/events/redactionevent.h>
|
||||||
#include <Quotient/events/roommessageevent.h>
|
#include <Quotient/events/roommessageevent.h>
|
||||||
#include <Quotient/events/stickerevent.h>
|
#include <Quotient/events/stickerevent.h>
|
||||||
@@ -504,7 +505,8 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
|
|
||||||
if (role == ProgressInfoRole) {
|
if (role == ProgressInfoRole) {
|
||||||
if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
|
if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
|
||||||
if (e->hasFileContent()) {
|
if (e->has<EventContent::FileContent>() || e->has<EventContent::ImageContent>() || e->has<EventContent::VideoContent>()
|
||||||
|
|| e->has<EventContent::AudioContent>()) {
|
||||||
return QVariant::fromValue(m_currentRoom->cachedFileTransferInfo(&evt));
|
return QVariant::fromValue(m_currentRoom->cachedFileTransferInfo(&evt));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -311,12 +311,7 @@ void PushRuleModel::addKeyword(const QString &keyword, const QString &roomId)
|
|||||||
pushConditions.append(keywordCondition);
|
pushConditions.append(keywordCondition);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
auto job = m_connection->callApi<Quotient::SetPushRuleJob>(PushRuleKind::kindString(kind),
|
auto job = m_connection->callApi<Quotient::SetPushRuleJob>(PushRuleKind::kindString(kind),
|
||||||
#else
|
|
||||||
auto job = m_connection->callApi<Quotient::SetPushRuleJob>(QLatin1String("global"),
|
|
||||||
PushRuleKind::kindString(kind),
|
|
||||||
#endif
|
|
||||||
keyword,
|
keyword,
|
||||||
actions,
|
actions,
|
||||||
QString(),
|
QString(),
|
||||||
@@ -341,11 +336,7 @@ void PushRuleModel::removeKeyword(const QString &keyword)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto kind = PushRuleKind::kindString(m_rules[index].kind);
|
auto kind = PushRuleKind::kindString(m_rules[index].kind);
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
auto job = m_connection->callApi<Quotient::DeletePushRuleJob>(kind, m_rules[index].id);
|
auto job = m_connection->callApi<Quotient::DeletePushRuleJob>(kind, m_rules[index].id);
|
||||||
#else
|
|
||||||
auto job = m_connection->callApi<Quotient::DeletePushRuleJob>(QStringLiteral("global"), kind, m_rules[index].id);
|
|
||||||
#endif
|
|
||||||
connect(job, &Quotient::BaseJob::failure, this, [this, job, index]() {
|
connect(job, &Quotient::BaseJob::failure, this, [this, job, index]() {
|
||||||
qWarning() << QLatin1String("Unable to remove push rule for keyword %1: ").arg(m_rules[index].id) << job->errorString();
|
qWarning() << QLatin1String("Unable to remove push rule for keyword %1: ").arg(m_rules[index].id) << job->errorString();
|
||||||
});
|
});
|
||||||
@@ -353,18 +344,10 @@ void PushRuleModel::removeKeyword(const QString &keyword)
|
|||||||
|
|
||||||
void PushRuleModel::setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled)
|
void PushRuleModel::setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled)
|
||||||
{
|
{
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
auto job = m_connection->callApi<Quotient::IsPushRuleEnabledJob>(kind, ruleId);
|
auto job = m_connection->callApi<Quotient::IsPushRuleEnabledJob>(kind, ruleId);
|
||||||
#else
|
|
||||||
auto job = m_connection->callApi<Quotient::IsPushRuleEnabledJob>(QStringLiteral("global"), kind, ruleId);
|
|
||||||
#endif
|
|
||||||
connect(job, &Quotient::BaseJob::success, this, [job, kind, ruleId, enabled, this]() {
|
connect(job, &Quotient::BaseJob::success, this, [job, kind, ruleId, enabled, this]() {
|
||||||
if (job->enabled() != enabled) {
|
if (job->enabled() != enabled) {
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
m_connection->callApi<Quotient::SetPushRuleEnabledJob>(kind, ruleId, enabled);
|
m_connection->callApi<Quotient::SetPushRuleEnabledJob>(kind, ruleId, enabled);
|
||||||
#else
|
|
||||||
m_connection->callApi<Quotient::SetPushRuleEnabledJob>(QStringLiteral("global"), kind, ruleId, enabled);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -378,11 +361,7 @@ void PushRuleModel::setNotificationRuleActions(const QString &kind, const QStrin
|
|||||||
actions = actionToVariant(action);
|
actions = actionToVariant(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
m_connection->callApi<Quotient::SetPushRuleActionsJob>(kind, ruleId, actions);
|
m_connection->callApi<Quotient::SetPushRuleActionsJob>(kind, ruleId, actions);
|
||||||
#else
|
|
||||||
m_connection->callApi<Quotient::SetPushRuleActionsJob>(QStringLiteral("global"), kind, ruleId, actions);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PushRuleAction::Action PushRuleModel::variantToAction(const QList<QVariant> &actions, bool enabled)
|
PushRuleAction::Action PushRuleModel::variantToAction(const QList<QVariant> &actions, bool enabled)
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ QVariant RoomListModel::data(const QModelIndex &index, int role) const
|
|||||||
return room->displayName().toHtmlEscaped();
|
return room->displayName().toHtmlEscaped();
|
||||||
}
|
}
|
||||||
if (role == AvatarRole) {
|
if (role == AvatarRole) {
|
||||||
return room->avatarMediaId();
|
return room->avatarMediaUrl();
|
||||||
}
|
}
|
||||||
if (role == CanonicalAliasRole) {
|
if (role == CanonicalAliasRole) {
|
||||||
return room->canonicalAlias();
|
return room->canonicalAlias();
|
||||||
|
|||||||
@@ -286,6 +286,7 @@ QHash<int, QByteArray> RoomTreeModel::roleNames() const
|
|||||||
roles[IconRole] = "icon";
|
roles[IconRole] = "icon";
|
||||||
roles[AttentionRole] = "attention";
|
roles[AttentionRole] = "attention";
|
||||||
roles[FavouriteRole] = "favourite";
|
roles[FavouriteRole] = "favourite";
|
||||||
|
roles[RoomTypeRole] = "roomType";
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,7 +324,7 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
|
|||||||
return room->displayName();
|
return room->displayName();
|
||||||
}
|
}
|
||||||
if (role == AvatarRole) {
|
if (role == AvatarRole) {
|
||||||
return room->avatarMediaId();
|
return room->avatarMediaUrl();
|
||||||
}
|
}
|
||||||
if (role == CanonicalAliasRole) {
|
if (role == CanonicalAliasRole) {
|
||||||
return room->canonicalAlias();
|
return room->canonicalAlias();
|
||||||
@@ -385,6 +386,11 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
|
|||||||
if (role == FavouriteRole) {
|
if (role == FavouriteRole) {
|
||||||
return room->isFavourite();
|
return room->isFavourite();
|
||||||
}
|
}
|
||||||
|
if (role == RoomTypeRole) {
|
||||||
|
if (room->creation()) {
|
||||||
|
return room->creation()->contentPart<QString>("type"_L1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ public:
|
|||||||
IconRole,
|
IconRole,
|
||||||
AttentionRole, /**< Whether there are any notifications. */
|
AttentionRole, /**< Whether there are any notifications. */
|
||||||
FavouriteRole, /**< Whether the room is favourited. */
|
FavouriteRole, /**< Whether the room is favourited. */
|
||||||
|
RoomTypeRole, /**< The room's type. */
|
||||||
};
|
};
|
||||||
Q_ENUM(EventRoles)
|
Q_ENUM(EventRoles)
|
||||||
explicit RoomTreeModel(QObject *parent = nullptr);
|
explicit RoomTreeModel(QObject *parent = nullptr);
|
||||||
|
|||||||
@@ -157,6 +157,11 @@ bool SortFilterRoomTreeModel::filterAcceptsRow(int source_row, const QModelIndex
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hide rooms with defined types, assuming that data-holding rooms have a defined type
|
||||||
|
if (!sourceModel()->data(index, RoomTreeModel::RoomTypeRole).toString().isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static auto config = NeoChatConfig::self();
|
static auto config = NeoChatConfig::self();
|
||||||
if (config->allRoomsInHome() && RoomManager::instance().currentSpace().isEmpty()) {
|
if (config->allRoomsInHome() && RoomManager::instance().currentSpace().isEmpty()) {
|
||||||
return acceptRoom;
|
return acceptRoom;
|
||||||
|
|||||||
@@ -93,11 +93,7 @@ void SpaceChildrenModel::refreshModel()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#if Quotient_VERSION_MINOR >= 9
|
|
||||||
void SpaceChildrenModel::insertChildren(std::vector<Quotient::GetSpaceHierarchyJob::SpaceHierarchyRoomsChunk> children, const QModelIndex &parent)
|
void SpaceChildrenModel::insertChildren(std::vector<Quotient::GetSpaceHierarchyJob::SpaceHierarchyRoomsChunk> children, const QModelIndex &parent)
|
||||||
#else
|
|
||||||
void SpaceChildrenModel::insertChildren(std::vector<Quotient::GetSpaceHierarchyJob::ChildRoomsChunk> children, const QModelIndex &parent)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
SpaceTreeItem *parentItem = getItem(parent);
|
SpaceTreeItem *parentItem = getItem(parent);
|
||||||
|
|
||||||
|
|||||||
@@ -144,9 +144,5 @@ private:
|
|||||||
|
|
||||||
void refreshModel();
|
void refreshModel();
|
||||||
|
|
||||||
#if Quotient_VERSION_MINOR >= 9
|
|
||||||
void insertChildren(std::vector<Quotient::GetSpaceHierarchyJob::SpaceHierarchyRoomsChunk> children, const QModelIndex &parent = QModelIndex());
|
void insertChildren(std::vector<Quotient::GetSpaceHierarchyJob::SpaceHierarchyRoomsChunk> children, const QModelIndex &parent = QModelIndex());
|
||||||
#else
|
|
||||||
void insertChildren(std::vector<Quotient::GetSpaceHierarchyJob::ChildRoomsChunk> children, const QModelIndex &parent = QModelIndex());
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include <Quotient/events/event.h>
|
#include <Quotient/events/event.h>
|
||||||
#include <Quotient/events/stickerevent.h>
|
#include <Quotient/events/stickerevent.h>
|
||||||
#include <Quotient/jobs/basejob.h>
|
#include <Quotient/jobs/basejob.h>
|
||||||
#include <Quotient/omittable.h>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "chatbarcache.h"
|
#include "chatbarcache.h"
|
||||||
|
|||||||
@@ -87,11 +87,7 @@ QVariant UserListModel::data(const QModelIndex &index, int role) const
|
|||||||
return memberId;
|
return memberId;
|
||||||
}
|
}
|
||||||
if (role == AvatarRole) {
|
if (role == AvatarRole) {
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
return m_currentRoom->member(memberId).avatarUrl();
|
return m_currentRoom->member(memberId).avatarUrl();
|
||||||
#else
|
|
||||||
return m_currentRoom->memberAvatar(memberId).url();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
if (role == ObjectRole) {
|
if (role == ObjectRole) {
|
||||||
return QVariant::fromValue(memberId);
|
return QVariant::fromValue(memberId);
|
||||||
@@ -176,11 +172,7 @@ void UserListModel::refreshAllMembers()
|
|||||||
|
|
||||||
if (m_currentRoom != nullptr) {
|
if (m_currentRoom != nullptr) {
|
||||||
m_members = m_currentRoom->joinedMemberIds();
|
m_members = m_currentRoom->joinedMemberIds();
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
MemberSorter sorter;
|
MemberSorter sorter;
|
||||||
#else
|
|
||||||
MemberSorter sorter(m_currentRoom);
|
|
||||||
#endif
|
|
||||||
std::sort(m_members.begin(), m_members.end(), [&sorter, this](const auto &left, const auto &right) {
|
std::sort(m_members.begin(), m_members.end(), [&sorter, this](const auto &left, const auto &right) {
|
||||||
const auto leftPl = m_currentRoom->getUserPowerLevel(left);
|
const auto leftPl = m_currentRoom->getUserPowerLevel(left);
|
||||||
const auto rightPl = m_currentRoom->getUserPowerLevel(right);
|
const auto rightPl = m_currentRoom->getUserPowerLevel(right);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user