Send Threaded Messages
This MR deals with only sending threaded messages. Showing threads will turn up in a follow up. This allows you to start a new thread by clicking reply in thread to a normal message. You can also do a threaded reply to a threaded message in the main timeline at the moment because those messages aren't shown in a separate thread timeline yet but will be in future.
This commit is contained in:
381
autotests/data/test-eventhandler-sync.json
Normal file
381
autotests/data/test-eventhandler-sync.json
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
{
|
||||||
|
"account_data": {
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"tags": {
|
||||||
|
"u.work": {
|
||||||
|
"order": 0.9
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "m.tag"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"custom_config_key": "custom_config_value"
|
||||||
|
},
|
||||||
|
"type": "org.example.custom.room.config"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ephemeral": {
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"user_ids": [
|
||||||
|
"@alice:matrix.org",
|
||||||
|
"@bob:example.com"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||||
|
"type": "m.typing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"$153456789:example.org": {
|
||||||
|
"m.read": {
|
||||||
|
"@alice:matrix.org": {
|
||||||
|
"ts": 1436451550453
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "m.receipt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"$1532735824654:example.org": {
|
||||||
|
"m.read": {
|
||||||
|
"@bob:example.com": {
|
||||||
|
"ts": 1436451550453
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "m.receipt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"$1532735824654:example.org": {
|
||||||
|
"m.read": {
|
||||||
|
"@tim:example.com": {
|
||||||
|
"ts": 1436451550454
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "m.receipt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"$1532735824654:example.org": {
|
||||||
|
"m.read": {
|
||||||
|
"@jeff:example.com": {
|
||||||
|
"ts": 1436451550455
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "m.receipt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"$1532735824654:example.org": {
|
||||||
|
"m.read": {
|
||||||
|
"@tina:example.com": {
|
||||||
|
"ts": 1436451550456
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "m.receipt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"$1532735824654:example.org": {
|
||||||
|
"m.read": {
|
||||||
|
"@sally:example.com": {
|
||||||
|
"ts": 1436451550457
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "m.receipt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"$1532735824654:example.org": {
|
||||||
|
"m.read": {
|
||||||
|
"@fred:example.com": {
|
||||||
|
"ts": 1436451550458
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "m.receipt"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||||
|
"displayname": "Alice Margatroid",
|
||||||
|
"membership": "join",
|
||||||
|
"reason": "Looking for support"
|
||||||
|
},
|
||||||
|
"event_id": "$143273582443PhrSn:example.org",
|
||||||
|
"origin_server_ts": 1432735824653,
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||||
|
"sender": "@example:example.org",
|
||||||
|
"state_key": "@alice:example.org",
|
||||||
|
"type": "m.room.member",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 1234
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"summary": {
|
||||||
|
"m.heroes": [
|
||||||
|
"@alice:example.com",
|
||||||
|
"@bob:example.com"
|
||||||
|
],
|
||||||
|
"m.invited_member_count": 0,
|
||||||
|
"m.joined_member_count": 2
|
||||||
|
},
|
||||||
|
"timeline": {
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"body": "This is an example\ntext message",
|
||||||
|
"format": "org.matrix.custom.html",
|
||||||
|
"formatted_body": "<b>This is an example<br>text message</b>",
|
||||||
|
"msgtype": "m.text"
|
||||||
|
},
|
||||||
|
"event_id": "$153456789:example.org",
|
||||||
|
"origin_server_ts": 1432735824654,
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||||
|
"sender": "@example:example.org",
|
||||||
|
"type": "m.room.message",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 1232
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"avatar_url": "mxc://kde.org/123456",
|
||||||
|
"displayname": "after",
|
||||||
|
"membership": "join"
|
||||||
|
},
|
||||||
|
"origin_server_ts": 1690651134736,
|
||||||
|
"sender": "@example:example.org",
|
||||||
|
"state_key": "@example:example.org",
|
||||||
|
"type": "m.room.member",
|
||||||
|
"unsigned": {
|
||||||
|
"replaces_state": "$1234567890:example.org",
|
||||||
|
"prev_content": {
|
||||||
|
"avatar_url": "mxc://kde.org/12345",
|
||||||
|
"displayname": "before",
|
||||||
|
"membership": "join"
|
||||||
|
},
|
||||||
|
"prev_sender": "@example:example.orgg",
|
||||||
|
"age": 1234
|
||||||
|
},
|
||||||
|
"event_id": "$143273583553PhrSn:example.org",
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"body": "This is a highlight @bob:kde.org and this is a link https://kde.org",
|
||||||
|
"format": "org.matrix.custom.html",
|
||||||
|
"msgtype": "m.text"
|
||||||
|
},
|
||||||
|
"event_id": "$1532735824654:example.org",
|
||||||
|
"origin_server_ts": 1532735824654,
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||||
|
"sender": "@example:example.org",
|
||||||
|
"type": "m.room.message",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 1233
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"m.relates_to": {
|
||||||
|
"event_id": "$153456789:example.org",
|
||||||
|
"key": "👍",
|
||||||
|
"rel_type": "m.annotation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"origin_server_ts": 1690322545182,
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||||
|
"sender": "@alice:matrix.org",
|
||||||
|
"type": "m.reaction",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 390159120
|
||||||
|
},
|
||||||
|
"event_id": "$163456789:example.org",
|
||||||
|
"age": 390159120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"age": 4926305285,
|
||||||
|
"content": {
|
||||||
|
"body": "video caption",
|
||||||
|
"filename": "video.mp4",
|
||||||
|
"info": {
|
||||||
|
"duration": 10,
|
||||||
|
"h": 1080,
|
||||||
|
"mimetype": "video/mp4",
|
||||||
|
"size": 62650636,
|
||||||
|
"w": 1920,
|
||||||
|
"thumbnail_info": {
|
||||||
|
"h": 450,
|
||||||
|
"mimetype": "image/jpeg",
|
||||||
|
"size": 382249,
|
||||||
|
"w": 800
|
||||||
|
},
|
||||||
|
"thumbnail_url": "mxc://kde.org/2234567"
|
||||||
|
},
|
||||||
|
"msgtype": "m.video",
|
||||||
|
"url": "mxc://kde.org/1234567"
|
||||||
|
},
|
||||||
|
"event_id": "$263456789:example.org",
|
||||||
|
"origin_server_ts": 1685793783330,
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||||
|
"sender": "@example:example.org",
|
||||||
|
"type": "m.room.message",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 4926305285
|
||||||
|
},
|
||||||
|
"user_id": "@example:example.org"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"body": "> <@example:example.org> This is an example\ntext message\n\nreply",
|
||||||
|
"format": "org.matrix.custom.html",
|
||||||
|
"formatted_body": "<mx-reply><blockquote><a href=\"https://matrix.to/#/!jEsUZKDJdhlrceRyVU:example.org/$153456789:example.org?via=kde.org&via=matrix.org\">In reply to</a> <a href=\"https://matrix.to/#/@example:example.org\">@example:example.org</a><br><b>This is an example<br>text message</b></blockquote></mx-reply>reply",
|
||||||
|
"m.relates_to": {
|
||||||
|
"m.in_reply_to": {
|
||||||
|
"event_id": "$153456789:example.org"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"msgtype": "m.text"
|
||||||
|
},
|
||||||
|
"origin_server_ts": 1690725965572,
|
||||||
|
"sender": "@alice:matrix.org",
|
||||||
|
"type": "m.room.message",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 98
|
||||||
|
},
|
||||||
|
"event_id": "$154456789:example.org",
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"body": "> <@example:example.org> video caption\n\nreply",
|
||||||
|
"m.relates_to": {
|
||||||
|
"m.in_reply_to": {
|
||||||
|
"event_id": "$263456789:example.org"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"msgtype": "m.text"
|
||||||
|
},
|
||||||
|
"origin_server_ts": 1690725965573,
|
||||||
|
"sender": "@alice:matrix.org",
|
||||||
|
"type": "m.room.message",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 98
|
||||||
|
},
|
||||||
|
"event_id": "$154456799:example.org",
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"age": 96845207,
|
||||||
|
"content": {
|
||||||
|
"body": "Lat: 51.7035, Lon: -1.14394",
|
||||||
|
"geo_uri": "geo:51.7035,-1.14394",
|
||||||
|
"msgtype": "m.location",
|
||||||
|
"org.matrix.msc1767.text": "Lat: 51.7035, Lon: -1.14394",
|
||||||
|
"org.matrix.msc3488.asset": {
|
||||||
|
"type": "m.pin"
|
||||||
|
},
|
||||||
|
"org.matrix.msc3488.location": {
|
||||||
|
"uri": "geo:51.7035,-1.14394"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"event_id": "$1544567999:example.org",
|
||||||
|
"origin_server_ts": 1690821582876,
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||||
|
"sender": "@example:example.org",
|
||||||
|
"type": "m.room.message",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 96845207
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"body": "Thread root",
|
||||||
|
"format": "org.matrix.custom.html",
|
||||||
|
"msgtype": "m.text"
|
||||||
|
},
|
||||||
|
"event_id": "$threadroot:example.org",
|
||||||
|
"origin_server_ts": 1690821582879,
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||||
|
"sender": "@example:example.org",
|
||||||
|
"type": "m.room.message",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 1232
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"body": "Thread message 1",
|
||||||
|
"msgtype": "m.text",
|
||||||
|
"m.relates_to": {
|
||||||
|
"rel_type": "m.thread",
|
||||||
|
"event_id": "$threadroot:example.org",
|
||||||
|
"m.in_reply_to": {
|
||||||
|
"event_id": "$threadroot:example.org"
|
||||||
|
},
|
||||||
|
"is_falling_back": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"event_id": "$threadmessage1:example.org",
|
||||||
|
"origin_server_ts": 1690821582890,
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||||
|
"sender": "@example:example.org",
|
||||||
|
"type": "m.room.message",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 1238
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"body": "Thread message 2",
|
||||||
|
"msgtype": "m.text",
|
||||||
|
"m.relates_to": {
|
||||||
|
"rel_type": "m.thread",
|
||||||
|
"event_id": "$threadroot:example.org",
|
||||||
|
"m.in_reply_to": {
|
||||||
|
"event_id": "$threadmessage1:example.org"
|
||||||
|
},
|
||||||
|
"is_falling_back": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"event_id": "$threadmessage2:example.org",
|
||||||
|
"origin_server_ts": 1690821582890,
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||||
|
"sender": "@example:example.org",
|
||||||
|
"type": "m.room.message",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 1238
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"limited": true,
|
||||||
|
"prev_batch": "t34-23535_0_0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -64,6 +64,7 @@ private Q_SLOTS:
|
|||||||
void replyAuthor();
|
void replyAuthor();
|
||||||
void replyBody();
|
void replyBody();
|
||||||
void replyMediaInfo();
|
void replyMediaInfo();
|
||||||
|
void thread();
|
||||||
void location();
|
void location();
|
||||||
void readMarkers();
|
void readMarkers();
|
||||||
};
|
};
|
||||||
@@ -73,329 +74,11 @@ void EventHandlerTest::initTestCase()
|
|||||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||||
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
||||||
|
|
||||||
const auto json = QJsonDocument::fromJson(R"EVENT({
|
QFile testEventHandlerSyncFile;
|
||||||
"account_data": {
|
testEventHandlerSyncFile.setFileName(QLatin1String(DATA_DIR) + u'/' + QLatin1String("test-eventhandler-sync.json"));
|
||||||
"events": [
|
testEventHandlerSyncFile.open(QIODevice::ReadOnly);
|
||||||
{
|
const auto testEventHandlerSyncJson = QJsonDocument::fromJson(testEventHandlerSyncFile.readAll());
|
||||||
"content": {
|
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, testEventHandlerSyncJson.object());
|
||||||
"tags": {
|
|
||||||
"u.work": {
|
|
||||||
"order": 0.9
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "m.tag"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"custom_config_key": "custom_config_value"
|
|
||||||
},
|
|
||||||
"type": "org.example.custom.room.config"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ephemeral": {
|
|
||||||
"events": [
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"user_ids": [
|
|
||||||
"@alice:matrix.org",
|
|
||||||
"@bob:example.com"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
|
||||||
"type": "m.typing"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"$153456789:example.org": {
|
|
||||||
"m.read": {
|
|
||||||
"@alice:matrix.org": {
|
|
||||||
"ts": 1436451550453
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "m.receipt"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"$1532735824654:example.org": {
|
|
||||||
"m.read": {
|
|
||||||
"@bob:example.com": {
|
|
||||||
"ts": 1436451550453
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "m.receipt"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"$1532735824654:example.org": {
|
|
||||||
"m.read": {
|
|
||||||
"@tim:example.com": {
|
|
||||||
"ts": 1436451550454
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "m.receipt"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"$1532735824654:example.org": {
|
|
||||||
"m.read": {
|
|
||||||
"@jeff:example.com": {
|
|
||||||
"ts": 1436451550455
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "m.receipt"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"$1532735824654:example.org": {
|
|
||||||
"m.read": {
|
|
||||||
"@tina:example.com": {
|
|
||||||
"ts": 1436451550456
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "m.receipt"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"$1532735824654:example.org": {
|
|
||||||
"m.read": {
|
|
||||||
"@sally:example.com": {
|
|
||||||
"ts": 1436451550457
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "m.receipt"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"$1532735824654:example.org": {
|
|
||||||
"m.read": {
|
|
||||||
"@fred:example.com": {
|
|
||||||
"ts": 1436451550458
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "m.receipt"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"events": [
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
|
||||||
"displayname": "Alice Margatroid",
|
|
||||||
"membership": "join",
|
|
||||||
"reason": "Looking for support"
|
|
||||||
},
|
|
||||||
"event_id": "$143273582443PhrSn:example.org",
|
|
||||||
"origin_server_ts": 1432735824653,
|
|
||||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
|
||||||
"sender": "@example:example.org",
|
|
||||||
"state_key": "@alice:example.org",
|
|
||||||
"type": "m.room.member",
|
|
||||||
"unsigned": {
|
|
||||||
"age": 1234
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"summary": {
|
|
||||||
"m.heroes": [
|
|
||||||
"@alice:example.com",
|
|
||||||
"@bob:example.com"
|
|
||||||
],
|
|
||||||
"m.invited_member_count": 0,
|
|
||||||
"m.joined_member_count": 2
|
|
||||||
},
|
|
||||||
"timeline": {
|
|
||||||
"events": [
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"body": "This is an example\ntext message",
|
|
||||||
"format": "org.matrix.custom.html",
|
|
||||||
"formatted_body": "<b>This is an example<br>text message</b>",
|
|
||||||
"msgtype": "m.text"
|
|
||||||
},
|
|
||||||
"event_id": "$153456789:example.org",
|
|
||||||
"origin_server_ts": 1432735824654,
|
|
||||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
|
||||||
"sender": "@example:example.org",
|
|
||||||
"type": "m.room.message",
|
|
||||||
"unsigned": {
|
|
||||||
"age": 1232
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"avatar_url": "mxc://kde.org/123456",
|
|
||||||
"displayname": "after",
|
|
||||||
"membership": "join"
|
|
||||||
},
|
|
||||||
"origin_server_ts": 1690651134736,
|
|
||||||
"sender": "@example:example.org",
|
|
||||||
"state_key": "@example:example.org",
|
|
||||||
"type": "m.room.member",
|
|
||||||
"unsigned": {
|
|
||||||
"replaces_state": "$1234567890:example.org",
|
|
||||||
"prev_content": {
|
|
||||||
"avatar_url": "mxc://kde.org/12345",
|
|
||||||
"displayname": "before",
|
|
||||||
"membership": "join"
|
|
||||||
},
|
|
||||||
"prev_sender": "@example:example.orgg",
|
|
||||||
"age": 1234
|
|
||||||
},
|
|
||||||
"event_id": "$143273583553PhrSn:example.org",
|
|
||||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"body": "This is a highlight @bob:kde.org and this is a link https://kde.org",
|
|
||||||
"format": "org.matrix.custom.html",
|
|
||||||
"msgtype": "m.text"
|
|
||||||
},
|
|
||||||
"event_id": "$1532735824654:example.org",
|
|
||||||
"origin_server_ts": 1532735824654,
|
|
||||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
|
||||||
"sender": "@example:example.org",
|
|
||||||
"type": "m.room.message",
|
|
||||||
"unsigned": {
|
|
||||||
"age": 1233
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"m.relates_to": {
|
|
||||||
"event_id": "$153456789:example.org",
|
|
||||||
"key": "👍",
|
|
||||||
"rel_type": "m.annotation"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"origin_server_ts": 1690322545182,
|
|
||||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
|
||||||
"sender": "@alice:matrix.org",
|
|
||||||
"type": "m.reaction",
|
|
||||||
"unsigned": {
|
|
||||||
"age": 390159120
|
|
||||||
},
|
|
||||||
"event_id": "$163456789:example.org",
|
|
||||||
"age": 390159120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"age": 4926305285,
|
|
||||||
"content": {
|
|
||||||
"body": "video caption",
|
|
||||||
"filename": "video.mp4",
|
|
||||||
"info": {
|
|
||||||
"duration": 10,
|
|
||||||
"h": 1080,
|
|
||||||
"mimetype": "video/mp4",
|
|
||||||
"size": 62650636,
|
|
||||||
"w": 1920,
|
|
||||||
"thumbnail_info": {
|
|
||||||
"h": 450,
|
|
||||||
"mimetype": "image/jpeg",
|
|
||||||
"size": 382249,
|
|
||||||
"w": 800
|
|
||||||
},
|
|
||||||
"thumbnail_url": "mxc://kde.org/2234567"
|
|
||||||
},
|
|
||||||
"msgtype": "m.video",
|
|
||||||
"url": "mxc://kde.org/1234567"
|
|
||||||
},
|
|
||||||
"event_id": "$263456789:example.org",
|
|
||||||
"origin_server_ts": 1685793783330,
|
|
||||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
|
||||||
"sender": "@example:example.org",
|
|
||||||
"type": "m.room.message",
|
|
||||||
"unsigned": {
|
|
||||||
"age": 4926305285
|
|
||||||
},
|
|
||||||
"user_id": "@example:example.org"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"body": "> <@example:example.org> This is an example\ntext message\n\nreply",
|
|
||||||
"format": "org.matrix.custom.html",
|
|
||||||
"formatted_body": "<mx-reply><blockquote><a href=\"https://matrix.to/#/!jEsUZKDJdhlrceRyVU:example.org/$153456789:example.org?via=kde.org&via=matrix.org\">In reply to</a> <a href=\"https://matrix.to/#/@example:example.org\">@example:example.org</a><br><b>This is an example<br>text message</b></blockquote></mx-reply>reply",
|
|
||||||
"m.relates_to": {
|
|
||||||
"m.in_reply_to": {
|
|
||||||
"event_id": "$153456789:example.org"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"msgtype": "m.text"
|
|
||||||
},
|
|
||||||
"origin_server_ts": 1690725965572,
|
|
||||||
"sender": "@alice:matrix.org",
|
|
||||||
"type": "m.room.message",
|
|
||||||
"unsigned": {
|
|
||||||
"age": 98
|
|
||||||
},
|
|
||||||
"event_id": "$154456789:example.org",
|
|
||||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"body": "> <@example:example.org> video caption\n\nreply",
|
|
||||||
"m.relates_to": {
|
|
||||||
"m.in_reply_to": {
|
|
||||||
"event_id": "$263456789:example.org"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"msgtype": "m.text"
|
|
||||||
},
|
|
||||||
"origin_server_ts": 1690725965573,
|
|
||||||
"sender": "@alice:matrix.org",
|
|
||||||
"type": "m.room.message",
|
|
||||||
"unsigned": {
|
|
||||||
"age": 98
|
|
||||||
},
|
|
||||||
"event_id": "$154456799:example.org",
|
|
||||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"age": 96845207,
|
|
||||||
"content": {
|
|
||||||
"body": "Lat: 51.7035, Lon: -1.14394",
|
|
||||||
"geo_uri": "geo:51.7035,-1.14394",
|
|
||||||
"msgtype": "m.location",
|
|
||||||
"org.matrix.msc1767.text": "Lat: 51.7035, Lon: -1.14394",
|
|
||||||
"org.matrix.msc3488.asset": {
|
|
||||||
"type": "m.pin"
|
|
||||||
},
|
|
||||||
"org.matrix.msc3488.location": {
|
|
||||||
"uri": "geo:51.7035,-1.14394"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"event_id": "$1544567999:example.org",
|
|
||||||
"origin_server_ts": 1690821582876,
|
|
||||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
|
||||||
"sender": "@example:example.org",
|
|
||||||
"type": "m.room.message",
|
|
||||||
"unsigned": {
|
|
||||||
"age": 96845207
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"limited": true,
|
|
||||||
"prev_batch": "t34-23535_0_0"
|
|
||||||
}
|
|
||||||
})EVENT");
|
|
||||||
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, json.object());
|
|
||||||
room->update(std::move(roomData));
|
room->update(std::move(roomData));
|
||||||
|
|
||||||
eventHandler.setRoom(room);
|
eventHandler.setRoom(room);
|
||||||
@@ -686,6 +369,29 @@ void EventHandlerTest::replyMediaInfo()
|
|||||||
QCOMPARE(thumbnailInfo["height"_ls], 450);
|
QCOMPARE(thumbnailInfo["height"_ls], 450);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EventHandlerTest::thread()
|
||||||
|
{
|
||||||
|
auto event = room->messageEvents().at(0).get();
|
||||||
|
eventHandler.setEvent(event);
|
||||||
|
|
||||||
|
QCOMPARE(eventHandler.isThreaded(), false);
|
||||||
|
QCOMPARE(eventHandler.threadRoot(), QString());
|
||||||
|
|
||||||
|
event = room->messageEvents().at(9).get();
|
||||||
|
eventHandler.setEvent(event);
|
||||||
|
|
||||||
|
QCOMPARE(eventHandler.isThreaded(), true);
|
||||||
|
QCOMPARE(eventHandler.threadRoot(), QStringLiteral("$threadroot:example.org"));
|
||||||
|
QCOMPARE(eventHandler.getReplyId(), QStringLiteral("$threadroot:example.org"));
|
||||||
|
|
||||||
|
event = room->messageEvents().at(10).get();
|
||||||
|
eventHandler.setEvent(event);
|
||||||
|
|
||||||
|
QCOMPARE(eventHandler.isThreaded(), true);
|
||||||
|
QCOMPARE(eventHandler.threadRoot(), QStringLiteral("$threadroot:example.org"));
|
||||||
|
QCOMPARE(eventHandler.getReplyId(), QStringLiteral("$threadmessage1:example.org"));
|
||||||
|
}
|
||||||
|
|
||||||
void EventHandlerTest::location()
|
void EventHandlerTest::location()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(7).get();
|
auto event = room->messageEvents().at(7).get();
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ void ActionsHandler::handleMessage(const QString &text, QString handledText, Cha
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_room->postMessage(text, handledText, messageType, chatBarCache->replyId(), chatBarCache->editId());
|
m_room->postMessage(text, handledText, messageType, chatBarCache->replyId(), chatBarCache->editId(), chatBarCache->threadId());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActionsHandler::checkEffects(const QString &text)
|
void ActionsHandler::checkEffects(const QString &text)
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ void ChatBarCache::setReplyId(const QString &replyId)
|
|||||||
}
|
}
|
||||||
m_attachmentPath = QString();
|
m_attachmentPath = QString();
|
||||||
Q_EMIT relationIdChanged();
|
Q_EMIT relationIdChanged();
|
||||||
|
Q_EMIT attachmentPathChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChatBarCache::isEditing() const
|
bool ChatBarCache::isEditing() const
|
||||||
@@ -79,6 +80,7 @@ void ChatBarCache::setEditId(const QString &editId)
|
|||||||
}
|
}
|
||||||
m_attachmentPath = QString();
|
m_attachmentPath = QString();
|
||||||
Q_EMIT relationIdChanged();
|
Q_EMIT relationIdChanged();
|
||||||
|
Q_EMIT attachmentPathChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap ChatBarCache::relationUser() const
|
QVariantMap ChatBarCache::relationUser() const
|
||||||
@@ -121,6 +123,25 @@ QString ChatBarCache::relationMessage() const
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ChatBarCache::isThreaded() const
|
||||||
|
{
|
||||||
|
return !m_threadId.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatBarCache::threadId() const
|
||||||
|
{
|
||||||
|
return m_threadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatBarCache::setThreadId(const QString &threadId)
|
||||||
|
{
|
||||||
|
if (m_threadId == threadId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_threadId = threadId;
|
||||||
|
Q_EMIT threadIdChanged();
|
||||||
|
}
|
||||||
|
|
||||||
QString ChatBarCache::attachmentPath() const
|
QString ChatBarCache::attachmentPath() const
|
||||||
{
|
{
|
||||||
return m_attachmentPath;
|
return m_attachmentPath;
|
||||||
@@ -135,6 +156,7 @@ void ChatBarCache::setAttachmentPath(const QString &attachmentPath)
|
|||||||
m_relationType = None;
|
m_relationType = None;
|
||||||
m_relationId = QString();
|
m_relationId = QString();
|
||||||
Q_EMIT attachmentPathChanged();
|
Q_EMIT attachmentPathChanged();
|
||||||
|
Q_EMIT relationIdChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Mention> *ChatBarCache::mentions()
|
QList<Mention> *ChatBarCache::mentions()
|
||||||
|
|||||||
@@ -113,6 +113,16 @@ class ChatBarCache : public QObject
|
|||||||
*/
|
*/
|
||||||
Q_PROPERTY(QString relationMessage READ relationMessage NOTIFY relationIdChanged)
|
Q_PROPERTY(QString relationMessage READ relationMessage NOTIFY relationIdChanged)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Whether the chat bar is replying in a thread.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(bool isThreaded READ isThreaded NOTIFY threadIdChanged)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The Matrix message ID of thread root event, if any.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(QString threadId READ threadId WRITE setThreadId NOTIFY threadIdChanged)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The local path for a file to send, if any.
|
* @brief The local path for a file to send, if any.
|
||||||
*
|
*
|
||||||
@@ -152,6 +162,10 @@ public:
|
|||||||
|
|
||||||
QString relationMessage() const;
|
QString relationMessage() const;
|
||||||
|
|
||||||
|
bool isThreaded() const;
|
||||||
|
QString threadId() const;
|
||||||
|
void setThreadId(const QString &threadId);
|
||||||
|
|
||||||
QString attachmentPath() const;
|
QString attachmentPath() const;
|
||||||
void setAttachmentPath(const QString &attachmentPath);
|
void setAttachmentPath(const QString &attachmentPath);
|
||||||
|
|
||||||
@@ -173,12 +187,14 @@ public:
|
|||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void textChanged();
|
void textChanged();
|
||||||
void relationIdChanged();
|
void relationIdChanged();
|
||||||
|
void threadIdChanged();
|
||||||
void attachmentPathChanged();
|
void attachmentPathChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_text = QString();
|
QString m_text = QString();
|
||||||
QString m_relationId = QString();
|
QString m_relationId = QString();
|
||||||
RelationType m_relationType = RelationType::None;
|
RelationType m_relationType = RelationType::None;
|
||||||
|
QString m_threadId = QString();
|
||||||
QString m_attachmentPath = QString();
|
QString m_attachmentPath = QString();
|
||||||
QList<Mention> m_mentions;
|
QList<Mention> m_mentions;
|
||||||
QString m_savedText;
|
QString m_savedText;
|
||||||
|
|||||||
@@ -901,6 +901,28 @@ QVariantMap EventHandler::getReplyMediaInfo() const
|
|||||||
return getMediaInfoForEvent(replyPtr);
|
return getMediaInfoForEvent(replyPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EventHandler::isThreaded() const
|
||||||
|
{
|
||||||
|
return (m_event->contentPart<QJsonObject>("m.relates_to"_ls).contains("rel_type"_ls)
|
||||||
|
&& m_event->contentPart<QJsonObject>("m.relates_to"_ls)["rel_type"_ls].toString() == "m.thread"_ls)
|
||||||
|
|| (!m_event->unsignedPart<QJsonObject>("m.relations"_ls).isEmpty() && m_event->unsignedPart<QJsonObject>("m.relations"_ls).contains("m.thread"_ls));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString EventHandler::threadRoot() const
|
||||||
|
{
|
||||||
|
// Get the thread root ID from m.relates_to if it exists.
|
||||||
|
if (m_event->contentPart<QJsonObject>("m.relates_to"_ls).contains("rel_type"_ls)
|
||||||
|
&& m_event->contentPart<QJsonObject>("m.relates_to"_ls)["rel_type"_ls].toString() == "m.thread"_ls) {
|
||||||
|
return m_event->contentPart<QJsonObject>("m.relates_to"_ls)["event_id"_ls].toString();
|
||||||
|
}
|
||||||
|
// For thread root events they have an m.relations in the unsigned part with a m.thread object.
|
||||||
|
// If so return the event ID as it is the root.
|
||||||
|
if (!m_event->unsignedPart<QJsonObject>("m.relations"_ls).isEmpty() && m_event->unsignedPart<QJsonObject>("m.relations"_ls).contains("m.thread"_ls)) {
|
||||||
|
return getId();
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
float EventHandler::getLatitude() const
|
float EventHandler::getLatitude() const
|
||||||
{
|
{
|
||||||
const auto geoUri = m_event->contentJson()["geo_uri"_ls].toString();
|
const auto geoUri = m_event->contentJson()["geo_uri"_ls].toString();
|
||||||
|
|||||||
@@ -326,6 +326,20 @@ public:
|
|||||||
*/
|
*/
|
||||||
QVariantMap getReplyMediaInfo() const;
|
QVariantMap getReplyMediaInfo() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Whether the message is part of a thread.
|
||||||
|
*
|
||||||
|
* i.e. There is a rel_type of m.thread.
|
||||||
|
*/
|
||||||
|
bool isThreaded() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the Matrix ID of the thread's root message.
|
||||||
|
*
|
||||||
|
* Empty if this not part of a thread.
|
||||||
|
*/
|
||||||
|
QString threadRoot() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return the latitude for the event.
|
* @brief Return the latitude for the event.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
|
|||||||
roles[ReplyDelegateTypeRole] = "replyDelegateType";
|
roles[ReplyDelegateTypeRole] = "replyDelegateType";
|
||||||
roles[ReplyDisplayRole] = "replyDisplay";
|
roles[ReplyDisplayRole] = "replyDisplay";
|
||||||
roles[ReplyMediaInfoRole] = "replyMediaInfo";
|
roles[ReplyMediaInfoRole] = "replyMediaInfo";
|
||||||
|
roles[IsThreadedRole] = "isThreaded";
|
||||||
|
roles[ThreadRootRole] = "threadRoot";
|
||||||
roles[ShowAuthorRole] = "showAuthor";
|
roles[ShowAuthorRole] = "showAuthor";
|
||||||
roles[ShowSectionRole] = "showSection";
|
roles[ShowSectionRole] = "showSection";
|
||||||
roles[ReadMarkersRole] = "readMarkers";
|
roles[ReadMarkersRole] = "readMarkers";
|
||||||
@@ -586,6 +588,14 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
return eventHandler.getReplyMediaInfo();
|
return eventHandler.getReplyMediaInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (role == IsThreadedRole) {
|
||||||
|
return eventHandler.isThreaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == ThreadRootRole) {
|
||||||
|
return eventHandler.threadRoot();
|
||||||
|
}
|
||||||
|
|
||||||
if (role == ShowAuthorRole) {
|
if (role == ShowAuthorRole) {
|
||||||
for (auto r = row + 1; r < rowCount(); ++r) {
|
for (auto r = row + 1; r < rowCount(); ++r) {
|
||||||
auto i = index(r);
|
auto i = index(r);
|
||||||
|
|||||||
@@ -63,6 +63,9 @@ public:
|
|||||||
ReplyDisplayRole, /**< The body of the message that was replied to. */
|
ReplyDisplayRole, /**< The body of the message that was replied to. */
|
||||||
ReplyMediaInfoRole, /**< The media info of the message that was replied to. */
|
ReplyMediaInfoRole, /**< The media info of the message that was replied to. */
|
||||||
|
|
||||||
|
IsThreadedRole,
|
||||||
|
ThreadRootRole,
|
||||||
|
|
||||||
ShowAuthorRole, /**< Whether the author's name should be shown. */
|
ShowAuthorRole, /**< Whether the author's name should be shown. */
|
||||||
ShowSectionRole, /**< Whether the section header should be shown. */
|
ShowSectionRole, /**< Whether the section header should be shown. */
|
||||||
|
|
||||||
|
|||||||
@@ -526,20 +526,63 @@ QString msgTypeToString(MessageEventType msgType)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NeoChatRoom::postMessage(const QString &rawText, const QString &text, MessageEventType type, const QString &replyEventId, const QString &relateToEventId)
|
void NeoChatRoom::postMessage(const QString &rawText,
|
||||||
|
const QString &text,
|
||||||
|
MessageEventType type,
|
||||||
|
const QString &replyEventId,
|
||||||
|
const QString &relateToEventId,
|
||||||
|
const QString &threadRootId)
|
||||||
{
|
{
|
||||||
postHtmlMessage(rawText, text, type, replyEventId, relateToEventId);
|
postHtmlMessage(rawText, text, type, replyEventId, relateToEventId, threadRootId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NeoChatRoom::postHtmlMessage(const QString &text, const QString &html, MessageEventType type, const QString &replyEventId, const QString &relateToEventId)
|
void NeoChatRoom::postHtmlMessage(const QString &text,
|
||||||
|
const QString &html,
|
||||||
|
MessageEventType type,
|
||||||
|
const QString &replyEventId,
|
||||||
|
const QString &relateToEventId,
|
||||||
|
const QString &threadRootId)
|
||||||
{
|
{
|
||||||
bool isReply = !replyEventId.isEmpty();
|
bool isReply = !replyEventId.isEmpty();
|
||||||
bool isEdit = !relateToEventId.isEmpty();
|
bool isEdit = !relateToEventId.isEmpty();
|
||||||
|
bool isThread = !threadRootId.isEmpty();
|
||||||
const auto replyIt = findInTimeline(replyEventId);
|
const auto replyIt = findInTimeline(replyEventId);
|
||||||
if (replyIt == historyEdge()) {
|
if (replyIt == historyEdge()) {
|
||||||
isReply = false;
|
isReply = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isThread) {
|
||||||
|
EventHandler eventHandler;
|
||||||
|
eventHandler.setRoom(this);
|
||||||
|
eventHandler.setEvent(&**replyIt);
|
||||||
|
|
||||||
|
const bool isFallingBack = !eventHandler.isThreaded();
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
QJsonObject json{
|
||||||
|
{"msgtype"_ls, msgTypeToString(type)},
|
||||||
|
{"body"_ls, text},
|
||||||
|
{"format"_ls, "org.matrix.custom.html"_ls},
|
||||||
|
{"m.relates_to"_ls,
|
||||||
|
QJsonObject {
|
||||||
|
{"rel_type"_ls, "m.thread"_ls},
|
||||||
|
{"event_id"_ls, threadRootId},
|
||||||
|
{"is_falling_back"_ls, isFallingBack},
|
||||||
|
{"m.in_reply_to"_ls,
|
||||||
|
QJsonObject {
|
||||||
|
{"event_id"_ls, replyEventId}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{"formatted_body"_ls, html}
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
postJson("m.room.message"_ls, json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isEdit) {
|
if (isEdit) {
|
||||||
QJsonObject json{
|
QJsonObject json{
|
||||||
{"type"_ls, "m.room.message"_ls},
|
{"type"_ls, "m.room.message"_ls},
|
||||||
|
|||||||
@@ -884,7 +884,8 @@ public Q_SLOTS:
|
|||||||
const QString &cleanedText,
|
const QString &cleanedText,
|
||||||
Quotient::MessageEventType type = Quotient::MessageEventType::Text,
|
Quotient::MessageEventType type = Quotient::MessageEventType::Text,
|
||||||
const QString &replyEventId = QString(),
|
const QString &replyEventId = QString(),
|
||||||
const QString &relateToEventId = QString());
|
const QString &relateToEventId = QString(),
|
||||||
|
const QString &threadRootId = QString());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send an html message to the room.
|
* @brief Send an html message to the room.
|
||||||
@@ -899,7 +900,8 @@ public Q_SLOTS:
|
|||||||
const QString &html,
|
const QString &html,
|
||||||
Quotient::MessageEventType type = Quotient::MessageEventType::Text,
|
Quotient::MessageEventType type = Quotient::MessageEventType::Text,
|
||||||
const QString &replyEventId = QString(),
|
const QString &replyEventId = QString(),
|
||||||
const QString &relateToEventId = QString());
|
const QString &relateToEventId = QString(),
|
||||||
|
const QString &threadRootId = QString());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the room avatar.
|
* @brief Set the room avatar.
|
||||||
|
|||||||
@@ -105,6 +105,16 @@ QQC2.Control {
|
|||||||
root.currentRoom.editCache.editId = "";
|
root.currentRoom.editCache.editId = "";
|
||||||
root.focusChatBar();
|
root.focusChatBar();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
text: i18n("Reply in Thread")
|
||||||
|
icon.name: "dialog-messages"
|
||||||
|
onTriggered: {
|
||||||
|
root.currentRoom.mainCache.replyId = root.delegate.eventId;
|
||||||
|
root.currentRoom.mainCache.threadId = root.delegate.isThreaded ? root.delegate.threadRoot : root.delegate.eventId;
|
||||||
|
root.currentRoom.editCache.editId = "";
|
||||||
|
root.focusChatBar();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -187,6 +187,10 @@ TimelineDelegate {
|
|||||||
*/
|
*/
|
||||||
required property var replyMediaInfo
|
required property var replyMediaInfo
|
||||||
|
|
||||||
|
required property bool isThreaded
|
||||||
|
|
||||||
|
required property string threadRoot
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Whether this message is replying to another.
|
* @brief Whether this message is replying to another.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user