Compare commits
64 Commits
work/tobia
...
v24.01.75
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
819d88e18c | ||
|
|
dbbad2cf13 | ||
|
|
08b84c6592 | ||
|
|
b8abf0540d | ||
|
|
7f9e709559 | ||
|
|
c4f6abee9d | ||
|
|
1a4947b98a | ||
|
|
6ba2b715c3 | ||
|
|
e9e1e223f7 | ||
|
|
006da1fb16 | ||
|
|
3aff1795c8 | ||
|
|
576b1f928f | ||
|
|
fc3ab50701 | ||
|
|
e7fa3ad524 | ||
|
|
5adffddbd8 | ||
|
|
1d95d5aa15 | ||
|
|
dabd6291a5 | ||
|
|
0e55c3b38f | ||
|
|
ff4cf86ea5 | ||
|
|
f4d5ccbf12 | ||
|
|
192601d358 | ||
|
|
d7c432119e | ||
|
|
eddb2b73c2 | ||
|
|
0c60bfdb83 | ||
|
|
2bfb2fa1f9 | ||
|
|
d1aac971bf | ||
|
|
284cadf305 | ||
|
|
4697b7fcf1 | ||
|
|
9f356912c9 | ||
|
|
1cf891f845 | ||
|
|
52e2d636b9 | ||
|
|
dc6b539ddf | ||
|
|
e3cf85aa8c | ||
|
|
2065eb6684 | ||
|
|
9cac2a8abd | ||
|
|
feb87e6f70 | ||
|
|
a0057b8a49 | ||
|
|
dbb0269354 | ||
|
|
7fdb617b33 | ||
|
|
d798b0dec9 | ||
|
|
95cf23eb5b | ||
|
|
7e3db20229 | ||
|
|
12689babfb | ||
|
|
211407da44 | ||
|
|
400a84e48d | ||
|
|
7a45640e5e | ||
|
|
8af20885ab | ||
|
|
4033f07272 | ||
|
|
5df4fa297d | ||
|
|
33c5b418d2 | ||
|
|
69d378a17b | ||
|
|
f1b1b8ce53 | ||
|
|
526d4748e0 | ||
|
|
c5f93adbf4 | ||
|
|
61630cbe90 | ||
|
|
8c435e9d6d | ||
|
|
57978b1a6e | ||
|
|
f3c4d9449a | ||
|
|
9b37777f20 | ||
|
|
d6d6c161db | ||
|
|
772bca5ba6 | ||
|
|
036a60a095 | ||
|
|
b3315e1ed4 | ||
|
|
d300e9cf52 |
@@ -16,6 +16,7 @@ Dependencies:
|
|||||||
'frameworks/kcolorscheme': '@latest-kf6'
|
'frameworks/kcolorscheme': '@latest-kf6'
|
||||||
'libraries/kquickimageeditor': '@latest-kf6'
|
'libraries/kquickimageeditor': '@latest-kf6'
|
||||||
'frameworks/sonnet': '@latest-kf6'
|
'frameworks/sonnet': '@latest-kf6'
|
||||||
|
'frameworks/prison': '@latest-kf6'
|
||||||
'libraries/kirigami-addons': '@latest-kf6'
|
'libraries/kirigami-addons': '@latest-kf6'
|
||||||
'third-party/libquotient': '@latest'
|
'third-party/libquotient': '@latest'
|
||||||
'third-party/qtkeychain': '@latest'
|
'third-party/qtkeychain': '@latest'
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
# KDE Applications version, managed by release script.
|
# KDE Applications version, managed by release script.
|
||||||
set(RELEASE_SERVICE_VERSION_MAJOR "23")
|
set(RELEASE_SERVICE_VERSION_MAJOR "24")
|
||||||
set(RELEASE_SERVICE_VERSION_MINOR "11")
|
set(RELEASE_SERVICE_VERSION_MINOR "01")
|
||||||
set(RELEASE_SERVICE_VERSION_MICRO "70")
|
set(RELEASE_SERVICE_VERSION_MICRO "75")
|
||||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||||
|
|
||||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||||
@@ -37,6 +37,7 @@ include(ECMAddAppIcon)
|
|||||||
include(KDEGitCommitHooks)
|
include(KDEGitCommitHooks)
|
||||||
include(ECMCheckOutboundLicense)
|
include(ECMCheckOutboundLicense)
|
||||||
include(ECMQtDeclareLoggingCategory)
|
include(ECMQtDeclareLoggingCategory)
|
||||||
|
include(ECMAddAndroidApk)
|
||||||
if (NOT ANDROID)
|
if (NOT ANDROID)
|
||||||
include(KDEClangFormat)
|
include(KDEClangFormat)
|
||||||
endif()
|
endif()
|
||||||
@@ -119,6 +120,7 @@ ecm_find_qmlmodule(org.kde.kquickimageeditor 1.0)
|
|||||||
ecm_find_qmlmodule(org.kde.kitemmodels 1.0)
|
ecm_find_qmlmodule(org.kde.kitemmodels 1.0)
|
||||||
ecm_find_qmlmodule(org.kde.quickcharts 1.0)
|
ecm_find_qmlmodule(org.kde.quickcharts 1.0)
|
||||||
ecm_find_qmlmodule(QtLocation)
|
ecm_find_qmlmodule(QtLocation)
|
||||||
|
ecm_find_qmlmodule(org.kde.prison)
|
||||||
|
|
||||||
find_package(KQuickImageEditor COMPONENTS)
|
find_package(KQuickImageEditor COMPONENTS)
|
||||||
set_package_properties(KQuickImageEditor PROPERTIES
|
set_package_properties(KQuickImageEditor PROPERTIES
|
||||||
|
|||||||
@@ -9,9 +9,9 @@
|
|||||||
android:versionName="${versionName}"
|
android:versionName="${versionName}"
|
||||||
android:versionCode="${versionCode}"
|
android:versionCode="${versionCode}"
|
||||||
android:installLocation="auto">
|
android:installLocation="auto">
|
||||||
<application android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="NeoChat" android:icon="@drawable/neochat" android:usesCleartextTraffic="true">
|
<application android:name="org.qtproject.qt.android.bindings.QtApplication" android:label="NeoChat" android:icon="@drawable/neochat" android:usesCleartextTraffic="true">
|
||||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation"
|
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation"
|
||||||
android:name="org.qtproject.qt5.android.bindings.QtActivity"
|
android:name="org.qtproject.qt.android.bindings.QtActivity"
|
||||||
android:label="NeoChat"
|
android:label="NeoChat"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.0.2'
|
classpath 'com.android.tools.build:gradle:7.4.1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ android {
|
|||||||
* The following variables:
|
* The following variables:
|
||||||
* - androidBuildToolsVersion,
|
* - androidBuildToolsVersion,
|
||||||
* - androidCompileSdkVersion
|
* - androidCompileSdkVersion
|
||||||
* - qt5AndroidDir - holds the path to qt android files
|
* - qtAndroidDir - holds the path to qt android files
|
||||||
* needed to build any Qt application
|
* needed to build any Qt application
|
||||||
* on Android.
|
* on Android.
|
||||||
*
|
*
|
||||||
@@ -44,17 +44,20 @@ android {
|
|||||||
* Changing them manually might break the compilation!
|
* Changing them manually might break the compilation!
|
||||||
*******************************************************/
|
*******************************************************/
|
||||||
|
|
||||||
compileSdkVersion androidCompileSdkVersion.toInteger()
|
compileSdkVersion androidCompileSdkVersion
|
||||||
|
|
||||||
buildToolsVersion androidBuildToolsVersion
|
buildToolsVersion androidBuildToolsVersion
|
||||||
ndkVersion androidNdkVersion
|
ndkVersion androidNdkVersion
|
||||||
|
|
||||||
|
// Extract native libraries from the APK
|
||||||
|
packagingOptions.jniLibs.useLegacyPackaging true
|
||||||
|
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main {
|
main {
|
||||||
manifest.srcFile 'AndroidManifest.xml'
|
manifest.srcFile 'AndroidManifest.xml'
|
||||||
java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
|
java.srcDirs = [qtAndroidDir + '/src', 'src', 'java']
|
||||||
aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
|
aidl.srcDirs = [qtAndroidDir + '/src', 'src', 'aidl']
|
||||||
res.srcDirs = [qt5AndroidDir + '/res', 'res']
|
res.srcDirs = [qtAndroidDir + '/res', 'res']
|
||||||
resources.srcDirs = ['src']
|
resources.srcDirs = ['src']
|
||||||
renderscript.srcDirs = ['src']
|
renderscript.srcDirs = ['src']
|
||||||
assets.srcDirs = ['assets']
|
assets.srcDirs = ['assets']
|
||||||
|
|||||||
@@ -40,3 +40,9 @@ ecm_add_test(
|
|||||||
LINK_LIBRARIES neochat Qt::Test
|
LINK_LIBRARIES neochat Qt::Test
|
||||||
TEST_NAME chatbarcachetest
|
TEST_NAME chatbarcachetest
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ecm_add_test(
|
||||||
|
chatdocumenthandlertest.cpp
|
||||||
|
LINK_LIBRARIES neochat Qt::Test
|
||||||
|
TEST_NAME chatdocumenthandlertest
|
||||||
|
)
|
||||||
|
|||||||
36
autotests/chatdocumenthandlertest.cpp
Normal file
36
autotests/chatdocumenthandlertest.cpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTest>
|
||||||
|
|
||||||
|
#include "chatdocumenthandler.h"
|
||||||
|
#include "neochatconfig.h"
|
||||||
|
|
||||||
|
class ChatDocumentHandlerTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
ChatDocumentHandler emptyHandler;
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void initTestCase();
|
||||||
|
|
||||||
|
void nullComplete();
|
||||||
|
};
|
||||||
|
|
||||||
|
void ChatDocumentHandlerTest::initTestCase()
|
||||||
|
{
|
||||||
|
// HACK: this is to stop KStatusNotifierItem SEGFAULTING on cleanup.
|
||||||
|
NeoChatConfig::self()->setSystemTray(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatDocumentHandlerTest::nullComplete()
|
||||||
|
{
|
||||||
|
QTest::ignoreMessage(QtWarningMsg, "complete called with m_document set to nullptr.");
|
||||||
|
emptyHandler.complete(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(ChatDocumentHandlerTest)
|
||||||
|
#include "chatdocumenthandlertest.moc"
|
||||||
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;
|
Connection *connection = nullptr;
|
||||||
TestRoom *room = nullptr;
|
TestRoom *room = nullptr;
|
||||||
EventHandler eventHandler;
|
EventHandler eventHandler;
|
||||||
|
EventHandler emptyHandler;
|
||||||
|
EventHandler noEventHandler;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
|
|
||||||
|
void nullSetEvent();
|
||||||
void eventId();
|
void eventId();
|
||||||
|
void nullEventId();
|
||||||
void delegateType_data();
|
void delegateType_data();
|
||||||
void delegateType();
|
void delegateType();
|
||||||
|
void nullDelegateType();
|
||||||
void author();
|
void author();
|
||||||
|
void nullAuthor();
|
||||||
void authorDisplayName();
|
void authorDisplayName();
|
||||||
|
void nullAuthorDisplayName();
|
||||||
void time();
|
void time();
|
||||||
|
void nullTime();
|
||||||
void timeString();
|
void timeString();
|
||||||
|
void nullTimeString();
|
||||||
void highlighted();
|
void highlighted();
|
||||||
|
void nullHighlighted();
|
||||||
void hidden();
|
void hidden();
|
||||||
|
void nullHidden();
|
||||||
void body();
|
void body();
|
||||||
|
void nullBody();
|
||||||
void genericBody_data();
|
void genericBody_data();
|
||||||
void genericBody();
|
void genericBody();
|
||||||
|
void nullGenericBody();
|
||||||
void mediaInfo();
|
void mediaInfo();
|
||||||
|
void nullMediaInfo();
|
||||||
void linkPreviewer();
|
void linkPreviewer();
|
||||||
|
void nullLinkPreviewer();
|
||||||
void reactions();
|
void reactions();
|
||||||
|
void nullReactions();
|
||||||
void hasReply();
|
void hasReply();
|
||||||
|
void nullHasReply();
|
||||||
void replyId();
|
void replyId();
|
||||||
|
void nullReplyId();
|
||||||
void replyDelegateType();
|
void replyDelegateType();
|
||||||
|
void nullReplyDelegateType();
|
||||||
void replyAuthor();
|
void replyAuthor();
|
||||||
|
void nullReplyAuthor();
|
||||||
void replyBody();
|
void replyBody();
|
||||||
|
void nullReplyBody();
|
||||||
void replyMediaInfo();
|
void replyMediaInfo();
|
||||||
|
void nullReplyMediaInfo();
|
||||||
void thread();
|
void thread();
|
||||||
|
void nullThread();
|
||||||
void location();
|
void location();
|
||||||
|
void nullLocation();
|
||||||
void readMarkers();
|
void readMarkers();
|
||||||
|
void nullReadMarkers();
|
||||||
|
|
||||||
|
void cleanup();
|
||||||
};
|
};
|
||||||
|
|
||||||
void EventHandlerTest::initTestCase()
|
void EventHandlerTest::initTestCase()
|
||||||
@@ -82,6 +109,13 @@ void EventHandlerTest::initTestCase()
|
|||||||
room->update(std::move(roomData));
|
room->update(std::move(roomData));
|
||||||
|
|
||||||
eventHandler.setRoom(room);
|
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()
|
void EventHandlerTest::eventId()
|
||||||
@@ -91,6 +125,12 @@ void EventHandlerTest::eventId()
|
|||||||
QCOMPARE(eventHandler.getId(), QStringLiteral("$153456789:example.org"));
|
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()
|
void EventHandlerTest::delegateType_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<int>("eventNum");
|
QTest::addColumn<int>("eventNum");
|
||||||
@@ -114,6 +154,12 @@ void EventHandlerTest::delegateType()
|
|||||||
QCOMPARE(eventHandler.getDelegateType(), 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()
|
void EventHandlerTest::author()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(0).get();
|
auto event = room->messageEvents().at(0).get();
|
||||||
@@ -131,6 +177,15 @@ void EventHandlerTest::author()
|
|||||||
QCOMPARE(eventHandlerAuthor["object"_ls], QVariant::fromValue(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()
|
void EventHandlerTest::authorDisplayName()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(1).get();
|
auto event = room->messageEvents().at(1).get();
|
||||||
@@ -139,6 +194,15 @@ void EventHandlerTest::authorDisplayName()
|
|||||||
QCOMPARE(eventHandler.getAuthorDisplayName(), QStringLiteral("before"));
|
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()
|
void EventHandlerTest::time()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(0).get();
|
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));
|
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()
|
void EventHandlerTest::timeString()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(0).get();
|
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));
|
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()
|
void EventHandlerTest::highlighted()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(2).get();
|
auto event = room->messageEvents().at(2).get();
|
||||||
@@ -182,6 +266,15 @@ void EventHandlerTest::highlighted()
|
|||||||
QCOMPARE(eventHandler.isHighlighted(), false);
|
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()
|
void EventHandlerTest::hidden()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(3).get();
|
auto event = room->messageEvents().at(3).get();
|
||||||
@@ -195,6 +288,15 @@ void EventHandlerTest::hidden()
|
|||||||
QCOMPARE(eventHandler.isHidden(), false);
|
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()
|
void EventHandlerTest::body()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(0).get();
|
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"));
|
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()
|
void EventHandlerTest::genericBody_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<int>("eventNum");
|
QTest::addColumn<int>("eventNum");
|
||||||
@@ -228,6 +339,12 @@ void EventHandlerTest::genericBody()
|
|||||||
QCOMPARE(eventHandler.getGenericBody(), output);
|
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()
|
void EventHandlerTest::mediaInfo()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(4).get();
|
auto event = room->messageEvents().at(4).get();
|
||||||
@@ -251,6 +368,15 @@ void EventHandlerTest::mediaInfo()
|
|||||||
QCOMPARE(thumbnailInfo["height"_ls], 450);
|
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()
|
void EventHandlerTest::linkPreviewer()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(2).get();
|
auto event = room->messageEvents().at(2).get();
|
||||||
@@ -264,6 +390,15 @@ void EventHandlerTest::linkPreviewer()
|
|||||||
QCOMPARE(eventHandler.getLinkPreviewer(), nullptr);
|
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()
|
void EventHandlerTest::reactions()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(0).get();
|
auto event = room->messageEvents().at(0).get();
|
||||||
@@ -272,6 +407,15 @@ void EventHandlerTest::reactions()
|
|||||||
QCOMPARE(eventHandler.getReactions()->rowCount(), 1);
|
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()
|
void EventHandlerTest::hasReply()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(5).get();
|
auto event = room->messageEvents().at(5).get();
|
||||||
@@ -285,6 +429,12 @@ void EventHandlerTest::hasReply()
|
|||||||
QCOMPARE(eventHandler.hasReply(), false);
|
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()
|
void EventHandlerTest::replyId()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(5).get();
|
auto event = room->messageEvents().at(5).get();
|
||||||
@@ -298,6 +448,12 @@ void EventHandlerTest::replyId()
|
|||||||
QCOMPARE(eventHandler.getReplyId(), QStringLiteral(""));
|
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()
|
void EventHandlerTest::replyDelegateType()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(5).get();
|
auto event = room->messageEvents().at(5).get();
|
||||||
@@ -311,6 +467,15 @@ void EventHandlerTest::replyDelegateType()
|
|||||||
QCOMPARE(eventHandler.getReplyDelegateType(), DelegateType::Other);
|
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()
|
void EventHandlerTest::replyAuthor()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(5).get();
|
auto event = room->messageEvents().at(5).get();
|
||||||
@@ -334,6 +499,15 @@ void EventHandlerTest::replyAuthor()
|
|||||||
QCOMPARE(eventHandler.getReplyAuthor(), room->getUser(nullptr));
|
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()
|
void EventHandlerTest::replyBody()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(5).get();
|
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"));
|
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()
|
void EventHandlerTest::replyMediaInfo()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(6).get();
|
auto event = room->messageEvents().at(6).get();
|
||||||
@@ -369,6 +552,15 @@ void EventHandlerTest::replyMediaInfo()
|
|||||||
QCOMPARE(thumbnailInfo["height"_ls], 450);
|
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()
|
void EventHandlerTest::thread()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(0).get();
|
auto event = room->messageEvents().at(0).get();
|
||||||
@@ -392,6 +584,15 @@ void EventHandlerTest::thread()
|
|||||||
QCOMPARE(eventHandler.getReplyId(), QStringLiteral("$threadmessage1:example.org"));
|
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()
|
void EventHandlerTest::location()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(7).get();
|
auto event = room->messageEvents().at(7).get();
|
||||||
@@ -402,6 +603,18 @@ void EventHandlerTest::location()
|
|||||||
QCOMPARE(eventHandler.getLocationAssetType(), QStringLiteral("m.pin"));
|
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()
|
void EventHandlerTest::readMarkers()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(0).get();
|
auto event = room->messageEvents().at(0).get();
|
||||||
@@ -431,5 +644,37 @@ void EventHandlerTest::readMarkers()
|
|||||||
QCOMPARE(eventHandler.getReadMarkersString().startsWith(QStringLiteral("6 users:")), true);
|
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)
|
QTEST_MAIN(EventHandlerTest)
|
||||||
#include "eventhandlertest.moc"
|
#include "eventhandlertest.moc"
|
||||||
|
|||||||
@@ -42,101 +42,18 @@ void NeoChatRoomTest::initTestCase()
|
|||||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||||
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
||||||
|
|
||||||
auto json = QJsonDocument::fromJson(R"EVENT({
|
QFile testMinSyncFile;
|
||||||
"account_data": {
|
testMinSyncFile.setFileName(QLatin1String(DATA_DIR) + u'/' + QLatin1String("test-min-sync.json"));
|
||||||
"events": [
|
testMinSyncFile.open(QIODevice::ReadOnly);
|
||||||
{
|
const auto testMinSyncJson = QJsonDocument::fromJson(testMinSyncFile.readAll());
|
||||||
"content": {
|
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, testMinSyncJson.object());
|
||||||
"tags": {
|
|
||||||
"u.work": {
|
|
||||||
"order": 0.9
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "m.tag"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"custom_config_key": "custom_config_value"
|
|
||||||
},
|
|
||||||
"type": "org.example.custom.room.config"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ephemeral": {
|
|
||||||
"events": [
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"user_ids": [
|
|
||||||
"@alice:matrix.org",
|
|
||||||
"@bob:example.com"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
|
||||||
"type": "m.typing"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"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());
|
|
||||||
room->update(std::move(roomData));
|
room->update(std::move(roomData));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NeoChatRoomTest::subtitleTextTest()
|
void NeoChatRoomTest::subtitleTextTest()
|
||||||
{
|
{
|
||||||
QCOMPARE(room->timelineSize(), 1);
|
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()
|
void NeoChatRoomTest::eventTest()
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ private Q_SLOTS:
|
|||||||
void stripDisallowedTags();
|
void stripDisallowedTags();
|
||||||
void stripDisallowedAttributes();
|
void stripDisallowedAttributes();
|
||||||
void emptyCodeTags();
|
void emptyCodeTags();
|
||||||
|
void formatBlockQuote();
|
||||||
|
|
||||||
void sendSimpleStringCase();
|
void sendSimpleStringCase();
|
||||||
void sendSingleParaMarkup();
|
void sendSingleParaMarkup();
|
||||||
@@ -66,6 +67,7 @@ private Q_SLOTS:
|
|||||||
void receiveRichEdited_data();
|
void receiveRichEdited_data();
|
||||||
void receiveRichEdited();
|
void receiveRichEdited();
|
||||||
void receiveLineSeparator();
|
void receiveLineSeparator();
|
||||||
|
void receiveRichCodeUrl();
|
||||||
|
|
||||||
void linkPreviewsMatch_data();
|
void linkPreviewsMatch_data();
|
||||||
void linkPreviewsMatch();
|
void linkPreviewsMatch();
|
||||||
@@ -78,131 +80,11 @@ void TextHandlerTest::initTestCase()
|
|||||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||||
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
||||||
|
|
||||||
const auto json = QJsonDocument::fromJson(R"EVENT({
|
QFile testTextHandlerSyncFile;
|
||||||
"account_data": {
|
testTextHandlerSyncFile.setFileName(QLatin1String(DATA_DIR) + u'/' + QLatin1String("test-texthandler-sync.json"));
|
||||||
"events": [
|
testTextHandlerSyncFile.open(QIODevice::ReadOnly);
|
||||||
{
|
const auto testTextHandlerSyncJson = QJsonDocument::fromJson(testTextHandlerSyncFile.readAll());
|
||||||
"content": {
|
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, testTextHandlerSyncJson.object());
|
||||||
"tags": {
|
|
||||||
"u.work": {
|
|
||||||
"order": 0.9
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "m.tag"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"custom_config_key": "custom_config_value"
|
|
||||||
},
|
|
||||||
"type": "org.example.custom.room.config"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ephemeral": {
|
|
||||||
"events": [
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"user_ids": [
|
|
||||||
"@alice:matrix.org",
|
|
||||||
"@bob:example.com"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
|
||||||
"type": "m.typing"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"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());
|
|
||||||
room->update(std::move(roomData));
|
room->update(std::move(roomData));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,6 +147,16 @@ void TextHandlerTest::emptyCodeTags()
|
|||||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextHandlerTest::formatBlockQuote()
|
||||||
|
{
|
||||||
|
auto input = QStringLiteral("<blockquote>\n<p>Lorem Ispum</p>\n</blockquote>");
|
||||||
|
auto expectedOutput = QStringLiteral("<blockquote><table><tr><td>\u201CLorem Ispum\u201D</td></tr></table></blockquote>");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(input);
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(), expectedOutput);
|
||||||
|
}
|
||||||
|
|
||||||
void TextHandlerTest::sendSimpleStringCase()
|
void TextHandlerTest::sendSimpleStringCase()
|
||||||
{
|
{
|
||||||
const QString testInputString = QStringLiteral("This data should just be put in a paragraph.");
|
const QString testInputString = QStringLiteral("This data should just be put in a paragraph.");
|
||||||
@@ -577,8 +469,9 @@ void TextHandlerTest::receiveRichEdited_data()
|
|||||||
QTest::newRow("basic") << QStringLiteral("Edited") << QStringLiteral("Edited <span style=\"color:#000000\">(edited)</span>");
|
QTest::newRow("basic") << QStringLiteral("Edited") << QStringLiteral("Edited <span style=\"color:#000000\">(edited)</span>");
|
||||||
QTest::newRow("multiple paragraphs") << QStringLiteral("<p>Edited</p>\n<p>Edited</p>")
|
QTest::newRow("multiple paragraphs") << QStringLiteral("<p>Edited</p>\n<p>Edited</p>")
|
||||||
<< QStringLiteral("<p>Edited</p>\n<p>Edited <span style=\"color:#000000\">(edited)</span></p>");
|
<< QStringLiteral("<p>Edited</p>\n<p>Edited <span style=\"color:#000000\">(edited)</span></p>");
|
||||||
QTest::newRow("blockquote") << QStringLiteral("<blockquote>Edited</blockquote>")
|
QTest::newRow("blockquote")
|
||||||
<< QStringLiteral("<blockquote>Edited</blockquote><p> <span style=\"color:#000000\">(edited)</span></p>");
|
<< QStringLiteral("<blockquote>Edited</blockquote>")
|
||||||
|
<< QStringLiteral("<blockquote><table><tr><td>\u201CEdited\u201D</td></tr></table></blockquote><p> <span style=\"color:#000000\">(edited)</span></p>");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextHandlerTest::receiveRichEdited()
|
void TextHandlerTest::receiveRichEdited()
|
||||||
@@ -647,5 +540,13 @@ void TextHandlerTest::linkPreviewsReject()
|
|||||||
QCOMPARE(testTextHandler.getLinkPreviews(), testOutputLinks);
|
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)
|
QTEST_MAIN(TextHandlerTest)
|
||||||
#include "texthandlertest.moc"
|
#include "texthandlertest.moc"
|
||||||
|
|||||||
@@ -343,6 +343,7 @@ to provide a convergent experience across multiple platforms.</p>
|
|||||||
<content_attribute id="social-chat">intense</content_attribute>
|
<content_attribute id="social-chat">intense</content_attribute>
|
||||||
</content_rating>
|
</content_rating>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="23.08.3" date="2023-11-09"/>
|
||||||
<release version="23.08.2" date="2023-10-12"/>
|
<release version="23.08.2" date="2023-10-12"/>
|
||||||
<release version="23.08.0" date="2023-08-24">
|
<release version="23.08.0" date="2023-08-24">
|
||||||
<url>https://kde.org/announcements/gear/23.08.0/#neochathttpsappskdeorgneochat</url>
|
<url>https://kde.org/announcements/gear/23.08.0/#neochathttpsappskdeorgneochat</url>
|
||||||
|
|||||||
824
po/ar/neochat.po
824
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
813
po/az/neochat.po
813
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
834
po/ca/neochat.po
834
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
803
po/cs/neochat.po
803
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
829
po/da/neochat.po
829
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
846
po/de/neochat.po
846
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
844
po/el/neochat.po
844
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
819
po/eo/neochat.po
819
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
841
po/es/neochat.po
841
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
827
po/eu/neochat.po
827
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
1060
po/fi/neochat.po
1060
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
854
po/fr/neochat.po
854
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
846
po/hu/neochat.po
846
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
831
po/ia/neochat.po
831
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
842
po/id/neochat.po
842
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
806
po/ie/neochat.po
806
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
857
po/it/neochat.po
857
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
813
po/ja/neochat.po
813
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
829
po/ka/neochat.po
829
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
837
po/ko/neochat.po
837
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
813
po/lt/neochat.po
813
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
837
po/nl/neochat.po
837
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
830
po/nn/neochat.po
830
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
810
po/pa/neochat.po
810
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
826
po/pl/neochat.po
826
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
836
po/pt/neochat.po
836
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
850
po/ru/neochat.po
850
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
810
po/sk/neochat.po
810
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
829
po/sl/neochat.po
829
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
850
po/sv/neochat.po
850
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
838
po/ta/neochat.po
838
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
852
po/tr/neochat.po
852
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
836
po/uk/neochat.po
836
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
@@ -146,13 +146,14 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
|||||||
qml/main.qml
|
qml/main.qml
|
||||||
qml/AccountMenu.qml
|
qml/AccountMenu.qml
|
||||||
qml/ExploreComponent.qml
|
qml/ExploreComponent.qml
|
||||||
|
qml/ExploreComponentMobile.qml
|
||||||
qml/ContextMenu.qml
|
qml/ContextMenu.qml
|
||||||
qml/CollapsedRoomDelegate.qml
|
qml/CollapsedRoomDelegate.qml
|
||||||
qml/RoomDelegate.qml
|
qml/RoomDelegate.qml
|
||||||
qml/RoomListPage.qml
|
qml/RoomListPage.qml
|
||||||
qml/SpaceListContextMenu.qml
|
qml/SpaceListContextMenu.qml
|
||||||
qml/UserInfo.qml
|
qml/UserInfo.qml
|
||||||
qml/LoadingPage.qml
|
qml/UserInfoDesktop.qml
|
||||||
qml/RoomPage.qml
|
qml/RoomPage.qml
|
||||||
qml/RoomWindow.qml
|
qml/RoomWindow.qml
|
||||||
qml/JoinRoomPage.qml
|
qml/JoinRoomPage.qml
|
||||||
@@ -163,7 +164,7 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
|||||||
qml/ImageEditorPage.qml
|
qml/ImageEditorPage.qml
|
||||||
qml/WelcomePage.qml
|
qml/WelcomePage.qml
|
||||||
qml/General.qml
|
qml/General.qml
|
||||||
qml/Security.qml
|
qml/RoomSecurity.qml
|
||||||
qml/PushNotification.qml
|
qml/PushNotification.qml
|
||||||
qml/Categories.qml
|
qml/Categories.qml
|
||||||
qml/Permissions.qml
|
qml/Permissions.qml
|
||||||
@@ -286,6 +287,9 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
|||||||
qml/SpaceHierarchyDelegate.qml
|
qml/SpaceHierarchyDelegate.qml
|
||||||
qml/RemoveChildDialog.qml
|
qml/RemoveChildDialog.qml
|
||||||
qml/SelectParentDialog.qml
|
qml/SelectParentDialog.qml
|
||||||
|
qml/Security.qml
|
||||||
|
qml/QrCodeMaximizeComponent.qml
|
||||||
|
qml/SelectSpacesDialog.qml
|
||||||
RESOURCES
|
RESOURCES
|
||||||
qml/confetti.png
|
qml/confetti.png
|
||||||
qml/glowdot.png
|
qml/glowdot.png
|
||||||
@@ -307,6 +311,13 @@ ecm_qt_declare_logging_category(neochat
|
|||||||
DEFAULT_SEVERITY Info
|
DEFAULT_SEVERITY Info
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ecm_qt_declare_logging_category(neochat
|
||||||
|
HEADER "chatdocumenthandler_logging.h"
|
||||||
|
IDENTIFIER "ChatDocumentHandling"
|
||||||
|
CATEGORY_NAME "org.kde.neochat.chatdocumenthandler"
|
||||||
|
DEFAULT_SEVERITY Info
|
||||||
|
)
|
||||||
|
|
||||||
add_executable(neochat-app
|
add_executable(neochat-app
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
@@ -345,7 +356,27 @@ if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models ${CMAKE_CURRENT_SOURCE_DIR}/enums)
|
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::ColorScheme 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)
|
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
||||||
|
|
||||||
@@ -433,6 +464,7 @@ if(ANDROID)
|
|||||||
"gps"
|
"gps"
|
||||||
"system-users-symbolic"
|
"system-users-symbolic"
|
||||||
)
|
)
|
||||||
|
ecm_add_android_apk(neochat-app ANDROID_DIR ${CMAKE_SOURCE_DIR/android})
|
||||||
else()
|
else()
|
||||||
target_link_libraries(neochat PUBLIC Qt::Widgets KF6::KIOWidgets)
|
target_link_libraries(neochat PUBLIC Qt::Widgets KF6::KIOWidgets)
|
||||||
install(FILES neochat.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFYRCDIR})
|
install(FILES neochat.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFYRCDIR})
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ QString ActionsHandler::handleMentions(QString handledText, QList<Mention> *ment
|
|||||||
}
|
}
|
||||||
handledText = handledText.replace(mention.cursor.anchor(),
|
handledText = handledText.replace(mention.cursor.anchor(),
|
||||||
mention.cursor.position() - mention.cursor.anchor(),
|
mention.cursor.position() - mention.cursor.anchor(),
|
||||||
QStringLiteral("[%1](https://matrix.to/#/%2)").arg(mention.text, mention.id));
|
QStringLiteral("[%1](https://matrix.to/#/%2)").arg(mention.text.toHtmlEscaped(), mention.id));
|
||||||
}
|
}
|
||||||
mentions->clear();
|
mentions->clear();
|
||||||
|
|
||||||
|
|||||||
@@ -173,3 +173,5 @@ void ChatBarCache::setSavedText(const QString &savedText)
|
|||||||
{
|
{
|
||||||
m_savedText = savedText;
|
m_savedText = savedText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "moc_chatbarcache.cpp"
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
#include <Sonnet/BackgroundChecker>
|
#include <Sonnet/BackgroundChecker>
|
||||||
#include <Sonnet/Settings>
|
#include <Sonnet/Settings>
|
||||||
|
|
||||||
|
#include "chatdocumenthandler_logging.h"
|
||||||
|
|
||||||
class SyntaxHighlighter : public QSyntaxHighlighter
|
class SyntaxHighlighter : public QSyntaxHighlighter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -98,7 +100,7 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
|
|||||||
, m_document(nullptr)
|
, m_document(nullptr)
|
||||||
, m_cursorPosition(-1)
|
, m_cursorPosition(-1)
|
||||||
, m_highlighter(new SyntaxHighlighter(this))
|
, m_highlighter(new SyntaxHighlighter(this))
|
||||||
, m_completionModel(new CompletionModel())
|
, m_completionModel(new CompletionModel(this))
|
||||||
{
|
{
|
||||||
connect(this, &ChatDocumentHandler::roomChanged, this, [this]() {
|
connect(this, &ChatDocumentHandler::roomChanged, this, [this]() {
|
||||||
m_completionModel->setRoom(m_room);
|
m_completionModel->setRoom(m_room);
|
||||||
@@ -148,20 +150,6 @@ int ChatDocumentHandler::completionStartIndex() const
|
|||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChatDocumentHandler::isEdit() const
|
|
||||||
{
|
|
||||||
return m_isEdit;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatDocumentHandler::setIsEdit(bool edit)
|
|
||||||
{
|
|
||||||
if (edit == m_isEdit) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_isEdit = edit;
|
|
||||||
Q_EMIT isEditChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
QQuickTextDocument *ChatDocumentHandler::document() const
|
QQuickTextDocument *ChatDocumentHandler::document() const
|
||||||
{
|
{
|
||||||
return m_document;
|
return m_document;
|
||||||
@@ -227,6 +215,15 @@ void ChatDocumentHandler::setChatBarCache(ChatBarCache *chatBarCache)
|
|||||||
|
|
||||||
void ChatDocumentHandler::complete(int index)
|
void ChatDocumentHandler::complete(int index)
|
||||||
{
|
{
|
||||||
|
if (m_document == nullptr) {
|
||||||
|
qCWarning(ChatDocumentHandling) << "complete called with m_document set to nullptr.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_completionModel->autoCompletionType() == CompletionModel::None) {
|
||||||
|
qCWarning(ChatDocumentHandling) << "complete called with m_completionModel->autoCompletionType() == CompletionModel::None.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_completionModel->autoCompletionType() == CompletionModel::User) {
|
if (m_completionModel->autoCompletionType() == CompletionModel::User) {
|
||||||
auto name = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::DisplayNameRole).toString();
|
auto name = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::DisplayNameRole).toString();
|
||||||
auto id = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::SubtitleRole).toString();
|
auto id = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::SubtitleRole).toString();
|
||||||
@@ -310,15 +307,17 @@ void ChatDocumentHandler::setSelectionEnd(int position)
|
|||||||
|
|
||||||
QString ChatDocumentHandler::getText() const
|
QString ChatDocumentHandler::getText() const
|
||||||
{
|
{
|
||||||
if (!m_room) {
|
if (!m_chatBarCache) {
|
||||||
return QString();
|
qCWarning(ChatDocumentHandling) << "getText called with m_chatBarCache set to nullptr.";
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
return m_chatBarCache->text();
|
return m_chatBarCache->text();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatDocumentHandler::pushMention(const Mention mention) const
|
void ChatDocumentHandler::pushMention(const Mention mention) const
|
||||||
{
|
{
|
||||||
if (!m_room) {
|
if (!m_chatBarCache) {
|
||||||
|
qCWarning(ChatDocumentHandling) << "pushMention called with m_chatBarCache set to nullptr.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_chatBarCache->mentions()->push_back(mention);
|
m_chatBarCache->mentions()->push_back(mention);
|
||||||
|
|||||||
@@ -62,14 +62,6 @@ class ChatDocumentHandler : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Is the instance being used to handle an edit message.
|
|
||||||
*
|
|
||||||
* This is needed to ensure that the text and mentions are saved and retrieved
|
|
||||||
* from the correct parameters in the assigned room.
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(bool isEdit READ isEdit WRITE setIsEdit NOTIFY isEditChanged)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The QQuickTextDocument that is being handled.
|
* @brief The QQuickTextDocument that is being handled.
|
||||||
*/
|
*/
|
||||||
@@ -96,7 +88,7 @@ class ChatDocumentHandler : public QObject
|
|||||||
* This is typically provided to a qml component to visualise the current
|
* This is typically provided to a qml component to visualise the current
|
||||||
* completion results.
|
* completion results.
|
||||||
*/
|
*/
|
||||||
Q_PROPERTY(CompletionModel *completionModel READ completionModel NOTIFY completionModelChanged)
|
Q_PROPERTY(CompletionModel *completionModel READ completionModel CONSTANT)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The current room that the the text document is being handled for.
|
* @brief The current room that the the text document is being handled for.
|
||||||
@@ -121,9 +113,6 @@ class ChatDocumentHandler : public QObject
|
|||||||
public:
|
public:
|
||||||
explicit ChatDocumentHandler(QObject *parent = nullptr);
|
explicit ChatDocumentHandler(QObject *parent = nullptr);
|
||||||
|
|
||||||
[[nodiscard]] bool isEdit() const;
|
|
||||||
void setIsEdit(bool edit);
|
|
||||||
|
|
||||||
[[nodiscard]] QQuickTextDocument *document() const;
|
[[nodiscard]] QQuickTextDocument *document() const;
|
||||||
void setDocument(QQuickTextDocument *document);
|
void setDocument(QQuickTextDocument *document);
|
||||||
|
|
||||||
@@ -144,7 +133,6 @@ public:
|
|||||||
|
|
||||||
Q_INVOKABLE void complete(int index);
|
Q_INVOKABLE void complete(int index);
|
||||||
|
|
||||||
void updateCompletions();
|
|
||||||
CompletionModel *completionModel() const;
|
CompletionModel *completionModel() const;
|
||||||
|
|
||||||
[[nodiscard]] QColor mentionColor() const;
|
[[nodiscard]] QColor mentionColor() const;
|
||||||
@@ -154,12 +142,10 @@ public:
|
|||||||
void setErrorColor(const QColor &color);
|
void setErrorColor(const QColor &color);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void isEditChanged();
|
|
||||||
void documentChanged();
|
void documentChanged();
|
||||||
void cursorPositionChanged();
|
void cursorPositionChanged();
|
||||||
void roomChanged();
|
void roomChanged();
|
||||||
void chatBarCacheChanged();
|
void chatBarCacheChanged();
|
||||||
void completionModelChanged();
|
|
||||||
void selectionStartChanged();
|
void selectionStartChanged();
|
||||||
void selectionEndChanged();
|
void selectionEndChanged();
|
||||||
void errorColorChanged();
|
void errorColorChanged();
|
||||||
@@ -168,8 +154,6 @@ Q_SIGNALS:
|
|||||||
private:
|
private:
|
||||||
int completionStartIndex() const;
|
int completionStartIndex() const;
|
||||||
|
|
||||||
bool m_isEdit = false;
|
|
||||||
|
|
||||||
QPointer<QQuickTextDocument> m_document;
|
QPointer<QQuickTextDocument> m_document;
|
||||||
|
|
||||||
QPointer<NeoChatRoom> m_room;
|
QPointer<NeoChatRoom> m_room;
|
||||||
|
|||||||
@@ -6,13 +6,8 @@
|
|||||||
|
|
||||||
#include <qt6keychain/keychain.h>
|
#include <qt6keychain/keychain.h>
|
||||||
|
|
||||||
#include <KConfig>
|
|
||||||
#include <KConfigGroup>
|
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
#include <KWindowConfig>
|
#include <KWindowConfig>
|
||||||
#ifdef HAVE_WINDOWSYSTEM
|
|
||||||
#include <KWindowEffects>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
@@ -65,7 +60,8 @@ Controller::Controller(QObject *parent)
|
|||||||
invokeLogin();
|
invokeLogin();
|
||||||
});
|
});
|
||||||
|
|
||||||
QObject::connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit, QGuiApplication::instance(), [] {
|
QObject::connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit, QGuiApplication::instance(), [this] {
|
||||||
|
delete m_trayIcon;
|
||||||
NeoChatConfig::self()->save();
|
NeoChatConfig::self()->save();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -103,10 +99,6 @@ Controller::Controller(QObject *parent)
|
|||||||
}
|
}
|
||||||
oldAccountCount = m_accountRegistry.size();
|
oldAccountCount = m_accountRegistry.size();
|
||||||
});
|
});
|
||||||
|
|
||||||
QTimer::singleShot(0, this, [this] {
|
|
||||||
m_pushRuleModel = new PushRuleModel;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller &Controller::instance()
|
Controller &Controller::instance()
|
||||||
@@ -175,6 +167,8 @@ void Controller::invokeLogin()
|
|||||||
QString id = NeoChatConfig::self()->activeConnection();
|
QString id = NeoChatConfig::self()->activeConnection();
|
||||||
for (const auto &accountId : accounts) {
|
for (const auto &accountId : accounts) {
|
||||||
AccountSettings account{accountId};
|
AccountSettings account{accountId};
|
||||||
|
m_accountsLoading += accountId;
|
||||||
|
Q_EMIT accountsLoadingChanged();
|
||||||
if (id.isEmpty()) {
|
if (id.isEmpty()) {
|
||||||
// handle case where the account config is empty
|
// handle case where the account config is empty
|
||||||
id = accountId;
|
id = accountId;
|
||||||
@@ -194,6 +188,8 @@ void Controller::invokeLogin()
|
|||||||
connect(connection, &NeoChatConnection::connected, this, [this, connection, id] {
|
connect(connection, &NeoChatConnection::connected, this, [this, connection, id] {
|
||||||
connection->loadState();
|
connection->loadState();
|
||||||
addConnection(connection);
|
addConnection(connection);
|
||||||
|
m_accountsLoading.removeAll(connection->userId());
|
||||||
|
Q_EMIT accountsLoadingChanged();
|
||||||
if (connection->userId() == id) {
|
if (connection->userId() == id) {
|
||||||
setActiveConnection(connection);
|
setActiveConnection(connection);
|
||||||
connectSingleShot(connection, &NeoChatConnection::syncDone, this, &Controller::initiated);
|
connectSingleShot(connection, &NeoChatConnection::syncDone, this, &Controller::initiated);
|
||||||
@@ -354,12 +350,6 @@ void Controller::setActiveConnection(NeoChatConnection *connection)
|
|||||||
}
|
}
|
||||||
NeoChatConfig::self()->save();
|
NeoChatConfig::self()->save();
|
||||||
Q_EMIT activeConnectionChanged();
|
Q_EMIT activeConnectionChanged();
|
||||||
Q_EMIT activeConnectionIndexChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
PushRuleModel *Controller::pushRuleModel() const
|
|
||||||
{
|
|
||||||
return m_pushRuleModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::saveWindowGeometry()
|
void Controller::saveWindowGeometry()
|
||||||
@@ -384,56 +374,6 @@ void Controller::joinRoom(const QString &alias)
|
|||||||
RoomManager::instance().joinRoom(m_connection, alias, QStringList{knownServer});
|
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)
|
void Controller::forceRefreshTextDocument(QQuickTextDocument *textDocument, QQuickItem *item)
|
||||||
{
|
{
|
||||||
// HACK: Workaround bug QTBUG 93281
|
// HACK: Workaround bug QTBUG 93281
|
||||||
@@ -470,14 +410,6 @@ void Controller::setApplicationProxy()
|
|||||||
QNetworkProxy::setApplicationProxy(proxy);
|
QNetworkProxy::setApplicationProxy(proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Controller::activeConnectionIndex() const
|
|
||||||
{
|
|
||||||
auto result = std::find_if(m_accountRegistry.accounts().begin(), m_accountRegistry.accounts().end(), [this](const auto &it) {
|
|
||||||
return it == m_connection;
|
|
||||||
});
|
|
||||||
return result - m_accountRegistry.accounts().begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Controller::isFlatpak() const
|
bool Controller::isFlatpak() const
|
||||||
{
|
{
|
||||||
#ifdef NEOCHAT_FLATPAK
|
#ifdef NEOCHAT_FLATPAK
|
||||||
|
|||||||
@@ -3,13 +3,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "models/pushrulemodel.h"
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QQmlEngine>
|
#include <QQmlEngine>
|
||||||
#include <QQuickItem>
|
#include <QQuickItem>
|
||||||
|
|
||||||
#include <KFormat>
|
|
||||||
|
|
||||||
#include "neochatconnection.h"
|
#include "neochatconnection.h"
|
||||||
#include <Quotient/accountregistry.h>
|
#include <Quotient/accountregistry.h>
|
||||||
#include <Quotient/jobs/basejob.h>
|
#include <Quotient/jobs/basejob.h>
|
||||||
@@ -49,26 +46,11 @@ class Controller : public QObject
|
|||||||
*/
|
*/
|
||||||
Q_PROPERTY(NeoChatConnection *activeConnection READ activeConnection WRITE setActiveConnection NOTIFY activeConnectionChanged)
|
Q_PROPERTY(NeoChatConnection *activeConnection READ activeConnection WRITE setActiveConnection NOTIFY activeConnectionChanged)
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The PushRuleModel that has the active connection's push rules.
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(PushRuleModel *pushRuleModel READ pushRuleModel CONSTANT)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The row number in the accounts directory of the active connection.
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(int activeConnectionIndex READ activeConnectionIndex NOTIFY activeConnectionIndexChanged)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Whether the OS NeoChat is running on supports sytem tray icons.
|
* @brief Whether the OS NeoChat is running on supports sytem tray icons.
|
||||||
*/
|
*/
|
||||||
Q_PROPERTY(bool supportSystemTray READ supportSystemTray CONSTANT)
|
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.
|
* @brief Whether NeoChat is currently able to connect to the server.
|
||||||
*/
|
*/
|
||||||
@@ -81,6 +63,8 @@ class Controller : public QObject
|
|||||||
*/
|
*/
|
||||||
Q_PROPERTY(bool isFlatpak READ isFlatpak CONSTANT)
|
Q_PROPERTY(bool isFlatpak READ isFlatpak CONSTANT)
|
||||||
|
|
||||||
|
Q_PROPERTY(QStringList accountsLoading MEMBER m_accountsLoading NOTIFY accountsLoadingChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Defines the status after an attempt to change the password on an account.
|
* @brief Defines the status after an attempt to change the password on an account.
|
||||||
@@ -102,8 +86,6 @@ public:
|
|||||||
void setActiveConnection(NeoChatConnection *connection);
|
void setActiveConnection(NeoChatConnection *connection);
|
||||||
[[nodiscard]] NeoChatConnection *activeConnection() const;
|
[[nodiscard]] NeoChatConnection *activeConnection() const;
|
||||||
|
|
||||||
[[nodiscard]] PushRuleModel *pushRuleModel() const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Add a new connection to the account registry.
|
* @brief Add a new connection to the account registry.
|
||||||
*/
|
*/
|
||||||
@@ -114,8 +96,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
void dropConnection(NeoChatConnection *c);
|
void dropConnection(NeoChatConnection *c);
|
||||||
|
|
||||||
int activeConnectionIndex() const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Save an access token to the keychain for the given account.
|
* @brief Save an access token to the keychain for the given account.
|
||||||
*/
|
*/
|
||||||
@@ -126,20 +106,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
Q_INVOKABLE void joinRoom(const QString &alias);
|
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;
|
[[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;
|
bool isOnline() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -151,20 +119,6 @@ public:
|
|||||||
|
|
||||||
bool isFlatpak() const;
|
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.
|
* @brief Force a QQuickTextDocument to refresh when images are loaded.
|
||||||
*
|
*
|
||||||
@@ -187,10 +141,8 @@ private:
|
|||||||
bool m_isOnline = true;
|
bool m_isOnline = true;
|
||||||
QMap<Quotient::Room *, int> m_notificationCounts;
|
QMap<Quotient::Room *, int> m_notificationCounts;
|
||||||
|
|
||||||
bool hasWindowSystem() const;
|
|
||||||
|
|
||||||
QPointer<PushRuleModel> m_pushRuleModel;
|
|
||||||
Quotient::AccountRegistry m_accountRegistry;
|
Quotient::AccountRegistry m_accountRegistry;
|
||||||
|
QStringList m_accountsLoading;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void invokeLogin();
|
void invokeLogin();
|
||||||
@@ -213,7 +165,7 @@ Q_SIGNALS:
|
|||||||
void passwordStatus(Controller::PasswordStatus status);
|
void passwordStatus(Controller::PasswordStatus status);
|
||||||
void userConsentRequired(QUrl url);
|
void userConsentRequired(QUrl url);
|
||||||
void isOnlineChanged(bool isOnline);
|
void isOnlineChanged(bool isOnline);
|
||||||
void activeConnectionIndexChanged();
|
void accountsLoadingChanged();
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void saveWindowGeometry();
|
void saveWindowGeometry();
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <Quotient/events/stickerevent.h>
|
#include <Quotient/events/stickerevent.h>
|
||||||
#include <Quotient/quotient_common.h>
|
#include <Quotient/quotient_common.h>
|
||||||
|
|
||||||
|
#include "delegatetype.h"
|
||||||
#include "eventhandler_logging.h"
|
#include "eventhandler_logging.h"
|
||||||
#include "events/pollevent.h"
|
#include "events/pollevent.h"
|
||||||
#include "linkpreviewer.h"
|
#include "linkpreviewer.h"
|
||||||
@@ -50,6 +51,10 @@ const Quotient::Event *EventHandler::getEvent() const
|
|||||||
|
|
||||||
void EventHandler::setEvent(const Quotient::RoomEvent *event)
|
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) {
|
if (event == m_event) {
|
||||||
return;
|
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
|
QString EventHandler::getTimeString(bool relative, QLocale::FormatType format, bool isPending, QDateTime lastUpdated) const
|
||||||
{
|
{
|
||||||
if (m_event == nullptr) {
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
if (isPending && lastUpdated == QDateTime()) {
|
if (isPending && lastUpdated == QDateTime()) {
|
||||||
@@ -216,6 +221,15 @@ bool EventHandler::isHighlighted()
|
|||||||
|
|
||||||
bool EventHandler::isHidden()
|
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()) {
|
if (m_event->isStateEvent() && !NeoChatConfig::self()->showStateEvent()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -611,6 +625,14 @@ QString EventHandler::getGenericBody() const
|
|||||||
|
|
||||||
QVariantMap EventHandler::getMediaInfo() 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);
|
return getMediaInfoForEvent(m_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -723,6 +745,14 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const EventContent::FileInfo
|
|||||||
|
|
||||||
QSharedPointer<LinkPreviewer> EventHandler::getLinkPreviewer() const
|
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>()) {
|
if (!m_event->is<RoomMessageEvent>()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -754,7 +784,7 @@ QSharedPointer<ReactionModel> EventHandler::getReactions() const
|
|||||||
{
|
{
|
||||||
if (m_room == nullptr) {
|
if (m_room == nullptr) {
|
||||||
qCWarning(EventHandling) << "getReactions called with m_room set to nullptr.";
|
qCWarning(EventHandling) << "getReactions called with m_room set to nullptr.";
|
||||||
return {};
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (m_event == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "getReactions called with m_event set to nullptr.";
|
qCWarning(EventHandling) << "getReactions called with m_event set to nullptr.";
|
||||||
@@ -806,16 +836,33 @@ QSharedPointer<ReactionModel> EventHandler::getReactions() const
|
|||||||
|
|
||||||
bool EventHandler::hasReply() 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();
|
return !m_event->contentJson()["m.relates_to"_ls].toObject()["m.in_reply_to"_ls].toObject()["event_id"_ls].toString().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EventHandler::getReplyId() const
|
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();
|
return m_event->contentJson()["m.relates_to"_ls].toObject()["m.in_reply_to"_ls].toObject()["event_id"_ls].toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateType::Type EventHandler::getReplyDelegateType() const
|
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);
|
auto replyEvent = m_room->getReplyForEvent(*m_event);
|
||||||
if (replyEvent == nullptr) {
|
if (replyEvent == nullptr) {
|
||||||
return DelegateType::Other;
|
return DelegateType::Other;
|
||||||
@@ -903,6 +950,11 @@ QVariantMap EventHandler::getReplyMediaInfo() const
|
|||||||
|
|
||||||
bool EventHandler::isThreaded() 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)
|
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->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));
|
|| (!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
|
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.
|
// 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)
|
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) {
|
&& 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
|
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();
|
const auto geoUri = m_event->contentJson()["geo_uri"_ls].toString();
|
||||||
if (geoUri.isEmpty()) {
|
if (geoUri.isEmpty()) {
|
||||||
return -100.0; // latitude runs from -90deg to +90deg so -100 is out of range.
|
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
|
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();
|
const auto geoUri = m_event->contentJson()["geo_uri"_ls].toString();
|
||||||
if (geoUri.isEmpty()) {
|
if (geoUri.isEmpty()) {
|
||||||
return -200.0; // longitude runs from -180deg to +180deg so -200 is out of range.
|
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
|
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();
|
const auto assetType = m_event->contentJson()["org.matrix.msc3488.asset"_ls].toObject()["type"_ls].toString();
|
||||||
if (assetType.isEmpty()) {
|
if (assetType.isEmpty()) {
|
||||||
return {};
|
return {};
|
||||||
@@ -954,6 +1026,15 @@ QString EventHandler::getLocationAssetType() const
|
|||||||
|
|
||||||
bool EventHandler::hasReadMarkers() 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());
|
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||||
userIds.remove(m_room->localUser()->id());
|
userIds.remove(m_room->localUser()->id());
|
||||||
return userIds.size() > 0;
|
return userIds.size() > 0;
|
||||||
@@ -961,6 +1042,15 @@ bool EventHandler::hasReadMarkers() const
|
|||||||
|
|
||||||
QVariantList EventHandler::getReadMarkers(int maxMarkers) 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());
|
auto userIds_temp = m_room->userIdsAtEvent(m_event->id());
|
||||||
userIds_temp.remove(m_room->localUser()->id());
|
userIds_temp.remove(m_room->localUser()->id());
|
||||||
|
|
||||||
@@ -981,6 +1071,15 @@ QVariantList EventHandler::getReadMarkers(int maxMarkers) const
|
|||||||
|
|
||||||
QString EventHandler::getNumberExcessReadMarkers(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());
|
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||||
userIds.remove(m_room->localUser()->id());
|
userIds.remove(m_room->localUser()->id());
|
||||||
|
|
||||||
@@ -993,6 +1092,15 @@ QString EventHandler::getNumberExcessReadMarkers(int maxMarkers) const
|
|||||||
|
|
||||||
QString EventHandler::getReadMarkersString() 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());
|
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||||
userIds.remove(m_room->localUser()->id());
|
userIds.remove(m_room->localUser()->id());
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,8 @@ void LoginHelper::init()
|
|||||||
Q_EMIT Controller::instance().globalErrorOccured(i18n("Network Error"), std::move(error));
|
Q_EMIT Controller::instance().globalErrorOccured(i18n("Network Error"), std::move(error));
|
||||||
});
|
});
|
||||||
|
|
||||||
connectSingleShot(m_connection, &Connection::syncDone, this, []() {
|
connectSingleShot(m_connection, &Connection::syncDone, this, [this]() {
|
||||||
|
Q_EMIT loaded();
|
||||||
Q_EMIT Controller::instance().initiated();
|
Q_EMIT Controller::instance().initiated();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/login.h
14
src/login.h
@@ -79,7 +79,17 @@ class LoginHelper : public QObject
|
|||||||
Q_PROPERTY(bool isInvalidPassword READ isInvalidPassword NOTIFY isInvalidPasswordChanged)
|
Q_PROPERTY(bool isInvalidPassword READ isInvalidPassword NOTIFY isInvalidPasswordChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit LoginHelper(QObject *parent = nullptr);
|
static LoginHelper &instance()
|
||||||
|
{
|
||||||
|
static LoginHelper _instance;
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LoginHelper *create(QQmlEngine *engine, QJSEngine *)
|
||||||
|
{
|
||||||
|
engine->setObjectOwnership(&instance(), QQmlEngine::CppOwnership);
|
||||||
|
return &instance();
|
||||||
|
}
|
||||||
|
|
||||||
Q_INVOKABLE void init();
|
Q_INVOKABLE void init();
|
||||||
|
|
||||||
@@ -125,6 +135,7 @@ Q_SIGNALS:
|
|||||||
void isLoggingInChanged();
|
void isLoggingInChanged();
|
||||||
void isLoggedInChanged();
|
void isLoggedInChanged();
|
||||||
void isInvalidPasswordChanged();
|
void isInvalidPasswordChanged();
|
||||||
|
void loaded();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setHomeserverReachable(bool reachable);
|
void setHomeserverReachable(bool reachable);
|
||||||
@@ -141,4 +152,5 @@ private:
|
|||||||
bool m_isLoggingIn = false;
|
bool m_isLoggingIn = false;
|
||||||
bool m_isLoggedIn = false;
|
bool m_isLoggedIn = false;
|
||||||
bool m_invalidPassword = false;
|
bool m_invalidPassword = false;
|
||||||
|
explicit LoginHelper(QObject *parent = nullptr);
|
||||||
};
|
};
|
||||||
|
|||||||
15
src/main.cpp
15
src/main.cpp
@@ -35,7 +35,6 @@
|
|||||||
#include "neochat-version.h"
|
#include "neochat-version.h"
|
||||||
|
|
||||||
#include <Quotient/networkaccessmanager.h>
|
#include <Quotient/networkaccessmanager.h>
|
||||||
#include <Quotient/util.h>
|
|
||||||
|
|
||||||
#include "blurhashimageprovider.h"
|
#include "blurhashimageprovider.h"
|
||||||
#include "colorschemer.h"
|
#include "colorschemer.h"
|
||||||
@@ -132,7 +131,11 @@ int main(int argc, char *argv[])
|
|||||||
i18n("Matrix client"),
|
i18n("Matrix client"),
|
||||||
KAboutLicense::GPL_V3,
|
KAboutLicense::GPL_V3,
|
||||||
i18n("© 2018-2020 Black Hat, 2020-2023 KDE Community"));
|
i18n("© 2018-2020 Black Hat, 2020-2023 KDE Community"));
|
||||||
about.addAuthor(i18n("Carl Schwan"), i18n("Maintainer"), QStringLiteral("carl@carlschwan.eu"), QStringLiteral("https://carlschwan.eu"));
|
about.addAuthor(i18n("Carl Schwan"),
|
||||||
|
i18n("Maintainer"),
|
||||||
|
QStringLiteral("carl@carlschwan.eu"),
|
||||||
|
QStringLiteral("https://carlschwan.eu"),
|
||||||
|
QStringLiteral("https://carlschwan.eu/avatar.png"));
|
||||||
about.addAuthor(i18n("Tobias Fella"), i18n("Maintainer"), QStringLiteral("tobias.fella@kde.org"), QStringLiteral("https://tobiasfella.de"));
|
about.addAuthor(i18n("Tobias Fella"), i18n("Maintainer"), QStringLiteral("tobias.fella@kde.org"), QStringLiteral("https://tobiasfella.de"));
|
||||||
about.addAuthor(i18n("James Graham"), i18n("Maintainer"), QStringLiteral("james.h.graham@protonmail.com"));
|
about.addAuthor(i18n("James Graham"), i18n("Maintainer"), QStringLiteral("james.h.graham@protonmail.com"));
|
||||||
about.addCredit(i18n("Black Hat"), i18n("Original author of Spectral"), QStringLiteral("bhat@encom.eu.org"));
|
about.addCredit(i18n("Black Hat"), i18n("Original author of Spectral"), QStringLiteral("bhat@encom.eu.org"));
|
||||||
@@ -141,7 +144,7 @@ int main(int argc, char *argv[])
|
|||||||
about.setOrganizationDomain("kde.org");
|
about.setOrganizationDomain("kde.org");
|
||||||
|
|
||||||
about.addComponent(QStringLiteral("libQuotient"),
|
about.addComponent(QStringLiteral("libQuotient"),
|
||||||
i18n("A Qt5 library to write cross-platform clients for Matrix"),
|
i18n("A Qt library to write cross-platform clients for Matrix"),
|
||||||
i18nc("<version number> (built against <possibly different version number>)",
|
i18nc("<version number> (built against <possibly different version number>)",
|
||||||
"%1 (built against %2)",
|
"%1 (built against %2)",
|
||||||
Quotient::versionString(),
|
Quotient::versionString(),
|
||||||
@@ -220,7 +223,7 @@ int main(int argc, char *argv[])
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
engine.addImageProvider(QLatin1String("mxc"), new MatrixImageProvider);
|
engine.addImageProvider(QLatin1String("mxc"), MatrixImageProvider::create(&engine, &engine));
|
||||||
engine.addImageProvider(QLatin1String("blurhash"), new BlurhashImageProvider);
|
engine.addImageProvider(QLatin1String("blurhash"), new BlurhashImageProvider);
|
||||||
|
|
||||||
engine.load(QUrl(QStringLiteral("qrc:/org/kde/neochat/qml/main.qml")));
|
engine.load(QUrl(QStringLiteral("qrc:/org/kde/neochat/qml/main.qml")));
|
||||||
@@ -233,8 +236,8 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_RUNNER
|
#ifdef HAVE_RUNNER
|
||||||
Runner runner;
|
auto runner = Runner::create(&engine, &engine);
|
||||||
QDBusConnection::sessionBus().registerObject("/RoomRunner"_ls, &runner, QDBusConnection::ExportScriptableContents);
|
QDBusConnection::sessionBus().registerObject("/RoomRunner"_ls, runner, QDBusConnection::ExportScriptableContents);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QWindow *window = windowFromEngine(&engine);
|
QWindow *window = windowFromEngine(&engine);
|
||||||
|
|||||||
@@ -13,11 +13,13 @@
|
|||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
|
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
|
#include "neochatconnection.h"
|
||||||
|
|
||||||
#include <Quotient/connection.h>
|
#include <Quotient/connection.h>
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
ThumbnailResponse::ThumbnailResponse(QString id, QSize size)
|
ThumbnailResponse::ThumbnailResponse(QString id, QSize size, NeoChatConnection *connection)
|
||||||
: mediaId(std::move(id))
|
: mediaId(std::move(id))
|
||||||
, requestedSize(size)
|
, requestedSize(size)
|
||||||
, localFile(QStringLiteral("%1/image_provider/%2-%3x%4.png")
|
, localFile(QStringLiteral("%1/image_provider/%2-%3x%4.png")
|
||||||
@@ -25,6 +27,7 @@ ThumbnailResponse::ThumbnailResponse(QString id, QSize size)
|
|||||||
mediaId,
|
mediaId,
|
||||||
QString::number(requestedSize.width()),
|
QString::number(requestedSize.width()),
|
||||||
QString::number(requestedSize.height())))
|
QString::number(requestedSize.height())))
|
||||||
|
, m_connection(connection)
|
||||||
, errorStr("Image request hasn't started"_ls)
|
, errorStr("Image request hasn't started"_ls)
|
||||||
{
|
{
|
||||||
if (requestedSize.isEmpty()) {
|
if (requestedSize.isEmpty()) {
|
||||||
@@ -51,24 +54,24 @@ ThumbnailResponse::ThumbnailResponse(QString id, QSize size)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Controller::instance().activeConnection()) {
|
if (!m_connection) {
|
||||||
qWarning() << "Current connection is null";
|
qWarning() << "Current connection is null";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute a request on the main thread asynchronously
|
// Execute a request on the main thread asynchronously
|
||||||
moveToThread(Controller::instance().activeConnection()->thread());
|
moveToThread(m_connection->thread());
|
||||||
QMetaObject::invokeMethod(this, &ThumbnailResponse::startRequest, Qt::QueuedConnection);
|
QMetaObject::invokeMethod(this, &ThumbnailResponse::startRequest, Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThumbnailResponse::startRequest()
|
void ThumbnailResponse::startRequest()
|
||||||
{
|
{
|
||||||
if (!Controller::instance().activeConnection()) {
|
if (!m_connection) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Runs in the main thread, not QML thread
|
// Runs in the main thread, not QML thread
|
||||||
Q_ASSERT(QThread::currentThread() == Controller::instance().activeConnection()->thread());
|
Q_ASSERT(QThread::currentThread() == m_connection->thread());
|
||||||
job = Controller::instance().activeConnection()->getThumbnail(mediaId, requestedSize);
|
job = m_connection->getThumbnail(mediaId, requestedSize);
|
||||||
// Connect to any possible outcome including abandonment
|
// Connect to any possible outcome including abandonment
|
||||||
// to make sure the QML thread is not left stuck forever.
|
// to make sure the QML thread is not left stuck forever.
|
||||||
connect(job, &BaseJob::finished, this, &ThumbnailResponse::prepareResult);
|
connect(job, &BaseJob::finished, this, &ThumbnailResponse::prepareResult);
|
||||||
@@ -118,7 +121,7 @@ QString ThumbnailResponse::errorString() const
|
|||||||
|
|
||||||
QQuickImageResponse *MatrixImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize)
|
QQuickImageResponse *MatrixImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize)
|
||||||
{
|
{
|
||||||
return new ThumbnailResponse(id, requestedSize);
|
return new ThumbnailResponse(id, requestedSize, m_connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "moc_matriximageprovider.cpp"
|
#include "moc_matriximageprovider.cpp"
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#include <QReadWriteLock>
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
|
class NeoChatConnection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class ThumbnailResponse
|
* @class ThumbnailResponse
|
||||||
*
|
*
|
||||||
@@ -21,7 +23,7 @@ class ThumbnailResponse : public QQuickImageResponse
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit ThumbnailResponse(QString mediaId, QSize requestedSize);
|
explicit ThumbnailResponse(QString mediaId, QSize requestedSize, NeoChatConnection *m_connection);
|
||||||
~ThumbnailResponse() override = default;
|
~ThumbnailResponse() override = default;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
@@ -33,6 +35,7 @@ private:
|
|||||||
QSize requestedSize;
|
QSize requestedSize;
|
||||||
const QString localFile;
|
const QString localFile;
|
||||||
Quotient::MediaThumbnailJob *job = nullptr;
|
Quotient::MediaThumbnailJob *job = nullptr;
|
||||||
|
NeoChatConnection *m_connection;
|
||||||
|
|
||||||
QImage image;
|
QImage image;
|
||||||
QString errorStr;
|
QString errorStr;
|
||||||
@@ -51,11 +54,27 @@ private:
|
|||||||
*/
|
*/
|
||||||
class MatrixImageProvider : public QQuickAsyncImageProvider
|
class MatrixImageProvider : public QQuickAsyncImageProvider
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
QML_SINGLETON
|
||||||
|
|
||||||
|
Q_PROPERTY(NeoChatConnection *connection MEMBER m_connection)
|
||||||
public:
|
public:
|
||||||
|
static MatrixImageProvider *create(QQmlEngine *engine, QJSEngine *)
|
||||||
|
{
|
||||||
|
static MatrixImageProvider instance;
|
||||||
|
engine->setObjectOwnership(&instance, QQmlEngine::CppOwnership);
|
||||||
|
return &instance;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return a job to provide the image with the given ID.
|
* @brief Return a job to provide the image with the given ID.
|
||||||
*
|
*
|
||||||
* @sa QQuickAsyncImageProvider::requestImageResponse
|
* @sa QQuickAsyncImageProvider::requestImageResponse
|
||||||
*/
|
*/
|
||||||
QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
|
QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
NeoChatConnection *m_connection = nullptr;
|
||||||
|
MatrixImageProvider() = default;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ QList<ActionsModel::Action> actions{
|
|||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Knocking room <roomname>.", "Knocking room %1.", text));
|
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Knocking room <roomname>.", "Knocking room %1.", text));
|
||||||
auto connection = Controller::instance().activeConnection();
|
auto connection = room->connection();
|
||||||
const auto knownServer = roomName.mid(roomName.indexOf(":"_ls) + 1);
|
const auto knownServer = roomName.mid(roomName.indexOf(":"_ls) + 1);
|
||||||
if (parts.length() >= 2) {
|
if (parts.length() >= 2) {
|
||||||
RoomManager::instance().knockRoom(connection, roomName, parts[1], QStringList{knownServer});
|
RoomManager::instance().knockRoom(connection, roomName, parts[1], QStringList{knownServer});
|
||||||
@@ -285,7 +285,7 @@ QList<ActionsModel::Action> actions{
|
|||||||
i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text));
|
i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text));
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
if (Controller::instance().activeConnection()->room(text) || Controller::instance().activeConnection()->roomByAlias(text)) {
|
if (room->connection()->room(text) || room->connection()->roomByAlias(text)) {
|
||||||
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("You are already in room <roomname>.", "You are already in room %1.", text));
|
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("You are already in room <roomname>.", "You are already in room %1.", text));
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,13 +15,28 @@
|
|||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
|
void CustomEmojiModel::setConnection(NeoChatConnection *connection)
|
||||||
|
{
|
||||||
|
if (connection == m_connection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_connection = connection;
|
||||||
|
Q_EMIT connectionChanged();
|
||||||
|
fetchEmojis();
|
||||||
|
}
|
||||||
|
|
||||||
|
NeoChatConnection *CustomEmojiModel::connection() const
|
||||||
|
{
|
||||||
|
return m_connection;
|
||||||
|
}
|
||||||
|
|
||||||
void CustomEmojiModel::fetchEmojis()
|
void CustomEmojiModel::fetchEmojis()
|
||||||
{
|
{
|
||||||
if (!Controller::instance().activeConnection()) {
|
if (!m_connection) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &data = Controller::instance().activeConnection()->accountData("im.ponies.user_emotes"_ls);
|
const auto &data = m_connection->accountData("im.ponies.user_emotes"_ls);
|
||||||
if (data == nullptr) {
|
if (data == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -53,10 +68,10 @@ void CustomEmojiModel::addEmoji(const QString &name, const QUrl &location)
|
|||||||
{
|
{
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
auto job = Controller::instance().activeConnection()->uploadFile(location.toLocalFile());
|
auto job = m_connection->uploadFile(location.toLocalFile());
|
||||||
|
|
||||||
connect(job, &BaseJob::success, this, [name, location, job] {
|
connect(job, &BaseJob::success, this, [name, location, job, this] {
|
||||||
const auto &data = Controller::instance().activeConnection()->accountData("im.ponies.user_emotes"_ls);
|
const auto &data = m_connection->accountData("im.ponies.user_emotes"_ls);
|
||||||
auto json = data != nullptr ? data->contentJson() : QJsonObject();
|
auto json = data != nullptr ? data->contentJson() : QJsonObject();
|
||||||
auto emojiData = json["images"_ls].toObject();
|
auto emojiData = json["images"_ls].toObject();
|
||||||
|
|
||||||
@@ -78,7 +93,7 @@ void CustomEmojiModel::addEmoji(const QString &name, const QUrl &location)
|
|||||||
});
|
});
|
||||||
|
|
||||||
json["images"_ls] = emojiData;
|
json["images"_ls] = emojiData;
|
||||||
Controller::instance().activeConnection()->setAccountData("im.ponies.user_emotes"_ls, json);
|
m_connection->setAccountData("im.ponies.user_emotes"_ls, json);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +101,7 @@ void CustomEmojiModel::removeEmoji(const QString &name)
|
|||||||
{
|
{
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
const auto &data = Controller::instance().activeConnection()->accountData("im.ponies.user_emotes"_ls);
|
const auto &data = m_connection->accountData("im.ponies.user_emotes"_ls);
|
||||||
Q_ASSERT(data);
|
Q_ASSERT(data);
|
||||||
auto json = data->contentJson();
|
auto json = data->contentJson();
|
||||||
const QString _name = name.mid(1).chopped(1);
|
const QString _name = name.mid(1).chopped(1);
|
||||||
@@ -109,18 +124,18 @@ void CustomEmojiModel::removeEmoji(const QString &name)
|
|||||||
emojiData.remove(_name);
|
emojiData.remove(_name);
|
||||||
json["emoticons"_ls] = emojiData;
|
json["emoticons"_ls] = emojiData;
|
||||||
}
|
}
|
||||||
Controller::instance().activeConnection()->setAccountData("im.ponies.user_emotes"_ls, json);
|
m_connection->setAccountData("im.ponies.user_emotes"_ls, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomEmojiModel::CustomEmojiModel(QObject *parent)
|
CustomEmojiModel::CustomEmojiModel(QObject *parent)
|
||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
{
|
{
|
||||||
connect(&Controller::instance(), &Controller::activeConnectionChanged, this, [this]() {
|
connect(this, &CustomEmojiModel::connectionChanged, this, [this]() {
|
||||||
if (!Controller::instance().activeConnection()) {
|
if (!m_connection) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CustomEmojiModel::fetchEmojis();
|
CustomEmojiModel::fetchEmojis();
|
||||||
connect(Controller::instance().activeConnection(), &Connection::accountDataChanged, this, [this](const QString &id) {
|
connect(m_connection, &Connection::accountDataChanged, this, [this](const QString &id) {
|
||||||
if (id != QStringLiteral("im.ponies.user_emotes")) {
|
if (id != QStringLiteral("im.ponies.user_emotes")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
class NeoChatConnection;
|
||||||
|
|
||||||
struct CustomEmoji {
|
struct CustomEmoji {
|
||||||
QString name; // with :semicolons:
|
QString name; // with :semicolons:
|
||||||
QString url; // mxc://
|
QString url; // mxc://
|
||||||
@@ -31,6 +33,8 @@ class CustomEmojiModel : public QAbstractListModel
|
|||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
QML_SINGLETON
|
QML_SINGLETON
|
||||||
|
|
||||||
|
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Defines the model roles.
|
* @brief Defines the model roles.
|
||||||
@@ -98,9 +102,16 @@ public:
|
|||||||
*/
|
*/
|
||||||
Q_INVOKABLE void removeEmoji(const QString &name);
|
Q_INVOKABLE void removeEmoji(const QString &name);
|
||||||
|
|
||||||
|
void setConnection(NeoChatConnection *connection);
|
||||||
|
NeoChatConnection *connection() const;
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void connectionChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit CustomEmojiModel(QObject *parent = nullptr);
|
explicit CustomEmojiModel(QObject *parent = nullptr);
|
||||||
QList<CustomEmoji> m_emojis;
|
QList<CustomEmoji> m_emojis;
|
||||||
|
NeoChatConnection *m_connection = nullptr;
|
||||||
|
|
||||||
void fetchEmojis();
|
void fetchEmojis();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ DevicesModel::DevicesModel(QObject *parent)
|
|||||||
|
|
||||||
void DevicesModel::fetchDevices()
|
void DevicesModel::fetchDevices()
|
||||||
{
|
{
|
||||||
if (Controller::instance().activeConnection()) {
|
if (m_connection) {
|
||||||
auto job = Controller::instance().activeConnection()->callApi<GetDevicesJob>();
|
auto job = m_connection->callApi<GetDevicesJob>();
|
||||||
connect(job, &BaseJob::success, this, [this, job]() {
|
connect(job, &BaseJob::success, this, [this, job]() {
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_devices = job->devices();
|
m_devices = job->devices();
|
||||||
@@ -102,7 +102,7 @@ void DevicesModel::logout(const QString &deviceId, const QString &password)
|
|||||||
for (index = 0; m_devices[index].deviceId != deviceId; index++)
|
for (index = 0; m_devices[index].deviceId != deviceId; index++)
|
||||||
;
|
;
|
||||||
|
|
||||||
auto job = Controller::instance().activeConnection()->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId);
|
auto job = m_connection->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId);
|
||||||
|
|
||||||
connect(job, &BaseJob::result, this, [this, job, password, index] {
|
connect(job, &BaseJob::result, this, [this, job, password, index] {
|
||||||
auto onSuccess = [this, index]() {
|
auto onSuccess = [this, index]() {
|
||||||
@@ -117,9 +117,9 @@ void DevicesModel::logout(const QString &deviceId, const QString &password)
|
|||||||
authData["session"_ls] = replyData["session"_ls];
|
authData["session"_ls] = replyData["session"_ls];
|
||||||
authData["password"_ls] = password;
|
authData["password"_ls] = password;
|
||||||
authData["type"_ls] = "m.login.password"_ls;
|
authData["type"_ls] = "m.login.password"_ls;
|
||||||
QJsonObject identifier = {{"type"_ls, "m.id.user"_ls}, {"user"_ls, Controller::instance().activeConnection()->user()->id()}};
|
QJsonObject identifier = {{"type"_ls, "m.id.user"_ls}, {"user"_ls, m_connection->user()->id()}};
|
||||||
authData["identifier"_ls] = identifier;
|
authData["identifier"_ls] = identifier;
|
||||||
auto *innerJob = Controller::instance().activeConnection()->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId, authData);
|
auto *innerJob = m_connection->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId, authData);
|
||||||
connect(innerJob, &BaseJob::success, this, onSuccess);
|
connect(innerJob, &BaseJob::success, this, onSuccess);
|
||||||
} else {
|
} else {
|
||||||
onSuccess();
|
onSuccess();
|
||||||
@@ -132,7 +132,7 @@ void DevicesModel::setName(const QString &deviceId, const QString &name)
|
|||||||
int index;
|
int index;
|
||||||
for (index = 0; m_devices[index].deviceId != deviceId; index++);
|
for (index = 0; m_devices[index].deviceId != deviceId; index++);
|
||||||
|
|
||||||
auto job = Controller::instance().activeConnection()->callApi<UpdateDeviceJob>(m_devices[index].deviceId, name);
|
auto job = m_connection->callApi<UpdateDeviceJob>(m_devices[index].deviceId, name);
|
||||||
QString oldName = m_devices[index].displayName;
|
QString oldName = m_devices[index].displayName;
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_devices[index].displayName = name;
|
m_devices[index].displayName = name;
|
||||||
@@ -160,7 +160,7 @@ void DevicesModel::setConnection(Connection *connection)
|
|||||||
|
|
||||||
connect(m_connection, &Connection::sessionVerified, this, [this](const QString &userId, const QString &deviceId) {
|
connect(m_connection, &Connection::sessionVerified, this, [this](const QString &userId, const QString &deviceId) {
|
||||||
Q_UNUSED(deviceId);
|
Q_UNUSED(deviceId);
|
||||||
if (userId == Controller::instance().activeConnection()->userId()) {
|
if (userId == m_connection->userId()) {
|
||||||
fetchDevices();
|
fetchDevices();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,112 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
|
||||||
|
|
||||||
#include "keywordnotificationrulemodel.h"
|
|
||||||
#include "controller.h"
|
|
||||||
#include "notificationsmanager.h"
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <Quotient/connection.h>
|
|
||||||
#include <Quotient/converters.h>
|
|
||||||
#include <Quotient/csapi/definitions/push_ruleset.h>
|
|
||||||
#include <Quotient/csapi/pushrules.h>
|
|
||||||
#include <Quotient/jobs/basejob.h>
|
|
||||||
|
|
||||||
KeywordNotificationRuleModel::KeywordNotificationRuleModel(QObject *parent)
|
|
||||||
: QAbstractListModel(parent)
|
|
||||||
{
|
|
||||||
if (Controller::instance().activeConnection()) {
|
|
||||||
controllerConnectionChanged();
|
|
||||||
}
|
|
||||||
connect(&Controller::instance(), &Controller::activeConnectionChanged, this, &KeywordNotificationRuleModel::controllerConnectionChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeywordNotificationRuleModel::controllerConnectionChanged()
|
|
||||||
{
|
|
||||||
connect(Controller::instance().activeConnection(), &Quotient::Connection::accountDataChanged, this, &KeywordNotificationRuleModel::updateNotificationRules);
|
|
||||||
updateNotificationRules("m.push_rules");
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeywordNotificationRuleModel::updateNotificationRules(const QString &type)
|
|
||||||
{
|
|
||||||
if (type != "m.push_rules") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QJsonObject ruleDataJson = Controller::instance().activeConnection()->accountDataJson("m.push_rules");
|
|
||||||
const Quotient::PushRuleset ruleData = Quotient::fromJson<Quotient::PushRuleset>(ruleDataJson["global"].toObject());
|
|
||||||
const QList<Quotient::PushRule> contentRules = ruleData.content;
|
|
||||||
|
|
||||||
beginResetModel();
|
|
||||||
m_notificationRules.clear();
|
|
||||||
for (const auto &i : contentRules) {
|
|
||||||
if (!m_notificationRules.contains(i.ruleId) && i.ruleId[0] != '.') {
|
|
||||||
m_notificationRules.append(i.ruleId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
endResetModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant KeywordNotificationRuleModel::data(const QModelIndex &index, int role) const
|
|
||||||
{
|
|
||||||
if (!index.isValid()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index.row() >= m_notificationRules.count()) {
|
|
||||||
qDebug() << "KeywordNotificationRuleModel, something's wrong: index.row() >= m_notificationRules.count()";
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (role == NameRole) {
|
|
||||||
return m_notificationRules.at(index.row());
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
int KeywordNotificationRuleModel::rowCount(const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(parent)
|
|
||||||
|
|
||||||
return m_notificationRules.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeywordNotificationRuleModel::addKeyword(const QString &keyword)
|
|
||||||
{
|
|
||||||
if (m_notificationRules.count() == 0) {
|
|
||||||
NotificationsManager::instance().initializeKeywordNotificationAction();
|
|
||||||
}
|
|
||||||
|
|
||||||
const QList<QVariant> actions = NotificationsManager::instance().getKeywordNotificationActions();
|
|
||||||
|
|
||||||
auto job = Controller::instance()
|
|
||||||
.activeConnection()
|
|
||||||
->callApi<Quotient::SetPushRuleJob>("global", "content", keyword, actions, "", "", QList<Quotient::PushCondition>(), keyword);
|
|
||||||
connect(job, &Quotient::BaseJob::success, this, [this, keyword]() {
|
|
||||||
beginInsertRows(QModelIndex(), m_notificationRules.count(), m_notificationRules.count());
|
|
||||||
m_notificationRules.append(keyword);
|
|
||||||
endInsertRows();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeywordNotificationRuleModel::removeKeywordAtIndex(int index)
|
|
||||||
{
|
|
||||||
auto job = Controller::instance().activeConnection()->callApi<Quotient::DeletePushRuleJob>("global", "content", m_notificationRules[index]);
|
|
||||||
connect(job, &Quotient::BaseJob::success, this, [this, index]() {
|
|
||||||
beginRemoveRows(QModelIndex(), index, index);
|
|
||||||
m_notificationRules.removeAt(index);
|
|
||||||
endRemoveRows();
|
|
||||||
|
|
||||||
if (m_notificationRules.count() == 0) {
|
|
||||||
NotificationsManager::instance().deactivateKeywordNotificationAction();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<int, QByteArray> KeywordNotificationRuleModel::roleNames() const
|
|
||||||
{
|
|
||||||
return {{NameRole, QByteArrayLiteral("name")}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "moc_keywordnotificationrulemodel.cpp"
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Quotient/csapi/definitions/push_rule.h>
|
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class KeywordNotificationRuleModel
|
|
||||||
*
|
|
||||||
* This class defines the model for managing notification push rule keywords.
|
|
||||||
*/
|
|
||||||
class KeywordNotificationRuleModel : public QAbstractListModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Defines the model roles.
|
|
||||||
*/
|
|
||||||
enum EventRoles {
|
|
||||||
NameRole = Qt::DisplayRole, /**< The push rule keyword. */
|
|
||||||
};
|
|
||||||
|
|
||||||
KeywordNotificationRuleModel(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the given role value at the given index.
|
|
||||||
*
|
|
||||||
* @sa QAbstractItemModel::data
|
|
||||||
*/
|
|
||||||
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Number of rows in the model.
|
|
||||||
*
|
|
||||||
* @sa QAbstractItemModel::rowCount
|
|
||||||
*/
|
|
||||||
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns a mapping from Role enum values to role names.
|
|
||||||
*
|
|
||||||
* @sa EventRoles, QAbstractItemModel::roleNames()
|
|
||||||
*/
|
|
||||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Add a new keyword to the model.
|
|
||||||
*/
|
|
||||||
Q_INVOKABLE void addKeyword(const QString &keyword);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Remove a keyword from the model.
|
|
||||||
*/
|
|
||||||
Q_INVOKABLE void removeKeywordAtIndex(int index);
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void controllerConnectionChanged();
|
|
||||||
void updateNotificationRules(const QString &type);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QList<QString> m_notificationRules;
|
|
||||||
};
|
|
||||||
@@ -66,19 +66,6 @@ PushRuleModel::PushRuleModel(QObject *parent)
|
|||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
{
|
{
|
||||||
m_defaultKeywordAction = static_cast<PushNotificationAction::Action>(NeoChatConfig::self()->keywordPushRuleDefault());
|
m_defaultKeywordAction = static_cast<PushNotificationAction::Action>(NeoChatConfig::self()->keywordPushRuleDefault());
|
||||||
|
|
||||||
if (Controller::instance().activeConnection()) {
|
|
||||||
controllerConnectionChanged();
|
|
||||||
}
|
|
||||||
connect(&Controller::instance(), &Controller::activeConnectionChanged, this, &PushRuleModel::controllerConnectionChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PushRuleModel::controllerConnectionChanged()
|
|
||||||
{
|
|
||||||
if (Controller::instance().activeConnection()) {
|
|
||||||
connect(Controller::instance().activeConnection(), &Quotient::Connection::accountDataChanged, this, &PushRuleModel::updateNotificationRules);
|
|
||||||
updateNotificationRules(QStringLiteral("m.push_rules"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PushRuleModel::updateNotificationRules(const QString &type)
|
void PushRuleModel::updateNotificationRules(const QString &type)
|
||||||
@@ -87,7 +74,7 @@ void PushRuleModel::updateNotificationRules(const QString &type)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QJsonObject ruleDataJson = Controller::instance().activeConnection()->accountDataJson(QStringLiteral("m.push_rules"));
|
const QJsonObject ruleDataJson = m_connection->accountDataJson(QStringLiteral("m.push_rules"));
|
||||||
const Quotient::PushRuleset ruleData = Quotient::fromJson<Quotient::PushRuleset>(ruleDataJson[QStringLiteral("global")].toObject());
|
const Quotient::PushRuleset ruleData = Quotient::fromJson<Quotient::PushRuleset>(ruleDataJson[QStringLiteral("global")].toObject());
|
||||||
|
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
@@ -156,8 +143,7 @@ PushNotificationSection::Section PushRuleModel::getSection(Quotient::PushRule ru
|
|||||||
*
|
*
|
||||||
* Rooms that the user hasn't joined shouldn't have a rule.
|
* Rooms that the user hasn't joined shouldn't have a rule.
|
||||||
*/
|
*/
|
||||||
auto connection = Controller::instance().activeConnection();
|
if (m_connection->room(ruleId) != nullptr) {
|
||||||
if (connection->room(ruleId) != nullptr) {
|
|
||||||
return PushNotificationSection::Undefined;
|
return PushNotificationSection::Undefined;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -171,7 +157,7 @@ PushNotificationSection::Section PushRuleModel::getSection(Quotient::PushRule ru
|
|||||||
if (!testUserId.startsWith(u'@')) {
|
if (!testUserId.startsWith(u'@')) {
|
||||||
testUserId.prepend(u'@');
|
testUserId.prepend(u'@');
|
||||||
}
|
}
|
||||||
if (testUserId.startsWith(u'@') && !Quotient::serverPart(testUserId).isEmpty() && connection->user(testUserId) != nullptr) {
|
if (testUserId.startsWith(u'@') && !Quotient::serverPart(testUserId).isEmpty() && m_connection->user(testUserId) != nullptr) {
|
||||||
return PushNotificationSection::Undefined;
|
return PushNotificationSection::Undefined;
|
||||||
}
|
}
|
||||||
// If the rule has push conditions and one is a room ID it is a room only keyword.
|
// If the rule has push conditions and one is a room ID it is a room only keyword.
|
||||||
@@ -325,14 +311,14 @@ void PushRuleModel::addKeyword(const QString &keyword, const QString &roomId)
|
|||||||
pushConditions.append(keywordCondition);
|
pushConditions.append(keywordCondition);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto job = Controller::instance().activeConnection()->callApi<Quotient::SetPushRuleJob>(QLatin1String("global"),
|
auto job = m_connection->callApi<Quotient::SetPushRuleJob>(QLatin1String("global"),
|
||||||
PushNotificationKind::kindString(kind),
|
PushNotificationKind::kindString(kind),
|
||||||
keyword,
|
keyword,
|
||||||
actions,
|
actions,
|
||||||
QString(),
|
QString(),
|
||||||
QString(),
|
QString(),
|
||||||
pushConditions,
|
pushConditions,
|
||||||
roomId.isEmpty() ? keyword : QString());
|
roomId.isEmpty() ? keyword : QString());
|
||||||
connect(job, &Quotient::BaseJob::failure, this, [job, keyword]() {
|
connect(job, &Quotient::BaseJob::failure, this, [job, keyword]() {
|
||||||
qWarning() << QLatin1String("Unable to set push rule for keyword %1: ").arg(keyword) << job->errorString();
|
qWarning() << QLatin1String("Unable to set push rule for keyword %1: ").arg(keyword) << job->errorString();
|
||||||
});
|
});
|
||||||
@@ -351,7 +337,7 @@ void PushRuleModel::removeKeyword(const QString &keyword)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto kind = PushNotificationKind::kindString(m_rules[index].kind);
|
auto kind = PushNotificationKind::kindString(m_rules[index].kind);
|
||||||
auto job = Controller::instance().activeConnection()->callApi<Quotient::DeletePushRuleJob>(QStringLiteral("global"), kind, m_rules[index].id);
|
auto job = m_connection->callApi<Quotient::DeletePushRuleJob>(QStringLiteral("global"), kind, m_rules[index].id);
|
||||||
connect(job, &Quotient::BaseJob::failure, this, [this, job, index]() {
|
connect(job, &Quotient::BaseJob::failure, this, [this, job, index]() {
|
||||||
qWarning() << QLatin1String("Unable to remove push rule for keyword %1: ").arg(m_rules[index].id) << job->errorString();
|
qWarning() << QLatin1String("Unable to remove push rule for keyword %1: ").arg(m_rules[index].id) << job->errorString();
|
||||||
});
|
});
|
||||||
@@ -359,10 +345,10 @@ void PushRuleModel::removeKeyword(const QString &keyword)
|
|||||||
|
|
||||||
void PushRuleModel::setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled)
|
void PushRuleModel::setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled)
|
||||||
{
|
{
|
||||||
auto job = Controller::instance().activeConnection()->callApi<Quotient::IsPushRuleEnabledJob>(QStringLiteral("global"), kind, ruleId);
|
auto job = m_connection->callApi<Quotient::IsPushRuleEnabledJob>(QStringLiteral("global"), kind, ruleId);
|
||||||
connect(job, &Quotient::BaseJob::success, this, [job, kind, ruleId, enabled]() {
|
connect(job, &Quotient::BaseJob::success, this, [job, kind, ruleId, enabled, this]() {
|
||||||
if (job->enabled() != enabled) {
|
if (job->enabled() != enabled) {
|
||||||
Controller::instance().activeConnection()->callApi<Quotient::SetPushRuleEnabledJob>(QStringLiteral("global"), kind, ruleId, enabled);
|
m_connection->callApi<Quotient::SetPushRuleEnabledJob>(QStringLiteral("global"), kind, ruleId, enabled);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -376,7 +362,7 @@ void PushRuleModel::setNotificationRuleActions(const QString &kind, const QStrin
|
|||||||
actions = actionToVariant(action);
|
actions = actionToVariant(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller::instance().activeConnection()->callApi<Quotient::SetPushRuleActionsJob>(QStringLiteral("global"), kind, ruleId, actions);
|
m_connection->callApi<Quotient::SetPushRuleActionsJob>(QStringLiteral("global"), kind, ruleId, actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
PushNotificationAction::Action PushRuleModel::variantToAction(const QList<QVariant> &actions, bool enabled)
|
PushNotificationAction::Action PushRuleModel::variantToAction(const QList<QVariant> &actions, bool enabled)
|
||||||
@@ -453,4 +439,23 @@ QList<QVariant> PushRuleModel::actionToVariant(PushNotificationAction::Action ac
|
|||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NeoChatConnection *PushRuleModel::connection() const
|
||||||
|
{
|
||||||
|
return m_connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushRuleModel::setConnection(NeoChatConnection *connection)
|
||||||
|
{
|
||||||
|
if (connection == m_connection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_connection = connection;
|
||||||
|
Q_EMIT connectionChanged();
|
||||||
|
|
||||||
|
if (m_connection) {
|
||||||
|
connect(m_connection, &Quotient::Connection::accountDataChanged, this, &PushRuleModel::updateNotificationRules);
|
||||||
|
updateNotificationRules(QStringLiteral("m.push_rules"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_pushrulemodel.cpp"
|
#include "moc_pushrulemodel.cpp"
|
||||||
|
|||||||
@@ -157,6 +157,8 @@ class PushRuleModel : public QAbstractListModel
|
|||||||
*/
|
*/
|
||||||
Q_PROPERTY(bool globalNotificationsSet READ globalNotificationsSet NOTIFY globalNotificationsSetChanged)
|
Q_PROPERTY(bool globalNotificationsSet READ globalNotificationsSet NOTIFY globalNotificationsSetChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct Rule {
|
struct Rule {
|
||||||
QString id;
|
QString id;
|
||||||
@@ -225,18 +227,22 @@ public:
|
|||||||
*/
|
*/
|
||||||
Q_INVOKABLE void removeKeyword(const QString &keyword);
|
Q_INVOKABLE void removeKeyword(const QString &keyword);
|
||||||
|
|
||||||
|
void setConnection(NeoChatConnection *connection);
|
||||||
|
NeoChatConnection *connection() const;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void defaultStateChanged();
|
void defaultStateChanged();
|
||||||
void globalNotificationsEnabledChanged();
|
void globalNotificationsEnabledChanged();
|
||||||
void globalNotificationsSetChanged();
|
void globalNotificationsSetChanged();
|
||||||
|
void connectionChanged();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void controllerConnectionChanged();
|
|
||||||
void updateNotificationRules(const QString &type);
|
void updateNotificationRules(const QString &type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PushNotificationAction::Action m_defaultKeywordAction;
|
PushNotificationAction::Action m_defaultKeywordAction;
|
||||||
QList<Rule> m_rules;
|
QList<Rule> m_rules;
|
||||||
|
NeoChatConnection *m_connection;
|
||||||
|
|
||||||
void setRules(QList<Quotient::PushRule> rules, PushNotificationKind::Kind kind);
|
void setRules(QList<Quotient::PushRule> rules, PushNotificationKind::Kind kind);
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ ServerListModel::ServerListModel(QObject *parent)
|
|||||||
const auto stateConfig = KSharedConfig::openStateConfig();
|
const auto stateConfig = KSharedConfig::openStateConfig();
|
||||||
const KConfigGroup serverGroup = stateConfig->group(QStringLiteral("Servers"));
|
const KConfigGroup serverGroup = stateConfig->group(QStringLiteral("Servers"));
|
||||||
|
|
||||||
QString domain = Controller::instance().activeConnection()->domain();
|
QString domain = m_connection->domain();
|
||||||
|
|
||||||
// Add the user's homeserver
|
// Add the user's homeserver
|
||||||
m_servers.append(Server{
|
m_servers.append(Server{
|
||||||
@@ -100,7 +100,7 @@ void ServerListModel::checkServer(const QString &url)
|
|||||||
m_checkServerJob->abandon();
|
m_checkServerJob->abandon();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_checkServerJob = Controller::instance().activeConnection()->callApi<Quotient::QueryPublicRoomsJob>(url, 1);
|
m_checkServerJob = m_connection->callApi<Quotient::QueryPublicRoomsJob>(url, 1);
|
||||||
connect(m_checkServerJob, &Quotient::BaseJob::success, this, [this, url] {
|
connect(m_checkServerJob, &Quotient::BaseJob::success, this, [this, url] {
|
||||||
Q_EMIT serverCheckComplete(url, true);
|
Q_EMIT serverCheckComplete(url, true);
|
||||||
});
|
});
|
||||||
@@ -153,4 +153,18 @@ QHash<int, QByteArray> ServerListModel::roleNames() const
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NeoChatConnection *ServerListModel::connection() const
|
||||||
|
{
|
||||||
|
return m_connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerListModel::setConnection(NeoChatConnection *connection)
|
||||||
|
{
|
||||||
|
if (m_connection == connection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_connection = connection;
|
||||||
|
Q_EMIT connectionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_serverlistmodel.cpp"
|
#include "moc_serverlistmodel.cpp"
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#include <QQmlEngine>
|
#include <QQmlEngine>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
|
class NeoChatConnection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class ServerListModel
|
* @class ServerListModel
|
||||||
*
|
*
|
||||||
@@ -27,6 +29,8 @@ class ServerListModel : public QAbstractListModel
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
|
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Define the data required to represent a server.
|
* @brief Define the data required to represent a server.
|
||||||
@@ -96,10 +100,15 @@ public:
|
|||||||
*/
|
*/
|
||||||
Q_INVOKABLE void removeServerAtIndex(int index);
|
Q_INVOKABLE void removeServerAtIndex(int index);
|
||||||
|
|
||||||
|
NeoChatConnection *connection() const;
|
||||||
|
void setConnection(NeoChatConnection *connection);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void serverCheckComplete(QString url, bool valid);
|
void serverCheckComplete(QString url, bool valid);
|
||||||
|
void connectionChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<Server> m_servers;
|
QList<Server> m_servers;
|
||||||
QPointer<Quotient::QueryPublicRoomsJob> m_checkServerJob = nullptr;
|
QPointer<Quotient::QueryPublicRoomsJob> m_checkServerJob = nullptr;
|
||||||
|
NeoChatConnection *m_connection = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
SpaceChildrenModel::SpaceChildrenModel(QObject *parent)
|
SpaceChildrenModel::SpaceChildrenModel(QObject *parent)
|
||||||
: QAbstractItemModel(parent)
|
: QAbstractItemModel(parent)
|
||||||
{
|
{
|
||||||
m_rootItem = new SpaceTreeItem();
|
m_rootItem = new SpaceTreeItem(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
SpaceChildrenModel::~SpaceChildrenModel()
|
SpaceChildrenModel::~SpaceChildrenModel()
|
||||||
@@ -71,7 +71,8 @@ void SpaceChildrenModel::refreshModel()
|
|||||||
delete m_rootItem;
|
delete m_rootItem;
|
||||||
m_loading = true;
|
m_loading = true;
|
||||||
Q_EMIT loadingChanged();
|
Q_EMIT loadingChanged();
|
||||||
m_rootItem = new SpaceTreeItem(nullptr, m_space->id(), m_space->displayName(), m_space->canonicalAlias());
|
m_rootItem =
|
||||||
|
new SpaceTreeItem(dynamic_cast<NeoChatConnection *>(m_space->connection()), nullptr, m_space->id(), m_space->displayName(), m_space->canonicalAlias());
|
||||||
endResetModel();
|
endResetModel();
|
||||||
auto job = m_space->connection()->callApi<Quotient::GetSpaceHierarchyJob>(m_space->id(), Quotient::none, Quotient::none, 1);
|
auto job = m_space->connection()->callApi<Quotient::GetSpaceHierarchyJob>(m_space->id(), Quotient::none, Quotient::none, 1);
|
||||||
m_currentJobs.append(job);
|
m_currentJobs.append(job);
|
||||||
@@ -112,7 +113,8 @@ void SpaceChildrenModel::insertChildren(std::vector<Quotient::GetSpaceHierarchyJ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
parentItem->insertChild(insertRow,
|
parentItem->insertChild(insertRow,
|
||||||
new SpaceTreeItem(parentItem,
|
new SpaceTreeItem(dynamic_cast<NeoChatConnection *>(m_space->connection()),
|
||||||
|
parentItem,
|
||||||
children[i].roomId,
|
children[i].roomId,
|
||||||
children[i].name,
|
children[i].name,
|
||||||
children[i].canonicalAlias,
|
children[i].canonicalAlias,
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
|
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
|
|
||||||
SpaceTreeItem::SpaceTreeItem(SpaceTreeItem *parent,
|
SpaceTreeItem::SpaceTreeItem(NeoChatConnection *connection,
|
||||||
|
SpaceTreeItem *parent,
|
||||||
const QString &id,
|
const QString &id,
|
||||||
const QString &name,
|
const QString &name,
|
||||||
const QString &canonicalAlias,
|
const QString &canonicalAlias,
|
||||||
@@ -15,7 +16,8 @@ SpaceTreeItem::SpaceTreeItem(SpaceTreeItem *parent,
|
|||||||
bool allowGuests,
|
bool allowGuests,
|
||||||
bool worldReadable,
|
bool worldReadable,
|
||||||
bool isSpace)
|
bool isSpace)
|
||||||
: m_parentItem(parent)
|
: m_connection(connection)
|
||||||
|
, m_parentItem(parent)
|
||||||
, m_id(id)
|
, m_id(id)
|
||||||
, m_name(name)
|
, m_name(name)
|
||||||
, m_canonicalAlias(canonicalAlias)
|
, m_canonicalAlias(canonicalAlias)
|
||||||
@@ -107,8 +109,7 @@ QUrl SpaceTreeItem::avatarUrl() const
|
|||||||
if (m_avatarUrl.isEmpty() || m_avatarUrl.scheme() != QLatin1String("mxc")) {
|
if (m_avatarUrl.isEmpty() || m_avatarUrl.scheme() != QLatin1String("mxc")) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
auto connection = Controller::instance().activeConnection();
|
auto url = m_connection->makeMediaUrl(m_avatarUrl);
|
||||||
auto url = connection->makeMediaUrl(m_avatarUrl);
|
|
||||||
if (url.scheme() == QLatin1String("mxc")) {
|
if (url.scheme() == QLatin1String("mxc")) {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
@@ -127,11 +128,10 @@ bool SpaceTreeItem::worldReadable() const
|
|||||||
|
|
||||||
bool SpaceTreeItem::isJoined() const
|
bool SpaceTreeItem::isJoined() const
|
||||||
{
|
{
|
||||||
auto connection = Controller::instance().activeConnection();
|
if (!m_connection) {
|
||||||
if (!connection) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return connection->room(id(), Quotient::JoinState::Join) != nullptr;
|
return m_connection->room(id(), Quotient::JoinState::Join) != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SpaceTreeItem::isSpace() const
|
bool SpaceTreeItem::isSpace() const
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include <Quotient/csapi/space_hierarchy.h>
|
#include <Quotient/csapi/space_hierarchy.h>
|
||||||
|
|
||||||
|
class NeoChatConnection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class SpaceTreeItem
|
* @class SpaceTreeItem
|
||||||
*
|
*
|
||||||
@@ -18,7 +20,8 @@
|
|||||||
class SpaceTreeItem
|
class SpaceTreeItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit SpaceTreeItem(SpaceTreeItem *parent = nullptr,
|
explicit SpaceTreeItem(NeoChatConnection *connection,
|
||||||
|
SpaceTreeItem *parent = nullptr,
|
||||||
const QString &id = {},
|
const QString &id = {},
|
||||||
const QString &name = {},
|
const QString &name = {},
|
||||||
const QString &canonicalAlias = {},
|
const QString &canonicalAlias = {},
|
||||||
@@ -121,6 +124,7 @@ public:
|
|||||||
bool isSpace() const;
|
bool isSpace() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
NeoChatConnection *m_connection;
|
||||||
QList<SpaceTreeItem *> m_children;
|
QList<SpaceTreeItem *> m_children;
|
||||||
SpaceTreeItem *m_parentItem;
|
SpaceTreeItem *m_parentItem;
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include <Quotient/csapi/content-repo.h>
|
#include <Quotient/csapi/content-repo.h>
|
||||||
#include <Quotient/csapi/profile.h>
|
#include <Quotient/csapi/profile.h>
|
||||||
|
#include <Quotient/database.h>
|
||||||
#include <Quotient/qt_connection_util.h>
|
#include <Quotient/qt_connection_util.h>
|
||||||
#include <Quotient/settings.h>
|
#include <Quotient/settings.h>
|
||||||
#include <Quotient/user.h>
|
#include <Quotient/user.h>
|
||||||
@@ -220,4 +221,35 @@ 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString NeoChatConnection::deviceKey() const
|
||||||
|
{
|
||||||
|
return edKeyForUserDevice(userId(), deviceId());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString NeoChatConnection::encryptionKey() const
|
||||||
|
{
|
||||||
|
auto query = database()->prepareQuery(QStringLiteral("SELECT curveKey FROM tracked_devices WHERE matrixId=:matrixId AND deviceid=:deviceId LIMIT 1;"));
|
||||||
|
query.bindValue(QStringLiteral(":matrixId"), userId());
|
||||||
|
query.bindValue(QStringLiteral(":deviceId"), deviceId());
|
||||||
|
database()->execute(query);
|
||||||
|
if (!query.next()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return query.value(0).toString();
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_neochatconnection.cpp"
|
#include "moc_neochatconnection.cpp"
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ class NeoChatConnection : public Quotient::Connection
|
|||||||
* Set to an empty string to remove the label.
|
* Set to an empty string to remove the label.
|
||||||
*/
|
*/
|
||||||
Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged)
|
Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged)
|
||||||
|
Q_PROPERTY(QString deviceKey READ deviceKey CONSTANT)
|
||||||
|
Q_PROPERTY(QString encryptionKey READ encryptionKey CONSTANT)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NeoChatConnection(QObject *parent = nullptr);
|
NeoChatConnection(QObject *parent = nullptr);
|
||||||
@@ -61,6 +63,16 @@ public:
|
|||||||
*/
|
*/
|
||||||
Q_INVOKABLE void createSpace(const QString &name, const QString &topic, const QString &parent = {}, bool setChildParent = false);
|
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);
|
||||||
|
|
||||||
|
QString deviceKey() const;
|
||||||
|
QString encryptionKey() const;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void labelChanged();
|
void labelChanged();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
|
|||||||
Q_EMIT canEncryptRoomChanged();
|
Q_EMIT canEncryptRoomChanged();
|
||||||
Q_EMIT parentIdsChanged();
|
Q_EMIT parentIdsChanged();
|
||||||
Q_EMIT canonicalParentChanged();
|
Q_EMIT canonicalParentChanged();
|
||||||
|
Q_EMIT joinRuleChanged();
|
||||||
});
|
});
|
||||||
connect(connection, &Connection::capabilitiesLoaded, this, &NeoChatRoom::maxRoomVersionChanged);
|
connect(connection, &Connection::capabilitiesLoaded, this, &NeoChatRoom::maxRoomVersionChanged);
|
||||||
connect(this, &Room::changed, this, [this]() {
|
connect(this, &Room::changed, this, [this]() {
|
||||||
@@ -418,30 +419,6 @@ QDateTime NeoChatRoom::lastActiveTime()
|
|||||||
return messageEvents().rbegin()->get()->originTimestamp();
|
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.
|
// An empty user is useful for returning as a model value to avoid properties being undefined.
|
||||||
static const QVariantMap emptyUser = {
|
static const QVariantMap emptyUser = {
|
||||||
{"isLocalUser"_ls, false},
|
{"isLocalUser"_ls, false},
|
||||||
@@ -736,16 +713,51 @@ QString NeoChatRoom::joinRule() const
|
|||||||
return joinRulesEvent->joinRule();
|
return joinRulesEvent->joinRule();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NeoChatRoom::setJoinRule(const QString &joinRule)
|
void NeoChatRoom::setJoinRule(const QString &joinRule, const QList<QString> &allowedSpaces)
|
||||||
{
|
{
|
||||||
if (!canSendState("m.room.join_rules"_ls)) {
|
if (!canSendState("m.room.join_rules"_ls)) {
|
||||||
qWarning() << "Power level too low to set join rules";
|
qWarning() << "Power level too low to set join rules";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setState("m.room.join_rules"_ls, {}, QJsonObject{{"join_rule"_ls, joinRule}});
|
auto actualRule = joinRule;
|
||||||
|
if (joinRule == "restricted"_ls && allowedSpaces.isEmpty()) {
|
||||||
|
actualRule = "private"_ls;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray allowConditions;
|
||||||
|
if (actualRule == "restricted"_ls) {
|
||||||
|
for (auto allowedSpace : allowedSpaces) {
|
||||||
|
allowConditions += QJsonObject{{"type"_ls, "m.room_membership"_ls}, {"room_id"_ls, allowedSpace}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject content;
|
||||||
|
content.insert("join_rule"_ls, joinRule);
|
||||||
|
if (!allowConditions.isEmpty()) {
|
||||||
|
content.insert("allow"_ls, allowConditions);
|
||||||
|
}
|
||||||
|
qWarning() << content;
|
||||||
|
setState("m.room.join_rules"_ls, {}, content);
|
||||||
// Not emitting joinRuleChanged() here, since that would override the change in the UI with the *current* value, which is not the *new* value.
|
// Not emitting joinRuleChanged() here, since that would override the change in the UI with the *current* value, which is not the *new* value.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<QString> NeoChatRoom::restrictedIds() const
|
||||||
|
{
|
||||||
|
auto joinRulesEvent = currentState().get<JoinRulesEvent>();
|
||||||
|
if (!joinRulesEvent) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (joinRulesEvent->joinRule() != "restricted"_ls) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QString> roomIds;
|
||||||
|
for (auto allow : joinRulesEvent->allow()) {
|
||||||
|
roomIds += allow.toObject().value("room_id"_ls).toString();
|
||||||
|
}
|
||||||
|
return roomIds;
|
||||||
|
}
|
||||||
|
|
||||||
QString NeoChatRoom::historyVisibility() const
|
QString NeoChatRoom::historyVisibility() const
|
||||||
{
|
{
|
||||||
return currentState().get("m.room.history_visibility"_ls)->contentJson()["history_visibility"_ls].toString();
|
return currentState().get("m.room.history_visibility"_ls)->contentJson()["history_visibility"_ls].toString();
|
||||||
@@ -1165,6 +1177,21 @@ QList<QString> NeoChatRoom::parentIds() const
|
|||||||
return parentIds;
|
return parentIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<NeoChatRoom *> NeoChatRoom::parentObjects(bool multiLevel) const
|
||||||
|
{
|
||||||
|
QList<NeoChatRoom *> parentObjects;
|
||||||
|
QList<QString> parentIds = this->parentIds();
|
||||||
|
for (const auto &parentId : parentIds) {
|
||||||
|
if (auto parentObject = static_cast<NeoChatRoom *>(connection()->room(parentId))) {
|
||||||
|
parentObjects += parentObject;
|
||||||
|
if (multiLevel) {
|
||||||
|
parentObjects += parentObject->parentObjects(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parentObjects;
|
||||||
|
}
|
||||||
|
|
||||||
QString NeoChatRoom::canonicalParent() const
|
QString NeoChatRoom::canonicalParent() const
|
||||||
{
|
{
|
||||||
auto parentEvents = currentState().eventsOfType("m.space.parent"_ls);
|
auto parentEvents = currentState().eventsOfType("m.space.parent"_ls);
|
||||||
@@ -1362,7 +1389,7 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
|||||||
for (const auto &i : roomRuleArray) {
|
for (const auto &i : roomRuleArray) {
|
||||||
QJsonObject roomRule = i.toObject();
|
QJsonObject roomRule = i.toObject();
|
||||||
if (roomRule["rule_id"_ls] == id()) {
|
if (roomRule["rule_id"_ls] == id()) {
|
||||||
Controller::instance().activeConnection()->callApi<DeletePushRuleJob>("global"_ls, "room"_ls, id());
|
connection()->callApi<DeletePushRuleJob>("global"_ls, "room"_ls, id());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1373,7 +1400,7 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
|||||||
for (const auto &i : overrideRuleArray) {
|
for (const auto &i : overrideRuleArray) {
|
||||||
QJsonObject overrideRule = i.toObject();
|
QJsonObject overrideRule = i.toObject();
|
||||||
if (overrideRule["rule_id"_ls] == id()) {
|
if (overrideRule["rule_id"_ls] == id()) {
|
||||||
Controller::instance().activeConnection()->callApi<DeletePushRuleJob>("global"_ls, "override"_ls, id());
|
connection()->callApi<DeletePushRuleJob>("global"_ls, "override"_ls, id());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1409,11 +1436,9 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
|||||||
const QList<PushCondition> conditions = {pushCondition};
|
const QList<PushCondition> conditions = {pushCondition};
|
||||||
|
|
||||||
// Add new override rule and make sure it's enabled
|
// Add new override rule and make sure it's enabled
|
||||||
auto job = Controller::instance()
|
auto job = connection()->callApi<SetPushRuleJob>("global"_ls, "override"_ls, id(), actions, QString(), QString(), conditions, QString());
|
||||||
.activeConnection()
|
|
||||||
->callApi<SetPushRuleJob>("global"_ls, "override"_ls, id(), actions, QString(), QString(), conditions, QString());
|
|
||||||
connect(job, &BaseJob::success, this, [this]() {
|
connect(job, &BaseJob::success, this, [this]() {
|
||||||
auto enableJob = Controller::instance().activeConnection()->callApi<SetPushRuleEnabledJob>("global"_ls, "override"_ls, id(), true);
|
auto enableJob = connection()->callApi<SetPushRuleEnabledJob>("global"_ls, "override"_ls, id(), true);
|
||||||
connect(enableJob, &BaseJob::success, this, [this]() {
|
connect(enableJob, &BaseJob::success, this, [this]() {
|
||||||
m_pushNotificationStateUpdating = false;
|
m_pushNotificationStateUpdating = false;
|
||||||
});
|
});
|
||||||
@@ -1437,11 +1462,9 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
|||||||
// No conditions for a room rule
|
// No conditions for a room rule
|
||||||
const QList<PushCondition> conditions;
|
const QList<PushCondition> conditions;
|
||||||
|
|
||||||
auto setJob = Controller::instance()
|
auto setJob = connection()->callApi<SetPushRuleJob>("global"_ls, "room"_ls, id(), actions, QString(), QString(), conditions, QString());
|
||||||
.activeConnection()
|
|
||||||
->callApi<SetPushRuleJob>("global"_ls, "room"_ls, id(), actions, QString(), QString(), conditions, QString());
|
|
||||||
connect(setJob, &BaseJob::success, this, [this]() {
|
connect(setJob, &BaseJob::success, this, [this]() {
|
||||||
auto enableJob = Controller::instance().activeConnection()->callApi<SetPushRuleEnabledJob>("global"_ls, "room"_ls, id(), true);
|
auto enableJob = connection()->callApi<SetPushRuleEnabledJob>("global"_ls, "room"_ls, id(), true);
|
||||||
connect(enableJob, &BaseJob::success, this, [this]() {
|
connect(enableJob, &BaseJob::success, this, [this]() {
|
||||||
m_pushNotificationStateUpdating = false;
|
m_pushNotificationStateUpdating = false;
|
||||||
});
|
});
|
||||||
@@ -1470,11 +1493,9 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
|||||||
const QList<PushCondition> conditions;
|
const QList<PushCondition> conditions;
|
||||||
|
|
||||||
// Add new room rule and make sure enabled
|
// Add new room rule and make sure enabled
|
||||||
auto setJob = Controller::instance()
|
auto setJob = connection()->callApi<SetPushRuleJob>("global"_ls, "room"_ls, id(), actions, QString(), QString(), conditions, QString());
|
||||||
.activeConnection()
|
|
||||||
->callApi<SetPushRuleJob>("global"_ls, "room"_ls, id(), actions, QString(), QString(), conditions, QString());
|
|
||||||
connect(setJob, &BaseJob::success, this, [this]() {
|
connect(setJob, &BaseJob::success, this, [this]() {
|
||||||
auto enableJob = Controller::instance().activeConnection()->callApi<SetPushRuleEnabledJob>("global"_ls, "room"_ls, id(), true);
|
auto enableJob = connection()->callApi<SetPushRuleEnabledJob>("global"_ls, "room"_ls, id(), true);
|
||||||
connect(enableJob, &BaseJob::success, this, [this]() {
|
connect(enableJob, &BaseJob::success, this, [this]() {
|
||||||
m_pushNotificationStateUpdating = false;
|
m_pushNotificationStateUpdating = false;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -160,6 +160,13 @@ class NeoChatRoom : public Quotient::Room
|
|||||||
*/
|
*/
|
||||||
Q_PROPERTY(QString joinRule READ joinRule WRITE setJoinRule NOTIFY joinRuleChanged)
|
Q_PROPERTY(QString joinRule READ joinRule WRITE setJoinRule NOTIFY joinRuleChanged)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The space IDs that members of can join this room.
|
||||||
|
*
|
||||||
|
* Empty if the join rule is not restricted.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(QList<QString> restrictedIds READ restrictedIds NOTIFY joinRuleChanged)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the maximum room version that the server supports.
|
* @brief Get the maximum room version that the server supports.
|
||||||
*
|
*
|
||||||
@@ -324,28 +331,6 @@ public:
|
|||||||
|
|
||||||
explicit NeoChatRoom(Quotient::Connection *connection, QString roomId, Quotient::JoinState joinState = {});
|
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.
|
* @brief Get a user in the context of this room.
|
||||||
*
|
*
|
||||||
@@ -527,6 +512,17 @@ public:
|
|||||||
|
|
||||||
QList<QString> parentIds() const;
|
QList<QString> parentIds() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get a list of parent space objects for this room.
|
||||||
|
*
|
||||||
|
* Will only return retrun spaces that are know, i.e. the user has joined and
|
||||||
|
* a valid NeoChatRoom is available.
|
||||||
|
*
|
||||||
|
* @param multiLevel whether the function should recursively gather all levels
|
||||||
|
* of parents
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE QList<NeoChatRoom *> parentObjects(bool multiLevel = false) const;
|
||||||
|
|
||||||
QString canonicalParent() const;
|
QString canonicalParent() const;
|
||||||
void setCanonicalParent(const QString &parentId);
|
void setCanonicalParent(const QString &parentId);
|
||||||
|
|
||||||
@@ -581,7 +577,23 @@ public:
|
|||||||
Q_INVOKABLE void clearInvitationNotification();
|
Q_INVOKABLE void clearInvitationNotification();
|
||||||
|
|
||||||
[[nodiscard]] QString joinRule() const;
|
[[nodiscard]] QString joinRule() const;
|
||||||
void setJoinRule(const QString &joinRule);
|
|
||||||
|
/**
|
||||||
|
* @brief Set the join rule for the room.
|
||||||
|
*
|
||||||
|
* Will fail if the user doesn't have the required privileges.
|
||||||
|
*
|
||||||
|
* @param joinRule the join rule [public, knock, invite, private, restricted].
|
||||||
|
* @param allowedSpaces only used when the join rule is restricted. This is a
|
||||||
|
* list of space Matrix IDs that members of can join without an invite.
|
||||||
|
* If the rule is restricted and this list is empty it is treated as a join
|
||||||
|
* rule of private instead.
|
||||||
|
*
|
||||||
|
* @sa https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void setJoinRule(const QString &joinRule, const QList<QString> &allowedSpaces = {});
|
||||||
|
|
||||||
|
QList<QString> restrictedIds() const;
|
||||||
|
|
||||||
int maxRoomVersion() const;
|
int maxRoomVersion() const;
|
||||||
|
|
||||||
|
|||||||
@@ -204,15 +204,15 @@ void NotificationsManager::postNotification(NeoChatRoom *room,
|
|||||||
notification->setText(notification->text() + QLatin1Char('\n') + entry);
|
notification->setText(notification->text() + QLatin1Char('\n') + entry);
|
||||||
notification->setPixmap(createNotificationImage(icon, room));
|
notification->setPixmap(createNotificationImage(icon, room));
|
||||||
|
|
||||||
notification->setDefaultAction(i18n("Open NeoChat in this room"));
|
auto defaultAction = notification->addDefaultAction(i18n("Open NeoChat in this room"));
|
||||||
connect(notification, &KNotification::defaultActivated, this, [notification, room]() {
|
connect(defaultAction, &KNotificationAction::activated, this, [notification, room]() {
|
||||||
WindowController::instance().showAndRaiseWindow(notification->xdgActivationToken());
|
WindowController::instance().showAndRaiseWindow(notification->xdgActivationToken());
|
||||||
if (!room) {
|
if (!room) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (room->localUser()->id() != Controller::instance().activeConnection()->userId()) {
|
auto connection = dynamic_cast<NeoChatConnection *>(Controller::instance().accounts().get(room->localUser()->id()));
|
||||||
Controller::instance().setActiveConnection(dynamic_cast<NeoChatConnection *>(Controller::instance().accounts().get(room->localUser()->id())));
|
Controller::instance().setActiveConnection(connection);
|
||||||
}
|
RoomManager::instance().setConnection(connection);
|
||||||
RoomManager::instance().enterRoom(room);
|
RoomManager::instance().enterRoom(room);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -240,28 +240,31 @@ void NotificationsManager::postInviteNotification(NeoChatRoom *room, const QStri
|
|||||||
notification->setTitle(title);
|
notification->setTitle(title);
|
||||||
notification->setPixmap(createNotificationImage(icon, nullptr));
|
notification->setPixmap(createNotificationImage(icon, nullptr));
|
||||||
notification->setFlags(KNotification::Persistent);
|
notification->setFlags(KNotification::Persistent);
|
||||||
notification->setDefaultAction(i18n("Open this invitation in NeoChat"));
|
auto defaultAction = notification->addDefaultAction(i18n("Open this invitation in NeoChat"));
|
||||||
connect(notification, &KNotification::defaultActivated, this, [notification, room]() {
|
connect(defaultAction, &KNotificationAction::activated, this, [notification, room]() {
|
||||||
WindowController::instance().showAndRaiseWindow(notification->xdgActivationToken());
|
WindowController::instance().showAndRaiseWindow(notification->xdgActivationToken());
|
||||||
notification->close();
|
notification->close();
|
||||||
RoomManager::instance().enterRoom(room);
|
RoomManager::instance().enterRoom(room);
|
||||||
});
|
});
|
||||||
notification->setActions({i18nc("@action:button The thing being accepted is an invitation to chat", "Accept"), i18nc("@action:button The thing being rejected is an invitation to chat", "Reject"), i18nc("@action:button The thing being rejected is an invitation to chat", "Reject and Ignore User")});
|
|
||||||
connect(notification, &KNotification::action1Activated, this, [room, notification]() {
|
const auto acceptAction = notification->addAction(i18nc("@action:button The thing being accepted is an invitation to chat", "Accept"));
|
||||||
|
const auto rejectAction = notification->addAction(i18nc("@action:button The thing being rejected is an invitation to chat", "Reject"));
|
||||||
|
const auto rejectAndIgnoreAction = notification->addAction(i18nc("@action:button The thing being rejected is an invitation to chat", "Reject and Ignore User"));
|
||||||
|
connect(acceptAction, &KNotificationAction::activated, this, [room, notification]() {
|
||||||
if (!room) {
|
if (!room) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
room->acceptInvitation();
|
room->acceptInvitation();
|
||||||
notification->close();
|
notification->close();
|
||||||
});
|
});
|
||||||
connect(notification, &KNotification::action2Activated, this, [room, notification]() {
|
connect(rejectAction, &KNotificationAction::activated, this, [room, notification]() {
|
||||||
if (!room) {
|
if (!room) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
RoomManager::instance().leaveRoom(room);
|
RoomManager::instance().leaveRoom(room);
|
||||||
notification->close();
|
notification->close();
|
||||||
});
|
});
|
||||||
connect(notification, &KNotification::action3Activated, this, [room, notification]() {
|
connect(rejectAndIgnoreAction, &KNotificationAction::activated, this, [room, notification]() {
|
||||||
if (!room) {
|
if (!room) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ FormCard.FormCardPage {
|
|||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: colorSchemeDelegate
|
id: colorSchemeDelegate
|
||||||
visible: item !== null && Qt.platform.os !== "android"
|
visible: item !== null
|
||||||
source: "qrc:/org/kde/neochat/qml/ColorScheme.qml"
|
source: "qrc:/org/kde/neochat/qml/ColorScheme.qml"
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
@@ -235,7 +235,7 @@ FormCard.FormCardPage {
|
|||||||
|
|
||||||
FormCard.FormCheckDelegate {
|
FormCard.FormCheckDelegate {
|
||||||
id: hasWindowSystemDelegate
|
id: hasWindowSystemDelegate
|
||||||
visible: Controller.hasWindowSystem
|
visible: WindowController.hasWindowSystem
|
||||||
text: i18n("Use transparent chat page")
|
text: i18n("Use transparent chat page")
|
||||||
enabled: !Config.compactLayout && !Config.isBlurImmutable
|
enabled: !Config.compactLayout && !Config.isBlurImmutable
|
||||||
checked: Config.blur
|
checked: Config.blur
|
||||||
@@ -249,7 +249,7 @@ FormCard.FormCardPage {
|
|||||||
|
|
||||||
FormCard.AbstractFormDelegate {
|
FormCard.AbstractFormDelegate {
|
||||||
id: transparencyDelegate
|
id: transparencyDelegate
|
||||||
visible: Controller.hasWindowSystem && Config.blur
|
visible: WindowController.hasWindowSystem && Config.blur
|
||||||
enabled: !Config.isTransparancyImmutable
|
enabled: !Config.isTransparancyImmutable
|
||||||
background: Item {}
|
background: Item {}
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import QtQuick.Controls as QQC2
|
|||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtMultimedia
|
import QtMultimedia
|
||||||
|
|
||||||
|
import org.kde.coreaddons
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
|
|
||||||
import org.kde.neochat
|
import org.kde.neochat
|
||||||
@@ -127,7 +128,7 @@ MessageDelegate {
|
|||||||
QQC2.Label {
|
QQC2.Label {
|
||||||
visible: root.contentMaxWidth > Kirigami.Units.gridUnit * 12
|
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 {
|
QQC2.Label {
|
||||||
@@ -135,7 +136,7 @@ MessageDelegate {
|
|||||||
Layout.rightMargin: Kirigami.Units.smallSpacing
|
Layout.rightMargin: Kirigami.Units.smallSpacing
|
||||||
visible: audio.hasAudio && root.contentMaxWidth < Kirigami.Units.gridUnit * 12
|
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"
|
actionName: "security"
|
||||||
text: i18n("Security")
|
text: i18n("Security")
|
||||||
icon.name: "security-low"
|
icon.name: "security-low"
|
||||||
page: Qt.resolvedUrl("Security.qml")
|
page: Qt.resolvedUrl("RoomSecurity.qml")
|
||||||
initialProperties: {
|
initialProperties: {
|
||||||
return {
|
return {
|
||||||
room: root.room
|
room: root.room
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ Kirigami.ScrollablePage {
|
|||||||
delegate: RoomDelegate {
|
delegate: RoomDelegate {
|
||||||
id: roomDelegate
|
id: roomDelegate
|
||||||
filterText: ""
|
filterText: ""
|
||||||
onClicked: {
|
onSelected: {
|
||||||
root.chosen(roomDelegate.currentRoom.id)
|
root.chosen(roomDelegate.currentRoom.id)
|
||||||
}
|
}
|
||||||
connection: root.connection
|
connection: root.connection
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ FormCard.FormCardPage {
|
|||||||
|
|
||||||
title: i18n("Devices")
|
title: i18n("Devices")
|
||||||
|
|
||||||
|
background: Kirigami.PlaceholderMessage {
|
||||||
|
text: i18n("Loading…")
|
||||||
|
visible: !thisDeviceCard.visible
|
||||||
|
}
|
||||||
|
|
||||||
required property NeoChatConnection connection
|
required property NeoChatConnection connection
|
||||||
|
|
||||||
property DevicesModel devicesModel: DevicesModel {
|
property DevicesModel devicesModel: DevicesModel {
|
||||||
@@ -24,6 +29,7 @@ FormCard.FormCardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DevicesCard {
|
DevicesCard {
|
||||||
|
id: thisDeviceCard
|
||||||
title: i18n("This Device")
|
title: i18n("This Device")
|
||||||
type: DevicesModel.This
|
type: DevicesModel.This
|
||||||
showVerifyButton: false
|
showVerifyButton: false
|
||||||
@@ -59,26 +65,33 @@ FormCard.FormCardPage {
|
|||||||
visible: !root.connection
|
visible: !root.connection
|
||||||
}
|
}
|
||||||
|
|
||||||
property Kirigami.OverlaySheet passwordSheet: Kirigami.OverlaySheet {
|
property Kirigami.Dialog passwordSheet: Kirigami.Dialog {
|
||||||
id: passwordSheet
|
id: passwordSheet
|
||||||
|
|
||||||
property string deviceId
|
property string deviceId
|
||||||
|
|
||||||
|
preferredWidth: Kirigami.Units.gridUnit * 24
|
||||||
|
|
||||||
title: i18n("Remove device")
|
title: i18n("Remove device")
|
||||||
Kirigami.FormLayout {
|
|
||||||
QQC2.TextField {
|
standardButtons: QQC2.Dialog.Cancel
|
||||||
|
FormCard.FormCard {
|
||||||
|
FormCard.FormTextFieldDelegate {
|
||||||
id: passwordField
|
id: passwordField
|
||||||
Kirigami.FormData.label: i18n("Password:")
|
label: i18n("Password:")
|
||||||
echoMode: TextInput.Password
|
echoMode: TextInput.Password
|
||||||
}
|
}
|
||||||
QQC2.Button {
|
}
|
||||||
text: i18n("Confirm")
|
customFooterActions: [
|
||||||
onClicked: {
|
Kirigami.Action {
|
||||||
|
text: i18nc("As in 'Remove this device'", "Remove")
|
||||||
|
icon.name: "delete"
|
||||||
|
onTriggered: {
|
||||||
devicesModel.logout(passwordSheet.deviceId, passwordField.text)
|
devicesModel.logout(passwordSheet.deviceId, passwordField.text)
|
||||||
passwordField.text = ""
|
passwordField.text = ""
|
||||||
passwordSheet.close()
|
passwordSheet.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,13 @@ import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
|||||||
import org.kde.neochat
|
import org.kde.neochat
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The current room that user is viewing.
|
||||||
|
*/
|
||||||
|
required property NeoChatRoom room
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
spacing: 0
|
spacing: 0
|
||||||
@@ -26,15 +33,15 @@ ColumnLayout {
|
|||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
RoomManager.visitUser(room.getUser(room.directChatRemoteUser.id).object, "mention")
|
RoomManager.visitUser(root.room.getUser(root.room.directChatRemoteUser.id).object, "mention")
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: KirigamiComponents.Avatar {
|
contentItem: KirigamiComponents.Avatar {
|
||||||
name: room ? room.displayName : ""
|
name: root.room ? root.room.displayName : ""
|
||||||
source: room ? ("image://mxc/" + room.avatarMediaId) : ""
|
source: root.room ? ("image://mxc/" + root.room.avatarMediaId) : ""
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
visible: room.usesEncryption
|
visible: root.room.usesEncryption
|
||||||
color: Kirigami.Theme.backgroundColor
|
color: Kirigami.Theme.backgroundColor
|
||||||
|
|
||||||
width: Kirigami.Units.gridUnit
|
width: Kirigami.Units.gridUnit
|
||||||
@@ -56,7 +63,7 @@ ColumnLayout {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
type: Kirigami.Heading.Type.Primary
|
type: Kirigami.Heading.Type.Primary
|
||||||
wrapMode: QQC2.Label.Wrap
|
wrapMode: QQC2.Label.Wrap
|
||||||
text: room.displayName
|
text: root.room.displayName
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|||||||
159
src/qml/ExploreComponentMobile.qml
Normal file
159
src/qml/ExploreComponentMobile.qml
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls as QQC2
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import org.kde.kirigami as Kirigami
|
||||||
|
import org.kde.kirigamiaddons.delegates as Delegates
|
||||||
|
|
||||||
|
import org.kde.neochat
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The connection for the current user.
|
||||||
|
*/
|
||||||
|
required property NeoChatConnection connection
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Emitted when the text is changed in the search field.
|
||||||
|
*/
|
||||||
|
signal textChanged(string newText)
|
||||||
|
|
||||||
|
Kirigami.Separator {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
Kirigami.NavigationTabBar {
|
||||||
|
id: exploreTabBar
|
||||||
|
Layout.fillWidth: true
|
||||||
|
actions: [
|
||||||
|
Kirigami.Action {
|
||||||
|
id: infoAction
|
||||||
|
text: i18n("Search")
|
||||||
|
icon.name: "search"
|
||||||
|
onTriggered: {
|
||||||
|
if (explorePopup.visible && explorePopupLoader.sourceComponent == search) {
|
||||||
|
explorePopup.close();
|
||||||
|
exploreTabBar.currentIndex = -1;
|
||||||
|
} else if (explorePopup.visible && explorePopupLoader.sourceComponent != search) {
|
||||||
|
explorePopup.close();
|
||||||
|
explorePopup.open();
|
||||||
|
} else {
|
||||||
|
explorePopup.open();
|
||||||
|
}
|
||||||
|
explorePopupLoader.sourceComponent = search;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
text: i18n("Explore rooms")
|
||||||
|
icon.name: "compass"
|
||||||
|
onTriggered: {
|
||||||
|
let dialog = pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/JoinRoomPage.qml", {connection: root.connection}, {title: i18nc("@title", "Explore Rooms")})
|
||||||
|
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
|
||||||
|
if (isJoined) {
|
||||||
|
RoomManager.enterRoom(root.connection.room(roomId));
|
||||||
|
} else {
|
||||||
|
Controller.joinRoom(roomId.length > 0 ? roomId : alias);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
exploreTabBar.currentIndex = -1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
text: i18n("Start a Chat")
|
||||||
|
icon.name: "list-add-user"
|
||||||
|
onTriggered: {
|
||||||
|
pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/StartChatPage.qml", {connection: root.connection}, {title: i18nc("@title", "Start a Chat")})
|
||||||
|
exploreTabBar.currentIndex = -1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
text: i18n("Create New")
|
||||||
|
icon.name: "list-add"
|
||||||
|
onTriggered: {
|
||||||
|
if (explorePopup.visible && explorePopupLoader.sourceComponent == create) {
|
||||||
|
explorePopup.close();
|
||||||
|
exploreTabBar.currentIndex = -1;
|
||||||
|
} else if (explorePopup.visible && explorePopupLoader.sourceComponent != create) {
|
||||||
|
explorePopup.close();
|
||||||
|
explorePopup.open();
|
||||||
|
} else {
|
||||||
|
explorePopup.open();
|
||||||
|
}
|
||||||
|
explorePopupLoader.sourceComponent = create;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.Popup {
|
||||||
|
id: explorePopup
|
||||||
|
parent: root
|
||||||
|
|
||||||
|
y: -height + 1
|
||||||
|
width: root.width
|
||||||
|
leftPadding: Kirigami.Units.largeSpacing
|
||||||
|
rightPadding: Kirigami.Units.largeSpacing
|
||||||
|
bottomPadding: Kirigami.Units.largeSpacing
|
||||||
|
topPadding: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
|
closePolicy: QQC2.Popup.CloseOnEscape
|
||||||
|
|
||||||
|
contentItem: Loader {
|
||||||
|
id: explorePopupLoader
|
||||||
|
sourceComponent: search
|
||||||
|
}
|
||||||
|
|
||||||
|
background: ColumnLayout {
|
||||||
|
spacing: 0
|
||||||
|
Kirigami.Separator {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
color: Kirigami.Theme.backgroundColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: search
|
||||||
|
Kirigami.SearchField {
|
||||||
|
onTextChanged: root.textChanged(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Component {
|
||||||
|
id: create
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 0
|
||||||
|
Delegates.RoundedItemDelegate {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
action: Kirigami.Action {
|
||||||
|
text: i18n("Create a Room")
|
||||||
|
icon.name: "system-users-symbolic"
|
||||||
|
onTriggered: {
|
||||||
|
pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/CreateRoomDialog.qml", {connection: root.connection}, {title: i18nc("@title", "Create a Room")})
|
||||||
|
explorePopup.close()
|
||||||
|
}
|
||||||
|
shortcut: StandardKey.New
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Delegates.RoundedItemDelegate {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
action: Kirigami.Action {
|
||||||
|
text: i18n("Create a Space")
|
||||||
|
icon.name: "list-add"
|
||||||
|
onTriggered: {
|
||||||
|
pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/CreateRoomDialog.qml", {connection: root.connection, isSpace: true, title: i18nc("@title", "Create a Space")}, {title: i18nc("@title", "Create a Space")})
|
||||||
|
explorePopup.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import QtQuick.Controls as QQC2
|
|||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Qt.labs.platform
|
import Qt.labs.platform
|
||||||
|
|
||||||
|
import org.kde.coreaddons
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
|
|
||||||
import org.kde.neochat
|
import org.kde.neochat
|
||||||
@@ -104,7 +105,7 @@ MessageDelegate {
|
|||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: sizeLabel
|
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 {
|
PropertyChanges {
|
||||||
target: downloadButton
|
target: downloadButton
|
||||||
@@ -140,7 +141,7 @@ MessageDelegate {
|
|||||||
QQC2.Label {
|
QQC2.Label {
|
||||||
id: sizeLabel
|
id: sizeLabel
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: Controller.formatByteSize(root.mediaInfo.size)
|
text: Format.formatByteSize(root.mediaInfo.size)
|
||||||
opacity: 0.7
|
opacity: 0.7
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
maximumLineCount: 1
|
maximumLineCount: 1
|
||||||
|
|||||||
@@ -14,17 +14,23 @@ import org.kde.neochat
|
|||||||
FormCard.FormCardPage {
|
FormCard.FormCardPage {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
required property NeoChatConnection connection
|
||||||
|
|
||||||
title: i18nc("@title:window", "Notifications")
|
title: i18nc("@title:window", "Notifications")
|
||||||
|
|
||||||
|
property PushRuleModel pushRuleModel: PushRuleModel {
|
||||||
|
connection: root.connection
|
||||||
|
}
|
||||||
|
|
||||||
FormCard.FormCard {
|
FormCard.FormCard {
|
||||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||||
FormCard.FormCheckDelegate {
|
FormCard.FormCheckDelegate {
|
||||||
text: i18n("Enable notifications for this account")
|
text: i18n("Enable notifications for this account")
|
||||||
description: i18n("Whether push notifications are generated by your Matrix server")
|
description: i18n("Whether push notifications are generated by your Matrix server")
|
||||||
checked: Controller.pushRuleModel.globalNotificationsEnabled
|
checked: root.pushRuleModel.globalNotificationsEnabled
|
||||||
enabled: Controller.pushRuleModel.globalNotificationsSet
|
enabled: root.pushRuleModel.globalNotificationsSet
|
||||||
onToggled: {
|
onToggled: {
|
||||||
Controller.pushRuleModel.globalNotificationsEnabled = checked
|
root.pushRuleModel.globalNotificationsEnabled = checked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -35,7 +41,7 @@ FormCard.FormCardPage {
|
|||||||
FormCard.FormCard {
|
FormCard.FormCard {
|
||||||
Repeater {
|
Repeater {
|
||||||
model: KSortFilterProxyModel {
|
model: KSortFilterProxyModel {
|
||||||
sourceModel: Controller.pushRuleModel
|
sourceModel: root.pushRuleModel
|
||||||
filterRowCallback: function(source_row, source_parent) {
|
filterRowCallback: function(source_row, source_parent) {
|
||||||
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole)
|
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole)
|
||||||
return sectionRole == PushNotificationSection.Room;
|
return sectionRole == PushNotificationSection.Room;
|
||||||
@@ -52,7 +58,7 @@ FormCard.FormCardPage {
|
|||||||
FormCard.FormCard {
|
FormCard.FormCard {
|
||||||
Repeater {
|
Repeater {
|
||||||
model: KSortFilterProxyModel {
|
model: KSortFilterProxyModel {
|
||||||
sourceModel: Controller.pushRuleModel
|
sourceModel: root.pushRuleModel
|
||||||
filterRowCallback: function(source_row, source_parent) {
|
filterRowCallback: function(source_row, source_parent) {
|
||||||
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole)
|
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole)
|
||||||
return sectionRole == PushNotificationSection.Mentions;
|
return sectionRole == PushNotificationSection.Mentions;
|
||||||
@@ -69,7 +75,7 @@ FormCard.FormCardPage {
|
|||||||
FormCard.FormCard {
|
FormCard.FormCard {
|
||||||
Repeater {
|
Repeater {
|
||||||
model: KSortFilterProxyModel {
|
model: KSortFilterProxyModel {
|
||||||
sourceModel: Controller.pushRuleModel
|
sourceModel: root.pushRuleModel
|
||||||
|
|
||||||
filterRowCallback: function(source_row, source_parent) {
|
filterRowCallback: function(source_row, source_parent) {
|
||||||
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole)
|
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole)
|
||||||
@@ -100,7 +106,7 @@ FormCard.FormCardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
Controller.pushRuleModel.addKeyword(keywordAddField.text)
|
root.pushRuleModel.addKeyword(keywordAddField.text)
|
||||||
keywordAddField.text = ""
|
keywordAddField.text = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,7 +120,7 @@ FormCard.FormCardPage {
|
|||||||
enabled: NotificationsManager.keywordNotificationAction !== PushNotificationAction.Unknown
|
enabled: NotificationsManager.keywordNotificationAction !== PushNotificationAction.Unknown
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Controller.pushRuleModel.addKeyword(keywordAddField.text)
|
root.pushRuleModel.addKeyword(keywordAddField.text)
|
||||||
keywordAddField.text = ""
|
keywordAddField.text = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +139,7 @@ FormCard.FormCardPage {
|
|||||||
FormCard.FormCard {
|
FormCard.FormCard {
|
||||||
Repeater {
|
Repeater {
|
||||||
model: KSortFilterProxyModel {
|
model: KSortFilterProxyModel {
|
||||||
sourceModel: Controller.pushRuleModel
|
sourceModel: root.pushRuleModel
|
||||||
filterRowCallback: function(source_row, source_parent) {
|
filterRowCallback: function(source_row, source_parent) {
|
||||||
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole)
|
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole)
|
||||||
return sectionRole == PushNotificationSection.Invites;
|
return sectionRole == PushNotificationSection.Invites;
|
||||||
@@ -154,7 +160,7 @@ FormCard.FormCardPage {
|
|||||||
Repeater {
|
Repeater {
|
||||||
model: KSortFilterProxyModel {
|
model: KSortFilterProxyModel {
|
||||||
id: unknownModel
|
id: unknownModel
|
||||||
sourceModel: Controller.pushRuleModel
|
sourceModel: root.pushRuleModel
|
||||||
filterRowCallback: function(source_row, source_parent) {
|
filterRowCallback: function(source_row, source_parent) {
|
||||||
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole)
|
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole)
|
||||||
return sectionRole == PushNotificationSection.Unknown;
|
return sectionRole == PushNotificationSection.Unknown;
|
||||||
@@ -169,9 +175,9 @@ FormCard.FormCardPage {
|
|||||||
id: ruleDelegate
|
id: ruleDelegate
|
||||||
NotificationRuleItem {
|
NotificationRuleItem {
|
||||||
onDeleteRule: {
|
onDeleteRule: {
|
||||||
Controller.pushRuleModel.removeKeyword(id)
|
root.pushRuleModel.removeKeyword(id)
|
||||||
}
|
}
|
||||||
onActionChanged: (action) => Controller.pushRuleModel.setPushRuleAction(id, action)
|
onActionChanged: (action) => root.pushRuleModel.setPushRuleAction(id, action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,13 +18,13 @@ Kirigami.ScrollablePage {
|
|||||||
|
|
||||||
title: i18n("Invite a User")
|
title: i18n("Invite a User")
|
||||||
|
|
||||||
actions {
|
actions: [
|
||||||
main: Kirigami.Action {
|
Kirigami.Action {
|
||||||
icon.name: "dialog-close"
|
icon.name: "dialog-close"
|
||||||
text: i18nc("@action", "Cancel")
|
text: i18nc("@action", "Cancel")
|
||||||
onTriggered: applicationWindow().pageStack.layers.pop()
|
onTriggered: root.closeDialog()
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
header: RowLayout {
|
header: RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.margins: Kirigami.Units.largeSpacing
|
Layout.margins: Kirigami.Units.largeSpacing
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ Kirigami.ScrollablePage {
|
|||||||
valueRole: "url"
|
valueRole: "url"
|
||||||
model: ServerListModel {
|
model: ServerListModel {
|
||||||
id: serverListModel
|
id: serverListModel
|
||||||
|
connection: root.connection
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: Delegates.RoundedItemDelegate {
|
delegate: Delegates.RoundedItemDelegate {
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import org.kde.kirigami as Kirigami
|
|
||||||
|
|
||||||
Kirigami.Page {
|
|
||||||
title: i18n("Loading…")
|
|
||||||
Kirigami.LoadingPlaceholder {
|
|
||||||
id: loadingIndicator
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -34,7 +34,7 @@ Components.AbstractMaximizeComponent {
|
|||||||
]
|
]
|
||||||
|
|
||||||
content: MapView {
|
content: MapView {
|
||||||
id: map
|
id: mapView
|
||||||
map.plugin: Plugin {
|
map.plugin: Plugin {
|
||||||
name: "osm"
|
name: "osm"
|
||||||
PluginParameter {
|
PluginParameter {
|
||||||
@@ -50,7 +50,7 @@ Components.AbstractMaximizeComponent {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.location = map.toCoordinate(Qt.point(mouseX, mouseY), false)
|
root.location = mapView.map.toCoordinate(Qt.point(mouseX, mouseY), false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -129,7 +129,6 @@ QQC2.TextArea {
|
|||||||
|
|
||||||
ChatDocumentHandler {
|
ChatDocumentHandler {
|
||||||
id: documentHandler
|
id: documentHandler
|
||||||
isEdit: true
|
|
||||||
document: root.textDocument
|
document: root.textDocument
|
||||||
cursorPosition: root.cursorPosition
|
cursorPosition: root.cursorPosition
|
||||||
selectionStart: root.selectionStart
|
selectionStart: root.selectionStart
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ FormCard.FormCardPage {
|
|||||||
|
|
||||||
property NeoChatRoom room
|
property NeoChatRoom room
|
||||||
|
|
||||||
|
property PushRuleModel pushRuleModel: PushRuleModel {
|
||||||
|
connection: root.room.connection
|
||||||
|
}
|
||||||
|
|
||||||
title: i18nc('@title:window', 'Notifications')
|
title: i18nc('@title:window', 'Notifications')
|
||||||
|
|
||||||
FormCard.FormHeader {
|
FormCard.FormHeader {
|
||||||
@@ -63,7 +67,7 @@ FormCard.FormCardPage {
|
|||||||
FormCard.FormCard {
|
FormCard.FormCard {
|
||||||
Repeater {
|
Repeater {
|
||||||
model: KSortFilterProxyModel {
|
model: KSortFilterProxyModel {
|
||||||
sourceModel: Controller.pushRuleModel
|
sourceModel: root.pushRuleModel
|
||||||
|
|
||||||
filterRowCallback: function(source_row, source_parent) {
|
filterRowCallback: function(source_row, source_parent) {
|
||||||
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole)
|
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole)
|
||||||
@@ -78,9 +82,9 @@ FormCard.FormCardPage {
|
|||||||
id: ruleDelegate
|
id: ruleDelegate
|
||||||
NotificationRuleItem {
|
NotificationRuleItem {
|
||||||
onDeleteRule: {
|
onDeleteRule: {
|
||||||
Controller.pushRuleModel.removeKeyword(id)
|
root.pushRuleModel.removeKeyword(id)
|
||||||
}
|
}
|
||||||
onActionChanged: (action) => Controller.pushRuleModel.setPushRuleAction(id, action)
|
onActionChanged: (action) => root.pushRuleModel.setPushRuleAction(id, action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,7 +109,7 @@ FormCard.FormCardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
Controller.pushRuleModel.addKeyword(keywordAddField.text, root.room.id)
|
root.pushRuleModel.addKeyword(keywordAddField.text, root.room.id)
|
||||||
keywordAddField.text = ""
|
keywordAddField.text = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,7 +123,7 @@ FormCard.FormCardPage {
|
|||||||
enabled: NotificationsManager.keywordNotificationAction !== PushNotificationAction.Unknown && keywordAddField.text.length > 0
|
enabled: NotificationsManager.keywordNotificationAction !== PushNotificationAction.Unknown && keywordAddField.text.length > 0
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Controller.pushRuleModel.addKeyword(keywordAddField.text, root.room.id)
|
root.pushRuleModel.addKeyword(keywordAddField.text, root.room.id)
|
||||||
keywordAddField.text = ""
|
keywordAddField.text = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
50
src/qml/QrCodeMaximizeComponent.qml
Normal file
50
src/qml/QrCodeMaximizeComponent.qml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||||
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls as QQC2
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import org.kde.kirigamiaddons.labs.components as Components
|
||||||
|
import org.kde.kirigami as Kirigami
|
||||||
|
import org.kde.prison
|
||||||
|
|
||||||
|
Components.AbstractMaximizeComponent {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property string text
|
||||||
|
required property color avatarColor
|
||||||
|
required property string avatarSource
|
||||||
|
|
||||||
|
Shortcut {
|
||||||
|
sequences: [StandardKey.Cancel]
|
||||||
|
onActivated: root.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
leading: Components.Avatar {
|
||||||
|
id: userAvatar
|
||||||
|
implicitWidth: Kirigami.Units.iconSizes.medium
|
||||||
|
implicitHeight: Kirigami.Units.iconSizes.medium
|
||||||
|
|
||||||
|
name: root.title
|
||||||
|
source: root.avatarSource
|
||||||
|
color: root.avatarColor
|
||||||
|
}
|
||||||
|
|
||||||
|
content: Item {
|
||||||
|
Keys.onEscapePressed: root.close()
|
||||||
|
Barcode {
|
||||||
|
barcodeType: Barcode.QRCode
|
||||||
|
content: root.text
|
||||||
|
height: Math.min(parent.height, Kirigami.Units.gridUnit * 20)
|
||||||
|
width: height
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
id: closeArea
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
|
onClicked: root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -94,7 +94,7 @@ QQC2.Dialog {
|
|||||||
|
|
||||||
connection: root.connection
|
connection: root.connection
|
||||||
|
|
||||||
onClicked: {
|
onSelected: {
|
||||||
RoomManager.enterRoom(currentRoom);
|
RoomManager.enterRoom(currentRoom);
|
||||||
root.close()
|
root.close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import QtQuick
|
|||||||
import QtQuick.Controls as QQC2
|
import QtQuick.Controls as QQC2
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import org.kde.coreaddons
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||||
|
|
||||||
@@ -77,15 +78,10 @@ Item {
|
|||||||
implicitWidth: mainLayout.implicitWidth
|
implicitWidth: mainLayout.implicitWidth
|
||||||
implicitHeight: mainLayout.implicitHeight
|
implicitHeight: mainLayout.implicitHeight
|
||||||
|
|
||||||
GridLayout {
|
RowLayout {
|
||||||
id: mainLayout
|
id: mainLayout
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
implicitHeight: Math.max(replyAvatar.implicitHeight, replyName.implicitHeight) + loader.height
|
spacing: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
rows: 2
|
|
||||||
columns: 3
|
|
||||||
rowSpacing: Kirigami.Units.smallSpacing
|
|
||||||
columnSpacing: Kirigami.Units.largeSpacing
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: verticalBorder
|
id: verticalBorder
|
||||||
@@ -96,47 +92,55 @@ Item {
|
|||||||
implicitWidth: Kirigami.Units.smallSpacing
|
implicitWidth: Kirigami.Units.smallSpacing
|
||||||
color: root.author.color
|
color: root.author.color
|
||||||
}
|
}
|
||||||
KirigamiComponents.Avatar {
|
ColumnLayout {
|
||||||
id: replyAvatar
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
implicitWidth: Kirigami.Units.iconSizes.small
|
RowLayout {
|
||||||
implicitHeight: Kirigami.Units.iconSizes.small
|
spacing: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
source: root.author.avatarSource
|
KirigamiComponents.Avatar {
|
||||||
name: root.author.displayName
|
id: replyAvatar
|
||||||
color: root.author.color
|
|
||||||
}
|
|
||||||
QQC2.Label {
|
|
||||||
id: replyName
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
color: root.author.color
|
implicitWidth: Kirigami.Units.iconSizes.small
|
||||||
text: root.author.displayName
|
implicitHeight: Kirigami.Units.iconSizes.small
|
||||||
elide: Text.ElideRight
|
|
||||||
}
|
|
||||||
Loader {
|
|
||||||
id: loader
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
source: root.author.avatarSource
|
||||||
Layout.maximumHeight: loader.item && (root.type == DelegateType.Image || root.type == DelegateType.Sticker) ? loader.item.height : loader.item.implicitHeight
|
name: root.author.displayName
|
||||||
Layout.columnSpan: 2
|
color: root.author.color
|
||||||
|
}
|
||||||
|
QQC2.Label {
|
||||||
|
id: replyName
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
sourceComponent: {
|
color: root.author.color
|
||||||
switch (root.type) {
|
text: root.author.displayName
|
||||||
case DelegateType.Image:
|
elide: Text.ElideRight
|
||||||
case DelegateType.Sticker:
|
}
|
||||||
return imageComponent;
|
}
|
||||||
case DelegateType.Message:
|
Loader {
|
||||||
case DelegateType.Notice:
|
id: loader
|
||||||
return textComponent;
|
|
||||||
case DelegateType.File:
|
Layout.fillWidth: true
|
||||||
case DelegateType.Video:
|
Layout.maximumHeight: loader.item && (root.type == DelegateType.Image || root.type == DelegateType.Sticker) ? loader.item.height : loader.item.implicitHeight
|
||||||
case DelegateType.Audio:
|
Layout.columnSpan: 2
|
||||||
return mimeComponent;
|
|
||||||
case DelegateType.Encrypted:
|
sourceComponent: {
|
||||||
return encryptedComponent;
|
switch (root.type) {
|
||||||
default:
|
case DelegateType.Image:
|
||||||
return textComponent;
|
case DelegateType.Sticker:
|
||||||
|
return imageComponent;
|
||||||
|
case DelegateType.Message:
|
||||||
|
case DelegateType.Notice:
|
||||||
|
return textComponent;
|
||||||
|
case DelegateType.File:
|
||||||
|
case DelegateType.Video:
|
||||||
|
case DelegateType.Audio:
|
||||||
|
return mimeComponent;
|
||||||
|
case DelegateType.Encrypted:
|
||||||
|
return encryptedComponent;
|
||||||
|
default:
|
||||||
|
return textComponent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,7 +191,7 @@ Item {
|
|||||||
MimeComponent {
|
MimeComponent {
|
||||||
mimeIconSource: root.mediaInfo.mimeIcon
|
mimeIconSource: root.mediaInfo.mimeIcon
|
||||||
label: root.display
|
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 {
|
Component {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user