Compare commits
48 Commits
work/nvrwh
...
work/tobia
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4033f07272 | ||
|
|
5df4fa297d | ||
|
|
33c5b418d2 | ||
|
|
69d378a17b | ||
|
|
f1b1b8ce53 | ||
|
|
526d4748e0 | ||
|
|
c5f93adbf4 | ||
|
|
61630cbe90 | ||
|
|
8c435e9d6d | ||
|
|
57978b1a6e | ||
|
|
f3c4d9449a | ||
|
|
9b37777f20 | ||
|
|
d6d6c161db | ||
|
|
772bca5ba6 | ||
|
|
036a60a095 | ||
|
|
b3315e1ed4 | ||
|
|
d300e9cf52 | ||
|
|
1f4bcd150f | ||
|
|
9ad8894983 | ||
|
|
9e63ca5eb7 | ||
|
|
ade66242bb | ||
|
|
2c6932b4cb | ||
|
|
5cce9e7205 | ||
|
|
7dd3ad9548 | ||
|
|
f690b76efa | ||
|
|
87b8d6710e | ||
|
|
965b890346 | ||
|
|
02b4e5cc70 | ||
|
|
410add04fb | ||
|
|
b575b1e700 | ||
|
|
5828ee1ada | ||
|
|
a64c80109e | ||
|
|
de47f7f2fa | ||
|
|
19adc7b9e5 | ||
|
|
23f60a59fe | ||
|
|
946ba2e56d | ||
|
|
9dcb7b49fa | ||
|
|
52f5901642 | ||
|
|
d4b4a7e1ff | ||
|
|
4449678b74 | ||
|
|
19e197e0ec | ||
|
|
cfc5202645 | ||
|
|
9b80d9e7aa | ||
|
|
dc409387bd | ||
|
|
1e73a7bda4 | ||
|
|
ef1d62d45c | ||
|
|
c2d82750b1 | ||
|
|
c97d276b36 |
@@ -13,6 +13,7 @@ Dependencies:
|
||||
'frameworks/kitemmodels': '@latest-kf6'
|
||||
'frameworks/kquickcharts': '@latest-kf6'
|
||||
'frameworks/knotifications': '@latest-kf6'
|
||||
'frameworks/kcolorscheme': '@latest-kf6'
|
||||
'libraries/kquickimageeditor': '@latest-kf6'
|
||||
'frameworks/sonnet': '@latest-kf6'
|
||||
'libraries/kirigami-addons': '@latest-kf6'
|
||||
@@ -25,7 +26,6 @@ Dependencies:
|
||||
'frameworks/qqc2-desktop-style': '@latest-kf6'
|
||||
'frameworks/kio': '@latest-kf6'
|
||||
'frameworks/kwindowsystem': '@latest-kf6'
|
||||
'frameworks/kconfigwidgets': '@latest-kf6'
|
||||
- 'on': ['Linux', 'FreeBSD']
|
||||
'require':
|
||||
'frameworks/kdbusaddons': '@latest-kf6'
|
||||
|
||||
@@ -57,7 +57,7 @@ set_package_properties(Qt6 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Basic application components"
|
||||
)
|
||||
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami2 I18n Notifications Config CoreAddons Sonnet ItemModels)
|
||||
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami2 I18n Notifications Config CoreAddons Sonnet ItemModels ColorScheme)
|
||||
set_package_properties(KF6 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Basic application components"
|
||||
@@ -76,7 +76,7 @@ if(ANDROID)
|
||||
)
|
||||
else()
|
||||
find_package(Qt6 ${QT_MIN_VERSION} COMPONENTS Widgets)
|
||||
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle ConfigWidgets KIO WindowSystem StatusNotifierItem)
|
||||
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle KIO WindowSystem StatusNotifierItem)
|
||||
set_package_properties(KF6QQC2DesktopStyle PROPERTIES
|
||||
TYPE RUNTIME
|
||||
)
|
||||
|
||||
124
autotests/data/test-texthandler-sync.json
Normal file
124
autotests/data/test-texthandler-sync.json
Normal file
@@ -0,0 +1,124 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
},
|
||||
"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** text message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<b>This is an example text message</b>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "/me This is an emote.",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "This is an emote.",
|
||||
"msgtype": "m.emote"
|
||||
},
|
||||
"event_id": "$153273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1532735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1231
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "tested",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$zrCiBxBnqqTn0Z5FY78qSZAszno_w8nJJXzfBULG-3E",
|
||||
"origin_server_ts": 1680948575928,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1747776,
|
||||
"m.relations": {
|
||||
"m.replace": {
|
||||
"event_id": "$UX0PlpyI7vYO32iHMuuYEP7ECMh4sX3XLGiB2SwM4mQ",
|
||||
"origin_server_ts": 1680948580992,
|
||||
"sender": "@example:example.org"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
}
|
||||
@@ -39,34 +39,61 @@ private:
|
||||
Connection *connection = nullptr;
|
||||
TestRoom *room = nullptr;
|
||||
EventHandler eventHandler;
|
||||
EventHandler emptyHandler;
|
||||
EventHandler noEventHandler;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void nullSetEvent();
|
||||
void eventId();
|
||||
void nullEventId();
|
||||
void delegateType_data();
|
||||
void delegateType();
|
||||
void nullDelegateType();
|
||||
void author();
|
||||
void nullAuthor();
|
||||
void authorDisplayName();
|
||||
void nullAuthorDisplayName();
|
||||
void time();
|
||||
void nullTime();
|
||||
void timeString();
|
||||
void nullTimeString();
|
||||
void highlighted();
|
||||
void nullHighlighted();
|
||||
void hidden();
|
||||
void nullHidden();
|
||||
void body();
|
||||
void nullBody();
|
||||
void genericBody_data();
|
||||
void genericBody();
|
||||
void nullGenericBody();
|
||||
void mediaInfo();
|
||||
void nullMediaInfo();
|
||||
void linkPreviewer();
|
||||
void nullLinkPreviewer();
|
||||
void reactions();
|
||||
void nullReactions();
|
||||
void hasReply();
|
||||
void nullHasReply();
|
||||
void replyId();
|
||||
void nullReplyId();
|
||||
void replyDelegateType();
|
||||
void nullReplyDelegateType();
|
||||
void replyAuthor();
|
||||
void nullReplyAuthor();
|
||||
void replyBody();
|
||||
void nullReplyBody();
|
||||
void replyMediaInfo();
|
||||
void nullReplyMediaInfo();
|
||||
void thread();
|
||||
void nullThread();
|
||||
void location();
|
||||
void nullLocation();
|
||||
void readMarkers();
|
||||
void nullReadMarkers();
|
||||
|
||||
void cleanup();
|
||||
};
|
||||
|
||||
void EventHandlerTest::initTestCase()
|
||||
@@ -82,6 +109,13 @@ void EventHandlerTest::initTestCase()
|
||||
room->update(std::move(roomData));
|
||||
|
||||
eventHandler.setRoom(room);
|
||||
noEventHandler.setRoom(room);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullSetEvent()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "cannot setEvent when m_room is set to nullptr.");
|
||||
emptyHandler.setEvent(room->messageEvents().at(0).get());
|
||||
}
|
||||
|
||||
void EventHandlerTest::eventId()
|
||||
@@ -91,6 +125,12 @@ void EventHandlerTest::eventId()
|
||||
QCOMPARE(eventHandler.getId(), QStringLiteral("$153456789:example.org"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullEventId()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getId called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getId(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::delegateType_data()
|
||||
{
|
||||
QTest::addColumn<int>("eventNum");
|
||||
@@ -114,6 +154,12 @@ void EventHandlerTest::delegateType()
|
||||
QCOMPARE(eventHandler.getDelegateType(), delegateType);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullDelegateType()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getDelegateType called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getDelegateType(), DelegateType::Other);
|
||||
}
|
||||
|
||||
void EventHandlerTest::author()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
@@ -131,6 +177,15 @@ void EventHandlerTest::author()
|
||||
QCOMPARE(eventHandlerAuthor["object"_ls], QVariant::fromValue(author));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullAuthor()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getAuthor called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getAuthor(), QVariantMap());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getAuthor called with m_event set to nullptr. Returning empty user.");
|
||||
QCOMPARE(noEventHandler.getAuthor(), room->getUser(nullptr));
|
||||
}
|
||||
|
||||
void EventHandlerTest::authorDisplayName()
|
||||
{
|
||||
auto event = room->messageEvents().at(1).get();
|
||||
@@ -139,6 +194,15 @@ void EventHandlerTest::authorDisplayName()
|
||||
QCOMPARE(eventHandler.getAuthorDisplayName(), QStringLiteral("before"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullAuthorDisplayName()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getAuthorDisplayName called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getAuthorDisplayName(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getAuthorDisplayName called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getAuthorDisplayName(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::time()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
@@ -148,6 +212,16 @@ void EventHandlerTest::time()
|
||||
QCOMPARE(eventHandler.getTime(true, QDateTime::fromMSecsSinceEpoch(1234, Qt::UTC)), QDateTime::fromMSecsSinceEpoch(1234, Qt::UTC));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullTime()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getTime called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getTime(), QDateTime());
|
||||
|
||||
eventHandler.setEvent(room->messageEvents().at(0).get());
|
||||
QTest::ignoreMessage(QtWarningMsg, "a value must be provided for lastUpdated for a pending event.");
|
||||
QCOMPARE(eventHandler.getTime(true), QDateTime());
|
||||
}
|
||||
|
||||
void EventHandlerTest::timeString()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
@@ -169,6 +243,16 @@ void EventHandlerTest::timeString()
|
||||
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().date(), QLocale::LongFormat));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullTimeString()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getTimeString called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getTimeString(false), QString());
|
||||
|
||||
eventHandler.setEvent(room->messageEvents().at(0).get());
|
||||
QTest::ignoreMessage(QtWarningMsg, "a value must be provided for lastUpdated for a pending event.");
|
||||
QCOMPARE(eventHandler.getTimeString(false, QLocale::ShortFormat, true), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::highlighted()
|
||||
{
|
||||
auto event = room->messageEvents().at(2).get();
|
||||
@@ -182,6 +266,15 @@ void EventHandlerTest::highlighted()
|
||||
QCOMPARE(eventHandler.isHighlighted(), false);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullHighlighted()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "isHighlighted called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.isHighlighted(), false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "isHighlighted called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.isHighlighted(), false);
|
||||
}
|
||||
|
||||
void EventHandlerTest::hidden()
|
||||
{
|
||||
auto event = room->messageEvents().at(3).get();
|
||||
@@ -195,6 +288,15 @@ void EventHandlerTest::hidden()
|
||||
QCOMPARE(eventHandler.isHidden(), false);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullHidden()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "isHidden called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.isHidden(), false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "isHidden called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.isHidden(), false);
|
||||
}
|
||||
|
||||
void EventHandlerTest::body()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
@@ -206,6 +308,15 @@ void EventHandlerTest::body()
|
||||
QCOMPARE(eventHandler.getPlainBody(true), QStringLiteral("This is an example text message"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullBody()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getRichBody called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getRichBody(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getPlainBody called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getPlainBody(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::genericBody_data()
|
||||
{
|
||||
QTest::addColumn<int>("eventNum");
|
||||
@@ -228,6 +339,12 @@ void EventHandlerTest::genericBody()
|
||||
QCOMPARE(eventHandler.getGenericBody(), output);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullGenericBody()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getGenericBody called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getGenericBody(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::mediaInfo()
|
||||
{
|
||||
auto event = room->messageEvents().at(4).get();
|
||||
@@ -251,6 +368,15 @@ void EventHandlerTest::mediaInfo()
|
||||
QCOMPARE(thumbnailInfo["height"_ls], 450);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullMediaInfo()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getMediaInfo called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getMediaInfo(), QVariantMap());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getMediaInfo called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getMediaInfo(), QVariantMap());
|
||||
}
|
||||
|
||||
void EventHandlerTest::linkPreviewer()
|
||||
{
|
||||
auto event = room->messageEvents().at(2).get();
|
||||
@@ -264,6 +390,15 @@ void EventHandlerTest::linkPreviewer()
|
||||
QCOMPARE(eventHandler.getLinkPreviewer(), nullptr);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullLinkPreviewer()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getLinkPreviewer called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getLinkPreviewer(), nullptr);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getLinkPreviewer called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getLinkPreviewer(), nullptr);
|
||||
}
|
||||
|
||||
void EventHandlerTest::reactions()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
@@ -272,6 +407,15 @@ void EventHandlerTest::reactions()
|
||||
QCOMPARE(eventHandler.getReactions()->rowCount(), 1);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReactions()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReactions called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReactions(), nullptr);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReactions called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReactions(), nullptr);
|
||||
}
|
||||
|
||||
void EventHandlerTest::hasReply()
|
||||
{
|
||||
auto event = room->messageEvents().at(5).get();
|
||||
@@ -285,6 +429,12 @@ void EventHandlerTest::hasReply()
|
||||
QCOMPARE(eventHandler.hasReply(), false);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullHasReply()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "hasReply called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.hasReply(), false);
|
||||
}
|
||||
|
||||
void EventHandlerTest::replyId()
|
||||
{
|
||||
auto event = room->messageEvents().at(5).get();
|
||||
@@ -298,6 +448,12 @@ void EventHandlerTest::replyId()
|
||||
QCOMPARE(eventHandler.getReplyId(), QStringLiteral(""));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReplyId()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyId called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReplyId(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::replyDelegateType()
|
||||
{
|
||||
auto event = room->messageEvents().at(5).get();
|
||||
@@ -311,6 +467,15 @@ void EventHandlerTest::replyDelegateType()
|
||||
QCOMPARE(eventHandler.getReplyDelegateType(), DelegateType::Other);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReplyDelegateType()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyDelegateType called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReplyDelegateType(), DelegateType::Other);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyDelegateType called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReplyDelegateType(), DelegateType::Other);
|
||||
}
|
||||
|
||||
void EventHandlerTest::replyAuthor()
|
||||
{
|
||||
auto event = room->messageEvents().at(5).get();
|
||||
@@ -334,6 +499,15 @@ void EventHandlerTest::replyAuthor()
|
||||
QCOMPARE(eventHandler.getReplyAuthor(), room->getUser(nullptr));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReplyAuthor()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyAuthor called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReplyAuthor(), QVariantMap());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyAuthor called with m_event set to nullptr. Returning empty user.");
|
||||
QCOMPARE(noEventHandler.getReplyAuthor(), room->getUser(nullptr));
|
||||
}
|
||||
|
||||
void EventHandlerTest::replyBody()
|
||||
{
|
||||
auto event = room->messageEvents().at(5).get();
|
||||
@@ -345,6 +519,15 @@ void EventHandlerTest::replyBody()
|
||||
QCOMPARE(eventHandler.getReplyPlainBody(true), QStringLiteral("This is an example text message"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReplyBody()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyRichBody called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReplyRichBody(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyPlainBody called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReplyPlainBody(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::replyMediaInfo()
|
||||
{
|
||||
auto event = room->messageEvents().at(6).get();
|
||||
@@ -369,6 +552,15 @@ void EventHandlerTest::replyMediaInfo()
|
||||
QCOMPARE(thumbnailInfo["height"_ls], 450);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReplyMediaInfo()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyMediaInfo called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReplyMediaInfo(), QVariantMap());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyMediaInfo called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReplyMediaInfo(), QVariantMap());
|
||||
}
|
||||
|
||||
void EventHandlerTest::thread()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
@@ -392,6 +584,15 @@ void EventHandlerTest::thread()
|
||||
QCOMPARE(eventHandler.getReplyId(), QStringLiteral("$threadmessage1:example.org"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullThread()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "isThreaded called with m_event set to nullptr.");
|
||||
QCOMPARE(emptyHandler.isThreaded(), false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "threadRoot called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.threadRoot(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::location()
|
||||
{
|
||||
auto event = room->messageEvents().at(7).get();
|
||||
@@ -402,6 +603,18 @@ void EventHandlerTest::location()
|
||||
QCOMPARE(eventHandler.getLocationAssetType(), QStringLiteral("m.pin"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullLocation()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getLatitude called with m_event set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getLatitude(), -100.0);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getLongitude called with m_event set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getLongitude(), -200.0);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getLocationAssetType called with m_event set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getLocationAssetType(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::readMarkers()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
@@ -431,5 +644,37 @@ void EventHandlerTest::readMarkers()
|
||||
QCOMPARE(eventHandler.getReadMarkersString().startsWith(QStringLiteral("6 users:")), true);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReadMarkers()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "hasReadMarkers called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.hasReadMarkers(), false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkers called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReadMarkers(), QVariantList());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getNumberExcessReadMarkers called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getNumberExcessReadMarkers(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkersString called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReadMarkersString(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "hasReadMarkers called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.hasReadMarkers(), false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkers called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReadMarkers(), QVariantList());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getNumberExcessReadMarkers called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getNumberExcessReadMarkers(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkersString called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReadMarkersString(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::cleanup()
|
||||
{
|
||||
eventHandler.setEvent(nullptr);
|
||||
}
|
||||
|
||||
QTEST_MAIN(EventHandlerTest)
|
||||
#include "eventhandlertest.moc"
|
||||
|
||||
@@ -42,101 +42,18 @@ void NeoChatRoomTest::initTestCase()
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
||||
|
||||
auto json = QJsonDocument::fromJson(R"EVENT({
|
||||
"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"
|
||||
}
|
||||
]
|
||||
},
|
||||
"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** text message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<b>This is an example text message</b>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1235
|
||||
}
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
})EVENT");
|
||||
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, json.object());
|
||||
QFile testMinSyncFile;
|
||||
testMinSyncFile.setFileName(QLatin1String(DATA_DIR) + u'/' + QLatin1String("test-min-sync.json"));
|
||||
testMinSyncFile.open(QIODevice::ReadOnly);
|
||||
const auto testMinSyncJson = QJsonDocument::fromJson(testMinSyncFile.readAll());
|
||||
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, testMinSyncJson.object());
|
||||
room->update(std::move(roomData));
|
||||
}
|
||||
|
||||
void NeoChatRoomTest::subtitleTextTest()
|
||||
{
|
||||
QCOMPARE(room->timelineSize(), 1);
|
||||
QCOMPARE(room->lastEventToString(), QStringLiteral("@example:example.org: This is an example text message"));
|
||||
QCOMPARE(room->lastEventToString(), QStringLiteral("@example:example.org: This is an example\ntext message"));
|
||||
}
|
||||
|
||||
void NeoChatRoomTest::eventTest()
|
||||
|
||||
@@ -66,6 +66,7 @@ private Q_SLOTS:
|
||||
void receiveRichEdited_data();
|
||||
void receiveRichEdited();
|
||||
void receiveLineSeparator();
|
||||
void receiveRichCodeUrl();
|
||||
|
||||
void linkPreviewsMatch_data();
|
||||
void linkPreviewsMatch();
|
||||
@@ -78,131 +79,11 @@ void TextHandlerTest::initTestCase()
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
||||
|
||||
const auto json = QJsonDocument::fromJson(R"EVENT({
|
||||
"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"
|
||||
}
|
||||
]
|
||||
},
|
||||
"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** text message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<b>This is an example text message</b>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "/me This is an emote.",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "This is an emote.",
|
||||
"msgtype": "m.emote"
|
||||
},
|
||||
"event_id": "$153273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1532735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1231
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "tested",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$zrCiBxBnqqTn0Z5FY78qSZAszno_w8nJJXzfBULG-3E",
|
||||
"origin_server_ts": 1680948575928,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1747776,
|
||||
"m.relations": {
|
||||
"m.replace": {
|
||||
"event_id": "$UX0PlpyI7vYO32iHMuuYEP7ECMh4sX3XLGiB2SwM4mQ",
|
||||
"origin_server_ts": 1680948580992,
|
||||
"sender": "@example:example.org"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
})EVENT");
|
||||
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, json.object());
|
||||
QFile testTextHandlerSyncFile;
|
||||
testTextHandlerSyncFile.setFileName(QLatin1String(DATA_DIR) + u'/' + QLatin1String("test-texthandler-sync.json"));
|
||||
testTextHandlerSyncFile.open(QIODevice::ReadOnly);
|
||||
const auto testTextHandlerSyncJson = QJsonDocument::fromJson(testTextHandlerSyncFile.readAll());
|
||||
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, testTextHandlerSyncJson.object());
|
||||
room->update(std::move(roomData));
|
||||
}
|
||||
|
||||
@@ -647,5 +528,13 @@ void TextHandlerTest::linkPreviewsReject()
|
||||
QCOMPARE(testTextHandler.getLinkPreviews(), testOutputLinks);
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveRichCodeUrl()
|
||||
{
|
||||
auto input = QStringLiteral("<code>https://kde.org</code>");
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(input);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), input);
|
||||
}
|
||||
|
||||
QTEST_MAIN(TextHandlerTest)
|
||||
#include "texthandlertest.moc"
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
</provides>
|
||||
<name>NeoChat</name>
|
||||
<name xml:lang="ar">نيوتشات</name>
|
||||
<name xml:lang="ast">NeoChat</name>
|
||||
<name xml:lang="az">NeoChat</name>
|
||||
<name xml:lang="ca">NeoChat</name>
|
||||
<name xml:lang="ca-valencia">NeoChat</name>
|
||||
@@ -98,7 +99,7 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<p>NeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.</p>
|
||||
<p xml:lang="ar">يهدف نيوتشات إلى أن يكون تطبيقًا كامل الميزات لمواصفات ماتركس. على هذا النحو يتم دعم كل شيء في المواصفات المستقرة الحالية مع الاستثناءات الملحوظة لـ VoIP والخيوط وبعض جوانب التشفير من طرف إلى طرف. هناك عدد قليل من الإغفالات الصغيرة الأخرى بسبب حقيقة أن مواصفات ماتركس تتطور باستمرار ، ولكن يبقى الهدف توفير الدعم النهائي للمواصفات بأكملها.</p>
|
||||
<p xml:lang="ca">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptatge d'extrem a extrem. Hi ha algunes altres omissions més petites a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu segueix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||
<p xml:lang="ca-valencia">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de VoIP, fils i alguns aspectes de l'encriptació d'extrem a extrem. Hi ha algunes altres omissions més xicotetes a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu seguix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||
<p xml:lang="ca-valencia">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptació d'extrem a extrem. Hi ha algunes altres omissions més xicotetes a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu seguix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||
<p xml:lang="en-GB">NeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.</p>
|
||||
<p xml:lang="eo">NeoChat celas esti plene kapabla aplikaĵo por la Matrix-specifo. Kiel tia, ĉio en la nuna stabila specifo kun la rimarkindaj esceptoj de VoIP, fadenoj kaj kelkaj aspektoj de Fin-al-Fina Ĉifrado estas subtenataj. Estas kelkaj aliaj pli malgrandaj preterlasoj pro la fakto, ke la Matrix-speco konstante evoluas, sed la celo restas provizi finfine subtenon por la tuta specifaĵo.</p>
|
||||
<p xml:lang="es">NeoChat pretende ser una aplicación con todas las funciones para la especificación de Matrix. Como tal, admite todo en la especificación estable actual, con las notables excepciones de VoIP, subprocesos y algunas funciones de cifrado de extremo a extremo. Existen algunas omisiones menos importantes debido al hecho de que la especificación de Matrix está en constante evolución, pero el objetivo sigue siendo brindar compatibilidad final con toda la especificación.</p>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
Version=1.5
|
||||
Name=NeoChat
|
||||
Name[ar]=نيوتشات
|
||||
Name[ast]=NeoChat
|
||||
Name[az]=NeoChat
|
||||
Name[ca]=NeoChat
|
||||
Name[ca@valencia]=NeoChat
|
||||
|
||||
590
po/ar/neochat.po
590
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
4353
po/ast/neochat.po
Normal file
4353
po/ast/neochat.po
Normal file
File diff suppressed because it is too large
Load Diff
600
po/az/neochat.po
600
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
641
po/ca/neochat.po
641
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
590
po/cs/neochat.po
590
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
638
po/da/neochat.po
638
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
612
po/de/neochat.po
612
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
606
po/el/neochat.po
606
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
606
po/eo/neochat.po
606
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
612
po/es/neochat.po
612
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
608
po/eu/neochat.po
608
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
588
po/fi/neochat.po
588
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
632
po/fr/neochat.po
632
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
608
po/hu/neochat.po
608
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
614
po/ia/neochat.po
614
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
608
po/id/neochat.po
608
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
594
po/ie/neochat.po
594
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
678
po/it/neochat.po
678
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
632
po/ja/neochat.po
632
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
606
po/ka/neochat.po
606
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
592
po/ko/neochat.po
592
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
632
po/lt/neochat.po
632
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
614
po/nl/neochat.po
614
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
602
po/nn/neochat.po
602
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
598
po/pa/neochat.po
598
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
621
po/pl/neochat.po
621
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
602
po/pt/neochat.po
602
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
612
po/ru/neochat.po
612
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
600
po/sk/neochat.po
600
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
614
po/sl/neochat.po
614
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
606
po/sv/neochat.po
606
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
729
po/ta/neochat.po
729
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
608
po/tr/neochat.po
608
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
612
po/uk/neochat.po
612
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
@@ -137,6 +137,8 @@ add_library(neochat STATIC
|
||||
roomlastmessageprovider.h
|
||||
chatbarcache.cpp
|
||||
chatbarcache.h
|
||||
colorschemer.cpp
|
||||
colorschemer.h
|
||||
)
|
||||
|
||||
qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
@@ -161,14 +163,13 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/ImageEditorPage.qml
|
||||
qml/WelcomePage.qml
|
||||
qml/General.qml
|
||||
qml/Security.qml
|
||||
qml/RoomSecurity.qml
|
||||
qml/PushNotification.qml
|
||||
qml/Categories.qml
|
||||
qml/Permissions.qml
|
||||
qml/NeochatMaximizeComponent.qml
|
||||
qml/FancyEffectsContainer.qml
|
||||
qml/TypingPane.qml
|
||||
qml/ShimmerGradient.qml
|
||||
qml/QuickSwitcher.qml
|
||||
qml/HoverActions.qml
|
||||
qml/ChatBox.qml
|
||||
@@ -326,15 +327,13 @@ ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png)
|
||||
target_sources(neochat-app PRIVATE ${NEOCHAT_ICON})
|
||||
|
||||
if(NOT ANDROID)
|
||||
target_sources(neochat PRIVATE colorschemer.cpp colorschemer.h)
|
||||
if (NOT WIN32 AND NOT APPLE)
|
||||
target_sources(neochat PRIVATE trayicon_sni.cpp trayicon_sni.h)
|
||||
target_link_libraries(neochat PRIVATE KF6::StatusNotifierItem)
|
||||
else()
|
||||
target_sources(neochat PRIVATE trayicon.cpp trayicon.h)
|
||||
endif()
|
||||
target_link_libraries(neochat PUBLIC KF6::ConfigWidgets KF6::WindowSystem ICU::uc)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_COLORSCHEME)
|
||||
target_link_libraries(neochat PUBLIC KF6::WindowSystem ICU::uc)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_WINDOWSYSTEM)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_ICU)
|
||||
endif()
|
||||
@@ -346,7 +345,27 @@ if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||
endif()
|
||||
|
||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models ${CMAKE_CURRENT_SOURCE_DIR}/enums)
|
||||
target_link_libraries(neochat PUBLIC Qt::Core Qt::Quick Qt::Qml Qt::Gui Qt::Multimedia Qt::Network Qt::QuickControls2 KF6::I18n KF6::Kirigami2 KF6::Notifications KF6::ConfigCore KF6::ConfigGui KF6::CoreAddons KF6::SonnetCore KF6::ItemModels QuotientQt6 cmark::cmark QCoro::Core)
|
||||
target_link_libraries(neochat PUBLIC
|
||||
Qt::Core
|
||||
Qt::Quick
|
||||
Qt::Qml
|
||||
Qt::Gui
|
||||
Qt::Multimedia
|
||||
Qt::Network
|
||||
Qt::QuickControls2
|
||||
KF6::I18n
|
||||
KF6::Kirigami2
|
||||
KF6::Notifications
|
||||
KF6::ConfigCore
|
||||
KF6::ConfigGui
|
||||
KF6::CoreAddons
|
||||
KF6::SonnetCore
|
||||
KF6::ColorScheme
|
||||
KF6::ItemModels
|
||||
QuotientQt6
|
||||
cmark::cmark
|
||||
QCoro::Core
|
||||
)
|
||||
|
||||
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ void ActionsHandler::setRoom(NeoChatRoom *room)
|
||||
}
|
||||
|
||||
m_room = room;
|
||||
Q_EMIT roomChanged();
|
||||
}
|
||||
|
||||
void ActionsHandler::handleMessageEvent(ChatBarCache *chatBarCache)
|
||||
|
||||
@@ -35,18 +35,20 @@ class ActionsHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
public:
|
||||
explicit ActionsHandler(QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief The room that messages will be sent to.
|
||||
*/
|
||||
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
|
||||
|
||||
public:
|
||||
explicit ActionsHandler(QObject *parent = nullptr);
|
||||
|
||||
[[nodiscard]] NeoChatRoom *room() const;
|
||||
void setRoom(NeoChatRoom *room);
|
||||
|
||||
Q_SIGNALS:
|
||||
void roomChanged();
|
||||
void showEffect(const QString &effect);
|
||||
|
||||
public Q_SLOTS:
|
||||
|
||||
@@ -6,13 +6,8 @@
|
||||
|
||||
#include <qt6keychain/keychain.h>
|
||||
|
||||
#include <KConfig>
|
||||
#include <KConfigGroup>
|
||||
#include <KLocalizedString>
|
||||
#include <KWindowConfig>
|
||||
#ifdef HAVE_WINDOWSYSTEM
|
||||
#include <KWindowEffects>
|
||||
#endif
|
||||
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
@@ -384,56 +379,6 @@ void Controller::joinRoom(const QString &alias)
|
||||
RoomManager::instance().joinRoom(m_connection, alias, QStringList{knownServer});
|
||||
}
|
||||
|
||||
void Controller::openOrCreateDirectChat(User *user)
|
||||
{
|
||||
const auto existing = activeConnection()->directChats();
|
||||
|
||||
if (existing.contains(user)) {
|
||||
const auto &room = static_cast<NeoChatRoom *>(activeConnection()->room(existing.value(user)));
|
||||
if (room) {
|
||||
RoomManager::instance().enterRoom(room);
|
||||
return;
|
||||
}
|
||||
}
|
||||
activeConnection()->requestDirectChat(user);
|
||||
}
|
||||
|
||||
QString Controller::formatByteSize(double size, int precision) const
|
||||
{
|
||||
return QLocale().formattedDataSize(size, precision);
|
||||
}
|
||||
|
||||
QString Controller::formatDuration(quint64 msecs, KFormat::DurationFormatOptions options) const
|
||||
{
|
||||
return KFormat().formatDuration(msecs, options);
|
||||
}
|
||||
|
||||
void Controller::setBlur(QQuickItem *item, bool blur)
|
||||
{
|
||||
#ifdef HAVE_WINDOWSYSTEM
|
||||
auto setWindows = [item, blur]() {
|
||||
auto reg = QRect(QPoint(0, 0), item->window()->size());
|
||||
KWindowEffects::enableBackgroundContrast(item->window(), blur, 1, 1, 1, reg);
|
||||
KWindowEffects::enableBlurBehind(item->window(), blur, reg);
|
||||
};
|
||||
|
||||
disconnect(item->window(), &QQuickWindow::heightChanged, this, nullptr);
|
||||
disconnect(item->window(), &QQuickWindow::widthChanged, this, nullptr);
|
||||
connect(item->window(), &QQuickWindow::heightChanged, this, setWindows);
|
||||
connect(item->window(), &QQuickWindow::widthChanged, this, setWindows);
|
||||
setWindows();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Controller::hasWindowSystem() const
|
||||
{
|
||||
#ifdef HAVE_WINDOWSYSTEM
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Controller::forceRefreshTextDocument(QQuickTextDocument *textDocument, QQuickItem *item)
|
||||
{
|
||||
// HACK: Workaround bug QTBUG 93281
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickItem>
|
||||
|
||||
#include <KFormat>
|
||||
|
||||
#include "neochatconnection.h"
|
||||
#include <Quotient/accountregistry.h>
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
@@ -64,11 +62,6 @@ class Controller : public QObject
|
||||
*/
|
||||
Q_PROPERTY(bool supportSystemTray READ supportSystemTray CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief Whether KWindowSystem specific features are available.
|
||||
*/
|
||||
Q_PROPERTY(bool hasWindowSystem READ hasWindowSystem CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief Whether NeoChat is currently able to connect to the server.
|
||||
*/
|
||||
@@ -126,20 +119,8 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE void joinRoom(const QString &alias);
|
||||
|
||||
/**
|
||||
* @brief Join a direct chat with the given user.
|
||||
*
|
||||
* If a direct chat with the user doesn't exist one is created and then joined.
|
||||
*/
|
||||
Q_INVOKABLE void openOrCreateDirectChat(Quotient::User *user);
|
||||
|
||||
[[nodiscard]] bool supportSystemTray() const;
|
||||
|
||||
/**
|
||||
* @brief Set the background blur status of the given item.
|
||||
*/
|
||||
Q_INVOKABLE void setBlur(QQuickItem *item, bool blur);
|
||||
|
||||
bool isOnline() const;
|
||||
|
||||
/**
|
||||
@@ -151,20 +132,6 @@ public:
|
||||
|
||||
bool isFlatpak() const;
|
||||
|
||||
/**
|
||||
* @brief Return a string for the input timestamp.
|
||||
*
|
||||
* The output format depends on the KFormat::DurationFormatOptions chosen.
|
||||
*
|
||||
* @sa KFormat::DurationFormatOptions
|
||||
*/
|
||||
Q_INVOKABLE QString formatDuration(quint64 msecs, KFormat::DurationFormatOptions options = KFormat::DefaultDuration) const;
|
||||
|
||||
/**
|
||||
* @brief Return a human readable string for a given input number of bytes.
|
||||
*/
|
||||
Q_INVOKABLE QString formatByteSize(double size, int precision = 1) const;
|
||||
|
||||
/**
|
||||
* @brief Force a QQuickTextDocument to refresh when images are loaded.
|
||||
*
|
||||
@@ -187,8 +154,6 @@ private:
|
||||
bool m_isOnline = true;
|
||||
QMap<Quotient::Room *, int> m_notificationCounts;
|
||||
|
||||
bool hasWindowSystem() const;
|
||||
|
||||
QPointer<PushRuleModel> m_pushRuleModel;
|
||||
Quotient::AccountRegistry m_accountRegistry;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <Quotient/events/stickerevent.h>
|
||||
#include <Quotient/quotient_common.h>
|
||||
|
||||
#include "delegatetype.h"
|
||||
#include "eventhandler_logging.h"
|
||||
#include "events/pollevent.h"
|
||||
#include "linkpreviewer.h"
|
||||
@@ -50,6 +51,10 @@ const Quotient::Event *EventHandler::getEvent() const
|
||||
|
||||
void EventHandler::setEvent(const Quotient::RoomEvent *event)
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "cannot setEvent when m_room is set to nullptr.";
|
||||
return;
|
||||
}
|
||||
if (event == m_event) {
|
||||
return;
|
||||
}
|
||||
@@ -181,7 +186,7 @@ QDateTime EventHandler::getTime(bool isPending, QDateTime lastUpdated) const
|
||||
QString EventHandler::getTimeString(bool relative, QLocale::FormatType format, bool isPending, QDateTime lastUpdated) const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getTime called with m_event set to nullptr.";
|
||||
qCWarning(EventHandling) << "getTimeString called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (isPending && lastUpdated == QDateTime()) {
|
||||
@@ -216,6 +221,15 @@ bool EventHandler::isHighlighted()
|
||||
|
||||
bool EventHandler::isHidden()
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "isHidden called with m_room set to nullptr.";
|
||||
return false;
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "isHidden called with m_event set to nullptr.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_event->isStateEvent() && !NeoChatConfig::self()->showStateEvent()) {
|
||||
return true;
|
||||
}
|
||||
@@ -611,6 +625,14 @@ QString EventHandler::getGenericBody() const
|
||||
|
||||
QVariantMap EventHandler::getMediaInfo() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getMediaInfo called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getMediaInfo called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
return getMediaInfoForEvent(m_event);
|
||||
}
|
||||
|
||||
@@ -723,6 +745,14 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const EventContent::FileInfo
|
||||
|
||||
QSharedPointer<LinkPreviewer> EventHandler::getLinkPreviewer() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getLinkPreviewer called with m_room set to nullptr.";
|
||||
return nullptr;
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getLinkPreviewer called with m_event set to nullptr.";
|
||||
return nullptr;
|
||||
}
|
||||
if (!m_event->is<RoomMessageEvent>()) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -754,7 +784,7 @@ QSharedPointer<ReactionModel> EventHandler::getReactions() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getReactions called with m_room set to nullptr.";
|
||||
return {};
|
||||
return nullptr;
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getReactions called with m_event set to nullptr.";
|
||||
@@ -806,16 +836,33 @@ QSharedPointer<ReactionModel> EventHandler::getReactions() const
|
||||
|
||||
bool EventHandler::hasReply() const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "hasReply called with m_event set to nullptr.";
|
||||
return false;
|
||||
}
|
||||
return !m_event->contentJson()["m.relates_to"_ls].toObject()["m.in_reply_to"_ls].toObject()["event_id"_ls].toString().isEmpty();
|
||||
}
|
||||
|
||||
QString EventHandler::getReplyId() const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getReplyId called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
return m_event->contentJson()["m.relates_to"_ls].toObject()["m.in_reply_to"_ls].toObject()["event_id"_ls].toString();
|
||||
}
|
||||
|
||||
DelegateType::Type EventHandler::getReplyDelegateType() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getReplyDelegateType called with m_room set to nullptr.";
|
||||
return DelegateType::Other;
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getReplyDelegateType called with m_event set to nullptr.";
|
||||
return DelegateType::Other;
|
||||
}
|
||||
|
||||
auto replyEvent = m_room->getReplyForEvent(*m_event);
|
||||
if (replyEvent == nullptr) {
|
||||
return DelegateType::Other;
|
||||
@@ -903,6 +950,11 @@ QVariantMap EventHandler::getReplyMediaInfo() const
|
||||
|
||||
bool EventHandler::isThreaded() const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "isThreaded called with m_event set to nullptr.";
|
||||
return false;
|
||||
}
|
||||
|
||||
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));
|
||||
@@ -910,6 +962,11 @@ bool EventHandler::isThreaded() const
|
||||
|
||||
QString EventHandler::threadRoot() const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "threadRoot called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@@ -925,6 +982,11 @@ QString EventHandler::threadRoot() const
|
||||
|
||||
float EventHandler::getLatitude() const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getLatitude called with m_event set to nullptr.";
|
||||
return -100.0;
|
||||
}
|
||||
|
||||
const auto geoUri = m_event->contentJson()["geo_uri"_ls].toString();
|
||||
if (geoUri.isEmpty()) {
|
||||
return -100.0; // latitude runs from -90deg to +90deg so -100 is out of range.
|
||||
@@ -935,6 +997,11 @@ float EventHandler::getLatitude() const
|
||||
|
||||
float EventHandler::getLongitude() const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getLongitude called with m_event set to nullptr.";
|
||||
return -200.0;
|
||||
}
|
||||
|
||||
const auto geoUri = m_event->contentJson()["geo_uri"_ls].toString();
|
||||
if (geoUri.isEmpty()) {
|
||||
return -200.0; // longitude runs from -180deg to +180deg so -200 is out of range.
|
||||
@@ -945,6 +1012,11 @@ float EventHandler::getLongitude() const
|
||||
|
||||
QString EventHandler::getLocationAssetType() const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getLocationAssetType called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto assetType = m_event->contentJson()["org.matrix.msc3488.asset"_ls].toObject()["type"_ls].toString();
|
||||
if (assetType.isEmpty()) {
|
||||
return {};
|
||||
@@ -954,6 +1026,15 @@ QString EventHandler::getLocationAssetType() const
|
||||
|
||||
bool EventHandler::hasReadMarkers() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "hasReadMarkers called with m_room set to nullptr.";
|
||||
return false;
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "hasReadMarkers called with m_event set to nullptr.";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds.remove(m_room->localUser()->id());
|
||||
return userIds.size() > 0;
|
||||
@@ -961,6 +1042,15 @@ bool EventHandler::hasReadMarkers() const
|
||||
|
||||
QVariantList EventHandler::getReadMarkers(int maxMarkers) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkers called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkers called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto userIds_temp = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds_temp.remove(m_room->localUser()->id());
|
||||
|
||||
@@ -981,6 +1071,15 @@ QVariantList EventHandler::getReadMarkers(int maxMarkers) const
|
||||
|
||||
QString EventHandler::getNumberExcessReadMarkers(int maxMarkers) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getNumberExcessReadMarkers called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getNumberExcessReadMarkers called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds.remove(m_room->localUser()->id());
|
||||
|
||||
@@ -993,6 +1092,15 @@ QString EventHandler::getNumberExcessReadMarkers(int maxMarkers) const
|
||||
|
||||
QString EventHandler::getReadMarkersString() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkersString called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkersString called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds.remove(m_room->localUser()->id());
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
file.write(buf.constData(), buf.size());
|
||||
file.flush();
|
||||
|
||||
if (oldHandler && (strcmp(context.category, "quotient.e2ee") != 0 || e2eeDebugEnabled)) {
|
||||
if (oldHandler && (!context.category || (strcmp(context.category, "quotient.e2ee") != 0 || e2eeDebugEnabled))) {
|
||||
oldHandler(type, context, message);
|
||||
}
|
||||
}
|
||||
@@ -197,7 +197,6 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt
|
||||
break;
|
||||
case QtFatalMsg:
|
||||
sInstance()->log(QtInfoMsg, context, message);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
12
src/main.cpp
12
src/main.cpp
@@ -35,9 +35,9 @@
|
||||
#include "neochat-version.h"
|
||||
|
||||
#include <Quotient/networkaccessmanager.h>
|
||||
#include <Quotient/util.h>
|
||||
|
||||
#include "blurhashimageprovider.h"
|
||||
#include "colorschemer.h"
|
||||
#include "controller.h"
|
||||
#include "logger.h"
|
||||
#include "matriximageprovider.h"
|
||||
@@ -45,10 +45,6 @@
|
||||
#include "roommanager.h"
|
||||
#include "windowcontroller.h"
|
||||
|
||||
#ifdef HAVE_COLORSCHEME
|
||||
#include "colorschemer.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_RUNNER
|
||||
#include "runner.h"
|
||||
#include <QDBusConnection>
|
||||
@@ -125,7 +121,7 @@ int main(int argc, char *argv[])
|
||||
font.setHintingPreference(QFont::PreferNoHinting);
|
||||
app.setFont(font);
|
||||
#endif
|
||||
KLocalizedString::setApplicationDomain("neochat");
|
||||
KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));
|
||||
|
||||
QGuiApplication::setOrganizationName("KDE"_ls);
|
||||
|
||||
@@ -157,9 +153,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
initLogging();
|
||||
|
||||
#if Quotient_VERSION_MINOR == 8
|
||||
Connection::setEncryptionDefault(true);
|
||||
#endif
|
||||
|
||||
#ifdef NEOCHAT_FLATPAK
|
||||
// Copy over the included FontConfig configuration to the
|
||||
@@ -168,12 +162,10 @@ int main(int argc, char *argv[])
|
||||
QStringLiteral("/var/config/fontconfig/conf.d/99-noto-mono-color-emoji.conf"));
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_COLORSCHEME
|
||||
ColorSchemer colorScheme;
|
||||
if (!NeoChatConfig::self()->colorScheme().isEmpty()) {
|
||||
colorScheme.apply(NeoChatConfig::self()->colorScheme());
|
||||
}
|
||||
#endif
|
||||
|
||||
qml_register_types_org_kde_neochat();
|
||||
qmlRegisterSingletonInstance("org.kde.neochat.config", 1, 0, "Config", NeoChatConfig::self());
|
||||
|
||||
@@ -100,7 +100,9 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
room->setDisplayed();
|
||||
|
||||
for (auto event = m_currentRoom->messageEvents().begin(); event != m_currentRoom->messageEvents().end(); ++event) {
|
||||
createEventObjects(&*event->viewAs<RoomMessageEvent>());
|
||||
if (const auto &roomMessageEvent = &*event->viewAs<RoomMessageEvent>()) {
|
||||
createEventObjects(roomMessageEvent);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_currentRoom->timelineSize() < 10 && !room->allHistoryLoaded()) {
|
||||
@@ -120,9 +122,8 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
for (auto &&event : events) {
|
||||
const RoomMessageEvent *message = dynamic_cast<RoomMessageEvent *>(event.get());
|
||||
|
||||
createEventObjects(message);
|
||||
|
||||
if (message != nullptr) {
|
||||
createEventObjects(message);
|
||||
if (NeoChatConfig::self()->showFancyEffects()) {
|
||||
QString planBody = message->plainBody();
|
||||
// snowflake
|
||||
@@ -158,8 +159,9 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
});
|
||||
connect(m_currentRoom, &Room::aboutToAddHistoricalMessages, this, [this](RoomEventsRange events) {
|
||||
for (auto &event : events) {
|
||||
RoomMessageEvent *message = dynamic_cast<RoomMessageEvent *>(event.get());
|
||||
createEventObjects(message);
|
||||
if (const auto &roomMessageEvent = dynamic_cast<RoomMessageEvent *>(event.get())) {
|
||||
createEventObjects(roomMessageEvent);
|
||||
}
|
||||
}
|
||||
if (rowCount() > 0) {
|
||||
rowBelowInserted = rowCount() - 1; // See #312
|
||||
@@ -228,7 +230,9 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
}
|
||||
const auto eventIt = m_currentRoom->findInTimeline(eventId);
|
||||
if (eventIt != m_currentRoom->historyEdge()) {
|
||||
createEventObjects(static_cast<const RoomMessageEvent *>(&**eventIt));
|
||||
if (const auto &event = dynamic_cast<const RoomMessageEvent *>(&**eventIt)) {
|
||||
createEventObjects(event);
|
||||
}
|
||||
}
|
||||
refreshEventRoles(eventId, {ReactionRole, ShowReactionsRole, Qt::DisplayRole});
|
||||
});
|
||||
@@ -704,10 +708,6 @@ int MessageEventModel::eventIdToRow(const QString &eventID) const
|
||||
|
||||
void MessageEventModel::createEventObjects(const Quotient::RoomMessageEvent *event)
|
||||
{
|
||||
if (event == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto eventId = event->id();
|
||||
|
||||
EventHandler eventHandler;
|
||||
|
||||
@@ -368,6 +368,9 @@ QVariant RoomListModel::data(const QModelIndex &index, int role) const
|
||||
if (role == IsChildSpaceRole) {
|
||||
return SpaceHierarchyCache::instance().isChildSpace(room->id());
|
||||
}
|
||||
if (role == ReplacementIdRole) {
|
||||
return room->successorId();
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ public:
|
||||
RoomIdRole, /**< The room matrix ID. */
|
||||
IsSpaceRole, /**< Whether the room is a space. */
|
||||
IsChildSpaceRole, /**< Whether this space is a child of a different space. */
|
||||
ReplacementIdRole, /**< The room id of the room replacing this one, if any. */
|
||||
};
|
||||
Q_ENUM(EventRoles)
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "sortfilterroomlistmodel.h"
|
||||
|
||||
#include "neochatconnection.h"
|
||||
#include "roomlistmodel.h"
|
||||
#include "spacehierarchycache.h"
|
||||
|
||||
@@ -14,6 +15,10 @@ SortFilterRoomListModel::SortFilterRoomListModel(QObject *parent)
|
||||
connect(this, &SortFilterRoomListModel::filterTextChanged, this, [this]() {
|
||||
invalidateFilter();
|
||||
});
|
||||
connect(this, &SortFilterRoomListModel::sourceModelChanged, this, [this]() {
|
||||
connect(sourceModel(), &QAbstractListModel::rowsInserted, this, &SortFilterRoomListModel::invalidateRowsFilter);
|
||||
connect(sourceModel(), &QAbstractListModel::rowsRemoved, this, &SortFilterRoomListModel::invalidateRowsFilter);
|
||||
});
|
||||
}
|
||||
|
||||
void SortFilterRoomListModel::setRoomSortOrder(SortFilterRoomListModel::RoomSortOrder sortOrder)
|
||||
@@ -78,9 +83,15 @@ bool SortFilterRoomListModel::filterAcceptsRow(int source_row, const QModelIndex
|
||||
{
|
||||
Q_UNUSED(source_parent);
|
||||
|
||||
if (sourceModel()->data(sourceModel()->index(source_row, 0), RoomListModel::JoinStateRole).toString() == QStringLiteral("upgraded")
|
||||
&& dynamic_cast<RoomListModel *>(sourceModel())
|
||||
->connection()
|
||||
->room(sourceModel()->data(sourceModel()->index(source_row, 0), RoomListModel::ReplacementIdRole).toString())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool acceptRoom =
|
||||
sourceModel()->data(sourceModel()->index(source_row, 0), RoomListModel::DisplayNameRole).toString().contains(m_filterText, Qt::CaseInsensitive)
|
||||
&& sourceModel()->data(sourceModel()->index(source_row, 0), RoomListModel::JoinStateRole).toString() != QStringLiteral("upgraded")
|
||||
&& sourceModel()->data(sourceModel()->index(source_row, 0), RoomListModel::IsSpaceRole).toBool() == false;
|
||||
|
||||
if (m_activeSpaceId.isEmpty()) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
IconName=org.kde.neochat
|
||||
Name=NeoChat
|
||||
Name[ar]=نيوتشات
|
||||
Name[ast]=NeoChat
|
||||
Name[az]=NeoChat
|
||||
Name[ca]=NeoChat
|
||||
Name[ca@valencia]=NeoChat
|
||||
|
||||
@@ -220,4 +220,18 @@ void NeoChatConnection::createSpace(const QString &name, const QString &topic, c
|
||||
});
|
||||
}
|
||||
|
||||
void NeoChatConnection::openOrCreateDirectChat(User *user)
|
||||
{
|
||||
const auto existing = directChats();
|
||||
|
||||
if (existing.contains(user)) {
|
||||
const auto room = static_cast<NeoChatRoom *>(this->room(existing.value(user)));
|
||||
if (room) {
|
||||
RoomManager::instance().enterRoom(room);
|
||||
return;
|
||||
}
|
||||
}
|
||||
requestDirectChat(user);
|
||||
}
|
||||
|
||||
#include "moc_neochatconnection.cpp"
|
||||
|
||||
@@ -61,6 +61,13 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE void createSpace(const QString &name, const QString &topic, const QString &parent = {}, bool setChildParent = false);
|
||||
|
||||
/**
|
||||
* @brief Join a direct chat with the given user.
|
||||
*
|
||||
* If a direct chat with the user doesn't exist one is created and then joined.
|
||||
*/
|
||||
Q_INVOKABLE void openOrCreateDirectChat(Quotient::User *user);
|
||||
|
||||
Q_SIGNALS:
|
||||
void labelChanged();
|
||||
};
|
||||
|
||||
@@ -418,30 +418,6 @@ QDateTime NeoChatRoom::lastActiveTime()
|
||||
return messageEvents().rbegin()->get()->originTimestamp();
|
||||
}
|
||||
|
||||
QVariantList NeoChatRoom::getUsers(const QString &keyword, int limit) const
|
||||
{
|
||||
const auto userList = users();
|
||||
QVariantList matchedList;
|
||||
int count = 0;
|
||||
for (const auto u : userList) {
|
||||
if (u->displayname(this).contains(keyword, Qt::CaseInsensitive)) {
|
||||
Quotient::User user(u->id(), u->connection());
|
||||
QVariantMap userVariant{{QStringLiteral("id"), user.id()},
|
||||
{QStringLiteral("displayName"), user.displayname(this)},
|
||||
{QStringLiteral("avatarMediaId"), user.avatarMediaId(this)},
|
||||
{QStringLiteral("color"), Utils::getUserColor(user.hueF())}};
|
||||
|
||||
matchedList.append(QVariant::fromValue(userVariant));
|
||||
count++;
|
||||
if (count == limit) { // -1 is infinite
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matchedList;
|
||||
}
|
||||
|
||||
// An empty user is useful for returning as a model value to avoid properties being undefined.
|
||||
static const QVariantMap emptyUser = {
|
||||
{"isLocalUser"_ls, false},
|
||||
|
||||
@@ -324,28 +324,6 @@ public:
|
||||
|
||||
explicit NeoChatRoom(Quotient::Connection *connection, QString roomId, Quotient::JoinState joinState = {});
|
||||
|
||||
/**
|
||||
* @brief Get a list of users in the context of this room.
|
||||
*
|
||||
* This is different to getting a list of Quotient::User objects
|
||||
* as neither of those can provide details like the displayName or avatarMediaId
|
||||
* without the room context as these can vary from room to room. This function
|
||||
* provides the room context and returns the result as a list of QVariantMap objects.
|
||||
*
|
||||
* @param keyword filters the users based on the displayname containing keyword.
|
||||
* @param limit max number of user returned, -1 is infinite.
|
||||
*
|
||||
* @return a QVariantList containing a QVariantMap for each user with the following
|
||||
* properties:
|
||||
* - id - User ID.
|
||||
* - displayName - Display name in the context of this room.
|
||||
* - avatarMediaId - Avatar id in the context of this room.
|
||||
* - color - Color for the user.
|
||||
*
|
||||
* @sa Quotient::User
|
||||
*/
|
||||
Q_INVOKABLE [[nodiscard]] QVariantList getUsers(const QString &keyword, int limit = -1) const;
|
||||
|
||||
/**
|
||||
* @brief Get a user in the context of this room.
|
||||
*
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
[Desktop Entry]
|
||||
Name=NeoChat
|
||||
Name[ar]=نيوتشات
|
||||
Name[ast]=NeoChat
|
||||
Name[az]=NeoChat
|
||||
Name[ca]=NeoChat
|
||||
Name[ca@valencia]=NeoChat
|
||||
|
||||
@@ -235,7 +235,7 @@ FormCard.FormCardPage {
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
id: hasWindowSystemDelegate
|
||||
visible: Controller.hasWindowSystem
|
||||
visible: WindowController.hasWindowSystem
|
||||
text: i18n("Use transparent chat page")
|
||||
enabled: !Config.compactLayout && !Config.isBlurImmutable
|
||||
checked: Config.blur
|
||||
@@ -249,7 +249,7 @@ FormCard.FormCardPage {
|
||||
|
||||
FormCard.AbstractFormDelegate {
|
||||
id: transparencyDelegate
|
||||
visible: Controller.hasWindowSystem && Config.blur
|
||||
visible: WindowController.hasWindowSystem && Config.blur
|
||||
enabled: !Config.isTransparancyImmutable
|
||||
background: Item {}
|
||||
contentItem: ColumnLayout {
|
||||
|
||||
@@ -6,6 +6,7 @@ import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import QtMultimedia
|
||||
|
||||
import org.kde.coreaddons
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.neochat
|
||||
@@ -127,7 +128,7 @@ MessageDelegate {
|
||||
QQC2.Label {
|
||||
visible: root.contentMaxWidth > Kirigami.Units.gridUnit * 12
|
||||
|
||||
text: Controller.formatDuration(audio.position) + "/" + Controller.formatDuration(audio.duration)
|
||||
text: Format.formatDuration(audio.position) + "/" + Format.formatDuration(audio.duration)
|
||||
}
|
||||
}
|
||||
QQC2.Label {
|
||||
@@ -135,7 +136,7 @@ MessageDelegate {
|
||||
Layout.rightMargin: Kirigami.Units.smallSpacing
|
||||
visible: audio.hasAudio && root.contentMaxWidth < Kirigami.Units.gridUnit * 12
|
||||
|
||||
text: Controller.formatDuration(audio.position) + "/" + Controller.formatDuration(audio.duration)
|
||||
text: Format.formatDuration(audio.position) + "/" + Format.formatDuration(audio.duration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ KirigamiSettings.CategorizedSettings {
|
||||
actionName: "security"
|
||||
text: i18n("Security")
|
||||
icon.name: "security-low"
|
||||
page: Qt.resolvedUrl("Security.qml")
|
||||
page: Qt.resolvedUrl("RoomSecurity.qml")
|
||||
initialProperties: {
|
||||
return {
|
||||
room: root.room
|
||||
|
||||
@@ -50,6 +50,14 @@ QQC2.Control {
|
||||
|
||||
property NeoChatConnection connection
|
||||
|
||||
/**
|
||||
* @brief The ActionsHandler object to use.
|
||||
*
|
||||
* This is expected to have the correct room set otherwise messages will be sent
|
||||
* to the wrong room.
|
||||
*/
|
||||
required property ActionsHandler actionsHandler
|
||||
|
||||
/**
|
||||
* @brief The list of actions in the ChatBar.
|
||||
*
|
||||
@@ -478,7 +486,7 @@ QQC2.Control {
|
||||
}
|
||||
|
||||
function postMessage() {
|
||||
RoomManager.actionsHandler.handleMessageEvent(_private.chatBarCache);
|
||||
root.actionsHandler.handleMessageEvent(_private.chatBarCache);
|
||||
repeatTimer.stop()
|
||||
root.currentRoom.markAllMessagesAsRead();
|
||||
textField.clear();
|
||||
|
||||
@@ -38,6 +38,14 @@ ColumnLayout {
|
||||
|
||||
required property NeoChatConnection connection
|
||||
|
||||
/**
|
||||
* @brief The ActionsHandler object to use.
|
||||
*
|
||||
* This is expected to have the correct room set otherwise messages will be sent
|
||||
* to the wrong room.
|
||||
*/
|
||||
required property ActionsHandler actionsHandler
|
||||
|
||||
/**
|
||||
* @brief A message has been sent from the chat bar.
|
||||
*/
|
||||
@@ -75,6 +83,7 @@ ColumnLayout {
|
||||
Layout.preferredHeight: Math.round(implicitHeight)
|
||||
|
||||
currentRoom: root.currentRoom
|
||||
actionsHandler: root.actionsHandler
|
||||
|
||||
FontMetrics {
|
||||
id: chatBarFontMetrics
|
||||
|
||||
@@ -6,6 +6,7 @@ import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import Qt.labs.platform
|
||||
|
||||
import org.kde.coreaddons
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.neochat
|
||||
@@ -104,7 +105,7 @@ MessageDelegate {
|
||||
|
||||
PropertyChanges {
|
||||
target: sizeLabel
|
||||
text: i18nc("file download progress", "%1 / %2", Controller.formatByteSize(root.progressInfo.progress), Controller.formatByteSize(root.progressInfo.total))
|
||||
text: i18nc("file download progress", "%1 / %2", Format.formatByteSize(root.progressInfo.progress), Format.formatByteSize(root.progressInfo.total))
|
||||
}
|
||||
PropertyChanges {
|
||||
target: downloadButton
|
||||
@@ -140,7 +141,7 @@ MessageDelegate {
|
||||
QQC2.Label {
|
||||
id: sizeLabel
|
||||
Layout.fillWidth: true
|
||||
text: Controller.formatByteSize(root.mediaInfo.size)
|
||||
text: Format.formatByteSize(root.mediaInfo.size)
|
||||
opacity: 0.7
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
|
||||
@@ -33,13 +33,13 @@ ApplicationWindow {
|
||||
onClicked: root.destroy()
|
||||
}
|
||||
|
||||
Map {
|
||||
id: map
|
||||
MapView {
|
||||
id: mapView
|
||||
anchors.fill: parent
|
||||
center: root.liveLocationModel ? QtPositioning.coordinate(root.liveLocationModel.boundingBox.y, root.liveLocationModel.boundingBox.x)
|
||||
map.center: root.liveLocationModel ? QtPositioning.coordinate(root.liveLocationModel.boundingBox.y, root.liveLocationModel.boundingBox.x)
|
||||
: QtPositioning.coordinate(root.latitude, root.longitude)
|
||||
zoomLevel: 15
|
||||
plugin: OsmLocationPlugin.plugin
|
||||
map.zoomLevel: 15
|
||||
map.plugin: OsmLocationPlugin.plugin
|
||||
LocationMapItem {
|
||||
latitude: root.latitude
|
||||
longitude: root.longitude
|
||||
@@ -53,8 +53,12 @@ ApplicationWindow {
|
||||
model: root.liveLocationModel
|
||||
delegate: LocationMapItem {}
|
||||
}
|
||||
onCopyrightLinkActivated: {
|
||||
Qt.openUrlExternally(link)
|
||||
|
||||
Connections {
|
||||
target: mapView.map
|
||||
function onCopyrightLinkActivated() {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,16 +25,15 @@ MessageDelegate {
|
||||
id: liveLocationModel
|
||||
eventId: root.eventId
|
||||
}
|
||||
Map {
|
||||
id: map
|
||||
MapView {
|
||||
id: mapView
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: root.contentMaxWidth / 16 * 9
|
||||
|
||||
center: QtPositioning.coordinate(liveLocationModel.boundingBox.y, liveLocationModel.boundingBox.x)
|
||||
zoomLevel: 15
|
||||
map.center: QtPositioning.coordinate(liveLocationModel.boundingBox.y, liveLocationModel.boundingBox.x)
|
||||
map.zoomLevel: 15
|
||||
|
||||
plugin: OsmLocationPlugin.plugin
|
||||
onCopyrightLinkActivated: Qt.openUrlExternally(link)
|
||||
map.plugin: OsmLocationPlugin.plugin
|
||||
|
||||
MapItemView {
|
||||
model: liveLocationModel
|
||||
@@ -53,6 +52,12 @@ MessageDelegate {
|
||||
acceptedButtons: Qt.RightButton
|
||||
onTapped: openMessageContext("")
|
||||
}
|
||||
Connections {
|
||||
target: mapView.map
|
||||
function onCopyrightLinkActivated() {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
Component {
|
||||
id: fullScreenMap
|
||||
|
||||
@@ -33,9 +33,9 @@ Components.AbstractMaximizeComponent {
|
||||
}
|
||||
]
|
||||
|
||||
content: Map {
|
||||
content: MapView {
|
||||
id: map
|
||||
plugin: Plugin {
|
||||
map.plugin: Plugin {
|
||||
name: "osm"
|
||||
PluginParameter {
|
||||
name: "osm.useragent"
|
||||
@@ -81,5 +81,11 @@ Components.AbstractMaximizeComponent {
|
||||
}
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: mapView.map
|
||||
function onCopyrightLinkActivated() {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,16 +36,15 @@ MessageDelegate {
|
||||
required property string asset
|
||||
|
||||
bubbleContent: ColumnLayout {
|
||||
Map {
|
||||
id: map
|
||||
MapView {
|
||||
id: mapView
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: root.contentMaxWidth / 16 * 9
|
||||
|
||||
center: QtPositioning.coordinate(root.latitude, root.longitude)
|
||||
zoomLevel: 15
|
||||
map.center: QtPositioning.coordinate(root.latitude, root.longitude)
|
||||
map.zoomLevel: 15
|
||||
|
||||
plugin: OsmLocationPlugin.plugin
|
||||
onCopyrightLinkActivated: Qt.openUrlExternally(link)
|
||||
map.plugin: OsmLocationPlugin.plugin
|
||||
|
||||
LocationMapItem {
|
||||
latitude: root.latitude
|
||||
@@ -68,6 +67,12 @@ MessageDelegate {
|
||||
acceptedButtons: Qt.RightButton
|
||||
onTapped: openMessageContext("")
|
||||
}
|
||||
Connections {
|
||||
target: mapView.map
|
||||
function onCopyrightLinkActivated() {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
Component {
|
||||
id: fullScreenMap
|
||||
|
||||
@@ -17,16 +17,16 @@ Kirigami.Page {
|
||||
|
||||
padding: 0
|
||||
|
||||
Map {
|
||||
id: map
|
||||
MapView {
|
||||
id: mapView
|
||||
anchors.fill: parent
|
||||
plugin: OsmLocationPlugin.plugin
|
||||
map.plugin: OsmLocationPlugin.plugin
|
||||
|
||||
center: {
|
||||
map.center: {
|
||||
let c = LocationHelper.center(LocationHelper.unite(locationsModel.boundingBox, liveLocationsModel.boundingBox));
|
||||
return QtPositioning.coordinate(c.y, c.x);
|
||||
}
|
||||
zoomLevel: LocationHelper.zoomToFit(LocationHelper.unite(locationsModel.boundingBox, liveLocationsModel.boundingBox), map.width, map.height)
|
||||
map.zoomLevel: LocationHelper.zoomToFit(LocationHelper.unite(locationsModel.boundingBox, liveLocationsModel.boundingBox), mapView.width, mapView.height)
|
||||
|
||||
MapItemView {
|
||||
model: LocationsModel {
|
||||
@@ -49,8 +49,14 @@ Kirigami.Page {
|
||||
|
||||
Kirigami.PlaceholderMessage {
|
||||
text: i18n("There are no locations shared in this room.")
|
||||
visible: map.mapItems.length === 0
|
||||
visible: mapView.mapItems.length === 0
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
Connections {
|
||||
target: mapView.map
|
||||
function onCopyrightLinkActivated() {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,14 @@ QQC2.TextArea {
|
||||
_private.chatBarCache.relationIdChanged.connect(_private.updateEditText)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The ActionsHandler object to use.
|
||||
*
|
||||
* This is expected to have the correct room set otherwise messages will be sent
|
||||
* to the wrong room.
|
||||
*/
|
||||
required property ActionsHandler actionsHandler
|
||||
|
||||
property string messageId
|
||||
|
||||
property var minimumHeight: editButtons.height + topPadding + bottomPadding
|
||||
@@ -137,7 +145,7 @@ QQC2.TextArea {
|
||||
}
|
||||
|
||||
function postEdit() {
|
||||
RoomManager.actionsHandler.handleMessageEvent(_private.chatBarCache);
|
||||
root.actionsHandler.handleMessageEvent(_private.chatBarCache);
|
||||
root.clear();
|
||||
_private.chatBarCache.editId = "";
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.coreaddons
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||
|
||||
@@ -187,7 +188,7 @@ Item {
|
||||
MimeComponent {
|
||||
mimeIconSource: root.mediaInfo.mimeIcon
|
||||
label: root.display
|
||||
subLabel: root.type === DelegateType.File ? Controller.formatByteSize(root.mediaInfo.size) : Controller.formatDuration(root.mediaInfo.duration)
|
||||
subLabel: root.type === DelegateType.File ? Format.formatByteSize(root.mediaInfo.size) : Format.formatDuration(root.mediaInfo.duration)
|
||||
}
|
||||
}
|
||||
Component {
|
||||
|
||||
@@ -21,6 +21,52 @@ Kirigami.Page {
|
||||
property NeoChatRoom currentRoom: RoomManager.currentRoom
|
||||
|
||||
required property NeoChatConnection connection
|
||||
|
||||
/**
|
||||
* @brief The MessageEventModel to use.
|
||||
*
|
||||
* Required so that new events can be requested when the end of the current
|
||||
* local timeline is reached.
|
||||
*
|
||||
* @note For loading a room in a different window, override this with a new
|
||||
* MessageEventModel set with the room to be shown.
|
||||
*
|
||||
* @sa MessageEventModel
|
||||
*/
|
||||
property MessageEventModel messageEventModel: RoomManager.messageEventModel
|
||||
|
||||
/**
|
||||
* @brief The MessageFilterModel to use.
|
||||
*
|
||||
* This model has the filtered list of events that should be shown in the timeline.
|
||||
*
|
||||
* @note For loading a room in a different window, override this with a new
|
||||
* MessageFilterModel with the new MessageEventModel as the source model.
|
||||
*
|
||||
* @sa MessageEventModel, MessageFilterModel
|
||||
*/
|
||||
property MessageFilterModel messageFilterModel: RoomManager.messageFilterModel
|
||||
|
||||
/**
|
||||
* @brief The MediaMessageFilterModel to use.
|
||||
*
|
||||
* This model has the filtered list of media events that should be shown in
|
||||
* the timeline.
|
||||
*
|
||||
* @note For loading a room in a different window, override this with a new
|
||||
* MediaMessageFilterModel with the new MessageFilterModel as the source model.
|
||||
*
|
||||
* @sa MessageEventModel, MessageFilterModel
|
||||
*/
|
||||
property MediaMessageFilterModel mediaMessageFilterModel: RoomManager.mediaMessageFilterModel
|
||||
|
||||
/**
|
||||
* @brief The ActionsHandler object to use.
|
||||
*/
|
||||
property ActionsHandler actionsHandler: ActionsHandler {
|
||||
room: root.currentRoom
|
||||
}
|
||||
|
||||
property bool loading: !root.currentRoom || (root.currentRoom.timelineSize === 0 && !root.currentRoom.allHistoryLoaded)
|
||||
|
||||
/// Disable cancel shortcut. Used by the separate window since it provides its own cancel implementation.
|
||||
@@ -41,6 +87,7 @@ Kirigami.Page {
|
||||
KeyNavigation.left: pageStack.get(0)
|
||||
|
||||
onCurrentRoomChanged: {
|
||||
banner.visible = false;
|
||||
if (!Kirigami.Settings.isMobile && chatBoxLoader.item) {
|
||||
chatBoxLoader.item.forceActiveFocus();
|
||||
}
|
||||
@@ -73,6 +120,9 @@ Kirigami.Page {
|
||||
sourceComponent: TimelineView {
|
||||
id: timelineView
|
||||
currentRoom: root.currentRoom
|
||||
messageEventModel: root.messageEventModel
|
||||
messageFilterModel: root.messageFilterModel
|
||||
actionsHandler: root.actionsHandler
|
||||
onFocusChatBox: {
|
||||
if (chatBoxLoader.item) {
|
||||
chatBoxLoader.item.forceActiveFocus()
|
||||
@@ -114,6 +164,7 @@ Kirigami.Page {
|
||||
width: parent.width
|
||||
currentRoom: root.currentRoom
|
||||
connection: root.connection
|
||||
actionsHandler: root.actionsHandler
|
||||
onMessageSent: {
|
||||
if (!timelineViewLoader.item.atYEnd) {
|
||||
timelineViewLoader.item.goToLastMessage();
|
||||
@@ -229,6 +280,17 @@ Kirigami.Page {
|
||||
});
|
||||
contextMenu.open();
|
||||
}
|
||||
|
||||
function onShowMaximizedMedia(index) {
|
||||
var popup = maximizeComponent.createObject(QQC2.ApplicationWindow.overlay, {
|
||||
initialIndex: index
|
||||
})
|
||||
popup.closed.connect(() => {
|
||||
messageListView.interactive = true
|
||||
popup.destroy()
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
|
||||
function showUserDetail(user) {
|
||||
@@ -256,4 +318,12 @@ Kirigami.Page {
|
||||
connection: root.connection
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: maximizeComponent
|
||||
NeochatMaximizeComponent {
|
||||
currentRoom: root.currentRoom
|
||||
model: root.mediaMessageFilterModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,21 @@ Kirigami.ApplicationWindow {
|
||||
onActivated: root.close()
|
||||
}
|
||||
pageStack.initialPage: RoomPage {
|
||||
id: roomPage
|
||||
visible: true
|
||||
currentRoom: root.currentRoom
|
||||
disableCancelShortcut: true
|
||||
connection: root.connection
|
||||
|
||||
messageEventModel: MessageEventModel {
|
||||
room: currentRoom
|
||||
}
|
||||
messageFilterModel: MessageFilterModel {
|
||||
sourceModel: roomPage.messageEventModel
|
||||
}
|
||||
mediaMessageFilterModel: MediaMessageFilterModel {
|
||||
sourceModel: roomPage.messageFilterModel
|
||||
}
|
||||
}
|
||||
|
||||
onCurrentRoomChanged: if (!currentRoom) {
|
||||
|
||||
@@ -38,7 +38,6 @@ QQC2.ItemDelegate {
|
||||
Layout.maximumWidth: maxWidth
|
||||
}
|
||||
Kirigami.Separator {
|
||||
Layout.minimumHeight: 2
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: maxWidth
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2021 Carson Black <uhhadd@gmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
|
||||
// Not to be confused with the Shimmer project.
|
||||
// I like their gradiented GTK themes though.
|
||||
|
||||
import QtQuick
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
Gradient {
|
||||
id: root
|
||||
|
||||
orientation: Gradient.Horizontal
|
||||
|
||||
property color color: Kirigami.Theme.textColor
|
||||
property color translucent: Qt.rgba(color.r, color.g, color.b, 0.2)
|
||||
property color bright: Qt.rgba(color.r, color.g, color.b, 0.3)
|
||||
property real pos: 0.5
|
||||
property real offset: 0.6
|
||||
|
||||
property SequentialAnimation ani: SequentialAnimation {
|
||||
running: true
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation {
|
||||
from: -2.0
|
||||
to: 2.0
|
||||
duration: 700
|
||||
target: root
|
||||
properties: "pos"
|
||||
}
|
||||
PauseAnimation {
|
||||
duration: 300
|
||||
}
|
||||
}
|
||||
|
||||
GradientStop { position: root.pos-root.offset; color: root.translucent }
|
||||
GradientStop { position: root.pos; color: root.bright }
|
||||
GradientStop { position: root.pos+root.offset; color: root.translucent }
|
||||
}
|
||||
@@ -119,22 +119,28 @@ Kirigami.ScrollablePage {
|
||||
ListView {
|
||||
clip: true
|
||||
model: settings.dictionaryModel
|
||||
delegate: Kirigami.CheckableListItem {
|
||||
label: model.display
|
||||
action: Kirigami.Action {
|
||||
onTriggered: model.checked = checked
|
||||
}
|
||||
delegate: QQC2.CheckDelegate {
|
||||
onClicked: model.checked = checked
|
||||
Accessible.description: model.isDefault ? i18n("Default Language") : ''
|
||||
checked: model.checked
|
||||
trailing: Kirigami.Icon {
|
||||
source: "favorite"
|
||||
visible: model.isDefault
|
||||
HoverHandler {
|
||||
id: hover
|
||||
width: scroll.width
|
||||
contentItem: RowLayout {
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
id: label
|
||||
text: model.display
|
||||
}
|
||||
QQC2.ToolTip {
|
||||
visible: hover.hovered
|
||||
text: i18n("Default Language")
|
||||
Kirigami.Icon {
|
||||
source: "favorite"
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing * 2
|
||||
visible: model.isDefault
|
||||
HoverHandler {
|
||||
id: hover
|
||||
}
|
||||
QQC2.ToolTip {
|
||||
visible: hover.hovered
|
||||
text: i18n("Default Language")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ MessageDelegate {
|
||||
active: visible
|
||||
sourceComponent: MessageEditComponent {
|
||||
room: currentRoom
|
||||
actionsHandler: root.ListView.view.actionsHandler
|
||||
messageId: root.eventId
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,30 @@ QQC2.ScrollView {
|
||||
hasScrolledUpBefore = false;
|
||||
}
|
||||
property bool roomChanging: false
|
||||
|
||||
/**
|
||||
* @brief The MessageEventModel to use.
|
||||
*
|
||||
* Required so that new events can be requested when the end of the current
|
||||
* local timeline is reached.
|
||||
*/
|
||||
required property MessageEventModel messageEventModel
|
||||
|
||||
/**
|
||||
* @brief The MessageFilterModel to use.
|
||||
*
|
||||
* This model has the filtered list of events that should be shown in the timeline.
|
||||
*/
|
||||
required property MessageFilterModel messageFilterModel
|
||||
|
||||
/**
|
||||
* @brief The ActionsHandler object to use.
|
||||
*
|
||||
* This is expected to have the correct room set otherwise messages will be sent
|
||||
* to the wrong room.
|
||||
*/
|
||||
required property ActionsHandler actionsHandler
|
||||
|
||||
readonly property bool atYEnd: messageListView.atYEnd
|
||||
|
||||
/// Used to determine if scrolling to the bottom should mark the message as unread
|
||||
@@ -36,6 +60,8 @@ QQC2.ScrollView {
|
||||
id: messageListView
|
||||
// So that delegates can access the current room properly.
|
||||
readonly property NeoChatRoom currentRoom: root.currentRoom
|
||||
// So that delegates can access the actionsHandler properly.
|
||||
readonly property ActionsHandler actionsHandler: root.actionsHandler
|
||||
|
||||
readonly property int largestVisibleIndex: count > 0 ? indexAt(contentX + (width / 2), contentY + height - 1) : -1
|
||||
readonly property var sectionBannerItem: contentHeight >= height ? itemAtIndex(sectionBannerIndex()) : undefined
|
||||
@@ -49,23 +75,23 @@ QQC2.ScrollView {
|
||||
interactive: Kirigami.Settings.isMobile
|
||||
bottomMargin: Kirigami.Units.largeSpacing + Math.round(Kirigami.Theme.defaultFont.pointSize * 2)
|
||||
|
||||
model: RoomManager.messageFilterModel
|
||||
model: root.messageFilterModel
|
||||
|
||||
Timer {
|
||||
interval: 1000
|
||||
running: messageListView.atYBeginning
|
||||
triggeredOnStart: true
|
||||
onTriggered: {
|
||||
if (messageListView.atYBeginning && RoomManager.messageEventModel.canFetchMore(RoomManager.messageEventModel.index(0, 0))) {
|
||||
RoomManager.messageEventModel.fetchMore(RoomManager.messageEventModel.index(0, 0));
|
||||
if (messageListView.atYBeginning && root.messageEventModel.canFetchMore(root.messageEventModel.index(0, 0))) {
|
||||
root.messageEventModel.fetchMore(root.messageEventModel.index(0, 0));
|
||||
}
|
||||
}
|
||||
repeat: true
|
||||
}
|
||||
|
||||
// HACK: The view should do this automatically but doesn't.
|
||||
onAtYBeginningChanged: if (atYBeginning && RoomManager.messageEventModel.canFetchMore(RoomManager.messageEventModel.index(0, 0))) {
|
||||
RoomManager.messageEventModel.fetchMore(RoomManager.messageEventModel.index(0, 0));
|
||||
onAtYBeginningChanged: if (atYBeginning && root.messageEventModel.canFetchMore(root.messageEventModel.index(0, 0))) {
|
||||
root.messageEventModel.fetchMore(root.messageEventModel.index(0, 0));
|
||||
}
|
||||
|
||||
Timer {
|
||||
@@ -183,7 +209,6 @@ QQC2.ScrollView {
|
||||
id: dropAreaFile
|
||||
anchors.fill: parent
|
||||
onDropped: root.currentRoom.mainCache.attachmentPath = drop.urls[0]
|
||||
;
|
||||
enabled: !Controller.isFlatpak
|
||||
}
|
||||
|
||||
@@ -242,7 +267,7 @@ QQC2.ScrollView {
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: RoomManager.messageEventModel
|
||||
target: root.messageEventModel
|
||||
|
||||
function onRowsInserted() {
|
||||
markReadIfVisibleTimer.restart()
|
||||
@@ -283,7 +308,7 @@ QQC2.ScrollView {
|
||||
|
||||
Connections {
|
||||
//enabled: Config.showFancyEffects
|
||||
target: RoomManager.messageEventModel
|
||||
target: root.messageEventModel
|
||||
|
||||
function onFancyEffectsReasonFound(fancyEffect) {
|
||||
fancyEffectsContainer.processFancyEffectsReason(fancyEffect)
|
||||
@@ -292,7 +317,7 @@ QQC2.ScrollView {
|
||||
|
||||
Connections {
|
||||
enabled: Config.showFancyEffects
|
||||
target: RoomManager.actionsHandler
|
||||
target: actionsHandler
|
||||
|
||||
function onShowEffect(fancyEffect) {
|
||||
fancyEffectsContainer.processFancyEffectsReason(fancyEffect)
|
||||
@@ -301,32 +326,6 @@ QQC2.ScrollView {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: maximizeComponent
|
||||
NeochatMaximizeComponent {
|
||||
currentRoom: root.currentRoom
|
||||
model: RoomManager.mediaMessageFilterModel
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: RoomManager
|
||||
function onShowMaximizedMedia(index) {
|
||||
messageListView.showMaximizedMedia(index)
|
||||
}
|
||||
}
|
||||
|
||||
function showMaximizedMedia(index) {
|
||||
var popup = maximizeComponent.createObject(QQC2.ApplicationWindow.overlay, {
|
||||
initialIndex: index
|
||||
})
|
||||
popup.closed.connect(() => {
|
||||
messageListView.interactive = true
|
||||
popup.destroy()
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
|
||||
function goToLastMessage() {
|
||||
root.currentRoom.markAllMessagesAsRead()
|
||||
// scroll to the very end, i.e to messageListView.YEnd
|
||||
@@ -334,10 +333,10 @@ QQC2.ScrollView {
|
||||
}
|
||||
|
||||
function eventToIndex(eventID) {
|
||||
const index = RoomManager.messageEventModel.eventIdToRow(eventID)
|
||||
const index = root.messageEventModel.eventIdToRow(eventID)
|
||||
if (index === -1)
|
||||
return -1
|
||||
return RoomManager.messageFilterModel.mapFromSource(RoomManager.messageEventModel.index(index, 0)).row
|
||||
return root.messageFilterModel.mapFromSource(root.messageEventModel.index(index, 0)).row
|
||||
}
|
||||
|
||||
function firstVisibleIndex() {
|
||||
|
||||
@@ -14,8 +14,6 @@ import org.kde.neochat
|
||||
Kirigami.Dialog {
|
||||
id: root
|
||||
|
||||
signal closed()
|
||||
|
||||
property NeoChatRoom room
|
||||
property var user
|
||||
|
||||
@@ -192,7 +190,7 @@ Kirigami.Dialog {
|
||||
text: i18n("Open a private chat")
|
||||
icon.name: "document-send"
|
||||
onTriggered: {
|
||||
Controller.openOrCreateDirectChat(root.user.object);
|
||||
root.room.connection.openOrCreateDirectChat(root.user.object)
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ QQC2.ToolBar {
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.connection = userDelegate.connection
|
||||
Controller.activeConnection = userDelegate.connection
|
||||
if (switchUserButton.checked) {
|
||||
switchUserButton.checked = false
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import QtQuick.Layouts
|
||||
import QtMultimedia
|
||||
import Qt.labs.platform as Platform
|
||||
|
||||
import org.kde.coreaddons
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.neochat
|
||||
@@ -191,7 +192,7 @@ MessageDelegate {
|
||||
onMoved: vid.seek(value)
|
||||
}
|
||||
QQC2.Label {
|
||||
text: Controller.formatDuration(vid.position) + "/" + Controller.formatDuration(vid.duration)
|
||||
text: Format.formatDuration(vid.position) + "/" + Format.formatDuration(vid.duration)
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
id: volumeButton
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
@@ -191,7 +190,7 @@ Kirigami.ApplicationWindow {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
Controller.setBlur(pageStack, Config.blur && !Config.compactLayout);
|
||||
WindowController.setBlur(pageStack, Config.blur && !Config.compactLayout);
|
||||
if (Config.minimizeToSystemTrayOnStartup && !Kirigami.Settings.isMobile && Controller.supportSystemTray && Config.systemTray) {
|
||||
restoreWindowGeometryConnections.enabled = true; // To restore window size and position
|
||||
} else {
|
||||
@@ -202,10 +201,10 @@ Kirigami.ApplicationWindow {
|
||||
Connections {
|
||||
target: Config
|
||||
function onBlurChanged() {
|
||||
Controller.setBlur(pageStack, Config.blur && !Config.compactLayout);
|
||||
WindowController.setBlur(pageStack, Config.blur && !Config.compactLayout);
|
||||
}
|
||||
function onCompactLayoutChanged() {
|
||||
Controller.setBlur(pageStack, Config.blur && !Config.compactLayout);
|
||||
WindowController.setBlur(pageStack, Config.blur && !Config.compactLayout);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include "roommanager.h"
|
||||
|
||||
#include "actionshandler.h"
|
||||
#include "chatbarcache.h"
|
||||
#include "controller.h"
|
||||
#include "enums/delegatetype.h"
|
||||
@@ -30,7 +29,6 @@ RoomManager::RoomManager(QObject *parent)
|
||||
, m_currentRoom(nullptr)
|
||||
, m_lastCurrentRoom(nullptr)
|
||||
, m_config(KSharedConfig::openStateConfig())
|
||||
, m_actionsHandler(new ActionsHandler(this))
|
||||
, m_messageEventModel(new MessageEventModel(this))
|
||||
, m_messageFilterModel(new MessageFilterModel(this, m_messageEventModel))
|
||||
, m_mediaMessageFilterModel(new MediaMessageFilterModel(this, m_messageFilterModel))
|
||||
@@ -38,7 +36,6 @@ RoomManager::RoomManager(QObject *parent)
|
||||
m_lastRoomConfig = m_config->group(QStringLiteral("LastOpenRoom"));
|
||||
|
||||
connect(this, &RoomManager::currentRoomChanged, this, [this]() {
|
||||
m_actionsHandler->setRoom(m_currentRoom);
|
||||
m_messageEventModel->setRoom(m_currentRoom);
|
||||
});
|
||||
}
|
||||
@@ -58,11 +55,6 @@ NeoChatRoom *RoomManager::currentRoom() const
|
||||
return m_currentRoom;
|
||||
}
|
||||
|
||||
ActionsHandler *RoomManager::actionsHandler() const
|
||||
{
|
||||
return m_actionsHandler;
|
||||
}
|
||||
|
||||
MessageEventModel *RoomManager::messageEventModel() const
|
||||
{
|
||||
return m_messageEventModel;
|
||||
@@ -208,6 +200,7 @@ void RoomManager::enterRoom(NeoChatRoom *room)
|
||||
m_currentRoom->mainCache()->setSavedText(m_chatDocumentHandler->document()->textDocument()->toPlainText());
|
||||
m_chatDocumentHandler->setRoom(room);
|
||||
m_chatDocumentHandler->document()->textDocument()->setPlainText(room->mainCache()->savedText());
|
||||
room->mainCache()->setText(room->mainCache()->savedText());
|
||||
} else {
|
||||
m_chatDocumentHandler->setRoom(room);
|
||||
}
|
||||
@@ -303,7 +296,7 @@ void RoomManager::visitRoom(Room *room, const QString &eventId)
|
||||
|
||||
void RoomManager::joinRoom(Quotient::Connection *account, const QString &roomAliasOrId, const QStringList &viaServers)
|
||||
{
|
||||
auto job = account->joinRoom(QString::fromLatin1(QUrl::toPercentEncoding(roomAliasOrId)), viaServers);
|
||||
auto job = account->joinRoom(roomAliasOrId, viaServers);
|
||||
connectSingleShot(job, &Quotient::BaseJob::finished, this, [this, account](Quotient::BaseJob *finish) {
|
||||
if (finish->status() == Quotient::BaseJob::Success) {
|
||||
connectSingleShot(account, &Quotient::Connection::newRoom, this, [this](Quotient::Room *room) {
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <Quotient/room.h>
|
||||
#include <Quotient/uriresolver.h>
|
||||
|
||||
#include "actionshandler.h"
|
||||
#include "chatdocumenthandler.h"
|
||||
#include "enums/delegatetype.h"
|
||||
#include "models/mediamessagefiltermodel.h"
|
||||
@@ -45,17 +44,6 @@ class RoomManager : public QObject, public UriResolverBase
|
||||
*/
|
||||
Q_PROPERTY(NeoChatRoom *currentRoom READ currentRoom NOTIFY currentRoomChanged)
|
||||
|
||||
/**
|
||||
* @brief The ActionsHandler that should be used when sending messages.
|
||||
*
|
||||
* The room object the object uses will be updated by this class so there is no
|
||||
* need to do this manually or replace the object when a room changes.
|
||||
*
|
||||
* @note Available here so that the room page and drawer both have access to the
|
||||
* same model.
|
||||
*/
|
||||
Q_PROPERTY(ActionsHandler *actionsHandler READ actionsHandler CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief The MessageEventModel that should be used for room message visualisation.
|
||||
*
|
||||
@@ -110,8 +98,6 @@ public:
|
||||
|
||||
NeoChatRoom *currentRoom() const;
|
||||
|
||||
ActionsHandler *actionsHandler() const;
|
||||
|
||||
MessageEventModel *messageEventModel() const;
|
||||
MessageFilterModel *messageFilterModel() const;
|
||||
MediaMessageFilterModel *mediaMessageFilterModel() const;
|
||||
@@ -389,8 +375,6 @@ private:
|
||||
KConfigGroup m_lastRoomConfig;
|
||||
QPointer<ChatDocumentHandler> m_chatDocumentHandler;
|
||||
|
||||
ActionsHandler *m_actionsHandler;
|
||||
|
||||
MessageEventModel *m_messageEventModel;
|
||||
MessageFilterModel *m_messageFilterModel;
|
||||
MediaMessageFilterModel *m_mediaMessageFilterModel;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#include <cmark.h>
|
||||
|
||||
#include <Kirigami/PlatformTheme>
|
||||
#include <Kirigami/Platform/PlatformTheme>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
@@ -153,7 +153,8 @@ QString TextHandler::handleRecieveRichText(Qt::TextFormat inputFormat, const Neo
|
||||
bool isEdited = !e->unsignedJson().isEmpty() && e->unsignedJson().contains(QStringLiteral("m.relations"))
|
||||
&& e->unsignedJson()[QStringLiteral("m.relations")].toObject().contains(QStringLiteral("m.replace"));
|
||||
if (isEdited) {
|
||||
Kirigami::PlatformTheme *theme = static_cast<Kirigami::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::PlatformTheme>(this, true));
|
||||
Kirigami::Platform::PlatformTheme *theme =
|
||||
static_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true));
|
||||
|
||||
QString editTextColor;
|
||||
if (theme != nullptr) {
|
||||
@@ -428,9 +429,54 @@ QString TextHandler::unescapeHtml(QString stringIn)
|
||||
|
||||
QString TextHandler::linkifyUrls(QString stringIn)
|
||||
{
|
||||
stringIn = stringIn.replace(TextRegex::mxId, QStringLiteral(R"(\1<a href="https://matrix.to/#/\2">\2</a>)"));
|
||||
stringIn.replace(TextRegex::plainUrl, QStringLiteral(R"(<a href="\1">\1</a>)"));
|
||||
stringIn = stringIn.replace(TextRegex::emailAddress, QStringLiteral(R"(<a href="mailto:\2">\1\2</a>)"));
|
||||
QRegularExpressionMatch match;
|
||||
int start = 0;
|
||||
for (int index = 0; index != -1; index = stringIn.indexOf(TextRegex::mxId, start, &match)) {
|
||||
int skip = 0;
|
||||
if (match.captured(0).size() > 0) {
|
||||
if (stringIn.left(index).count(QStringLiteral("<code>")) == stringIn.left(index).count(QStringLiteral("</code>"))) {
|
||||
auto replacement = QStringLiteral("<a href=\"https://matrix.to/#/%1\">%1</a>").arg(match.captured(2));
|
||||
stringIn = stringIn.replace(index, match.captured(0).size(), replacement);
|
||||
} else {
|
||||
skip = match.captured().length();
|
||||
}
|
||||
}
|
||||
start = index + skip;
|
||||
match = {};
|
||||
}
|
||||
start = 0;
|
||||
match = {};
|
||||
for (int index = 0; index != -1; index = stringIn.indexOf(TextRegex::plainUrl, start, &match)) {
|
||||
int skip = 0;
|
||||
if (match.captured(0).size() > 0) {
|
||||
if (stringIn.left(index).count(QStringLiteral("<code>")) == stringIn.left(index).count(QStringLiteral("</code>"))) {
|
||||
auto replacement = QStringLiteral("<a href=\"%1\">%1</a>").arg(match.captured(1));
|
||||
stringIn = stringIn.replace(index, match.captured(0).size(), replacement);
|
||||
skip = replacement.length();
|
||||
} else {
|
||||
skip = match.captured().length();
|
||||
}
|
||||
}
|
||||
start = index + skip;
|
||||
match = {};
|
||||
}
|
||||
start = 0;
|
||||
match = {};
|
||||
for (int index = 0; index != -1; index = stringIn.indexOf(TextRegex::emailAddress, start, &match)) {
|
||||
int skip = 0;
|
||||
if (match.captured(0).size() > 0) {
|
||||
if (stringIn.left(index).count(QStringLiteral("<code>")) == stringIn.left(index).count(QStringLiteral("</code>"))) {
|
||||
auto replacement = QStringLiteral("<a href=\"mailto:%1\">%1</a>").arg(match.captured(2));
|
||||
stringIn = stringIn.replace(index, match.captured(0).size(), replacement);
|
||||
skip = replacement.length();
|
||||
} else {
|
||||
skip = match.captured().length();
|
||||
}
|
||||
}
|
||||
start = index + skip;
|
||||
match = {};
|
||||
}
|
||||
|
||||
return stringIn;
|
||||
}
|
||||
|
||||
|
||||
@@ -76,4 +76,30 @@ void WindowController::showAndRaiseWindow(const QString &startupId)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool WindowController::hasWindowSystem() const
|
||||
{
|
||||
#ifdef HAVE_WINDOWSYSTEM
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void WindowController::setBlur(QQuickItem *item, bool blur)
|
||||
{
|
||||
#ifdef HAVE_WINDOWSYSTEM
|
||||
auto setWindows = [item, blur]() {
|
||||
auto reg = QRect(QPoint(0, 0), item->window()->size());
|
||||
KWindowEffects::enableBackgroundContrast(item->window(), blur, 1, 1, 1, reg);
|
||||
KWindowEffects::enableBlurBehind(item->window(), blur, reg);
|
||||
};
|
||||
|
||||
disconnect(item->window(), &QQuickWindow::heightChanged, this, nullptr);
|
||||
disconnect(item->window(), &QQuickWindow::widthChanged, this, nullptr);
|
||||
connect(item->window(), &QQuickWindow::heightChanged, this, setWindows);
|
||||
connect(item->window(), &QQuickWindow::widthChanged, this, setWindows);
|
||||
setWindows();
|
||||
#endif
|
||||
}
|
||||
|
||||
#include "moc_windowcontroller.cpp"
|
||||
|
||||
@@ -4,8 +4,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QWindow>
|
||||
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickItem>
|
||||
#include <QQuickWindow>
|
||||
#ifdef HAVE_WINDOWSYSTEM
|
||||
#include <KWindowEffects>
|
||||
#endif
|
||||
/**
|
||||
* @class WindowController
|
||||
*
|
||||
@@ -14,9 +18,21 @@
|
||||
class WindowController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
/**
|
||||
* @brief Whether KWindowSystem specific features are available.
|
||||
*/
|
||||
Q_PROPERTY(bool hasWindowSystem READ hasWindowSystem CONSTANT)
|
||||
|
||||
public:
|
||||
static WindowController &instance();
|
||||
static WindowController *create(QQmlEngine *engine, QJSEngine *)
|
||||
{
|
||||
engine->setObjectOwnership(&instance(), QQmlEngine::CppOwnership);
|
||||
return &instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the window that the will be managed.
|
||||
@@ -43,6 +59,13 @@ public:
|
||||
*/
|
||||
void showAndRaiseWindow(const QString &startupId);
|
||||
|
||||
bool hasWindowSystem() const;
|
||||
|
||||
/**
|
||||
* @brief Set the background blur status of the given item.
|
||||
*/
|
||||
Q_INVOKABLE void setBlur(QQuickItem *item, bool blur);
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* @brief Triggered if the managed window is changed.
|
||||
|
||||
Reference in New Issue
Block a user