Compare commits

..

1 Commits

164 changed files with 16268 additions and 25915 deletions

View File

@@ -4,7 +4,7 @@
include: include:
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/reuse-lint.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/reuse-lint.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android.yml
# - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android-qt6.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android-qt6.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux-qt6.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux-qt6.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows.yml

View File

@@ -1,8 +1,8 @@
# SPDX-FileCopyrightText: 2021 Tobias Fella <tobias.fella@kde.org> # SPDX-FileCopyrightText: 2021 Tobias Fella <fella@posteo.de>
# SPDX-License-Identifier: BSD-2-Clause # SPDX-License-Identifier: BSD-2-Clause
Dependencies: Dependencies:
- 'on': ['Linux/Qt5', 'Android/Qt5', 'FreeBSD/Qt5', 'Windows/Qt5'] - 'on': ['@all']
'require': 'require':
'frameworks/extra-cmake-modules': '@stable' 'frameworks/extra-cmake-modules': '@stable'
'frameworks/kcoreaddons': '@stable' 'frameworks/kcoreaddons': '@stable'
@@ -19,42 +19,15 @@ Dependencies:
'third-party/qtkeychain': '@latest' 'third-party/qtkeychain': '@latest'
'third-party/cmark': '@latest' 'third-party/cmark': '@latest'
'third-party/qcoro': '@latest' 'third-party/qcoro': '@latest'
- 'on': ['Windows/Qt5', 'Linux/Qt5', 'FreeBSD/Qt5'] - 'on': ['Windows', 'Linux', 'FreeBSD']
'require': 'require':
'frameworks/qqc2-desktop-style': '@stable' 'frameworks/qqc2-desktop-style': '@stable'
'frameworks/kio': '@stable' 'frameworks/kio': '@stable'
'frameworks/kwindowsystem': '@stable' 'frameworks/kwindowsystem': '@stable'
'frameworks/kconfigwidgets': '@stable' 'frameworks/kconfigwidgets': '@stable'
- 'on': ['Linux/Qt5', 'FreeBSD/Qt5'] - 'on': ['Linux', 'FreeBSD']
'require': 'require':
'frameworks/kdbusaddons': '@stable' 'frameworks/kdbusaddons': '@stable'
- 'on': ['Linux/Qt6', 'Android/Qt6', 'FreeBSD/Qt6', 'Windows/Qt6']
'require':
'frameworks/extra-cmake-modules': '@latest-kf6'
'frameworks/kcoreaddons': '@latest-kf6'
'frameworks/kirigami': '@latest-kf6'
'frameworks/ki18n': '@latest-kf6'
'frameworks/kconfig': '@latest-kf6'
'frameworks/syntax-highlighting': '@latest-kf6'
'frameworks/kitemmodels': '@latest-kf6'
'frameworks/knotifications': '@latest-kf6'
'libraries/kquickimageeditor': '@latest-kf6'
'frameworks/sonnet': '@latest-kf6'
'libraries/kirigami-addons': '@latest-kf6'
'third-party/libquotient': '@latest'
'third-party/qtkeychain': '@latest'
'third-party/cmark': '@latest'
'third-party/qcoro': '@latest'
- 'on': ['Windows/Qt6', 'Linux/Qt6', 'FreeBSD/Qt6']
'require':
'frameworks/qqc2-desktop-style': '@latest-kf6'
'frameworks/kio': '@latest-kf6'
'frameworks/kwindowsystem': '@latest-kf6'
'frameworks/kconfigwidgets': '@latest-kf6'
- 'on': ['Linux/Qt6', 'FreeBSD/Qt6']
'require':
'frameworks/kdbusaddons': '@latest-kf6'
Options: Options:
require-passing-tests-on: [ 'Linux/Qt5', 'FreeBSD', 'Windows' ] require-passing-tests-on: [ 'Linux/Qt5', 'FreeBSD', 'Windows' ]

View File

@@ -7,7 +7,7 @@ Copyright: 2020 Carson Black <uhhadd@gmail.com>
License: LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL License: LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
Files: android/res/drawable/splash.xml Files: android/res/drawable/splash.xml
Copyright: 2020 Tobias Fella <tobias.fella@kde.org> Copyright: 2020 Tobias Fella <fella@posteo.de>
License: BSD-2-Clause License: BSD-2-Clause
Files: .gitignore Files: .gitignore
@@ -27,11 +27,11 @@ Copyright: 2021 Carl Schwan <carlschwan@kde.org>
License: BSD-2-Clause License: BSD-2-Clause
Files: src/neochatconfig.kcfg Files: src/neochatconfig.kcfg
Copyright: 2020-2021 Carl Schwan <carlschwan@kde.org>, Tobias Fella <tobias.fella@kde.org> Copyright: 2020-2021 Carl Schwan <carlschwan@kde.org>, Tobias Fella <fella@posteo.de>
License: BSD-2-Clause License: BSD-2-Clause
Files: src/neochat.notifyrc Files: src/neochat.notifyrc
Copyright: 2020 Tobias Fella <tobias.fella@kde.org> Copyright: 2020 Tobias Fella <fella@posteo.de>
License: BSD-2-Clause License: BSD-2-Clause
Files: src/qml/Component/confetti.png src/qml/Component/glowdot.png Files: src/qml/Component/confetti.png src/qml/Component/glowdot.png
@@ -39,5 +39,5 @@ Copyright: 2021 Alexey Andreyev <aa13q@ya.ru>
License: CC0-1.0 License: CC0-1.0
Files: .flatpak-manifest.json Files: .flatpak-manifest.json
Copyright: 2020-2022 Tobias Fella <tobias.fella@kde.org> Copyright: 2020-2022 Tobias Fella <fella@posteo.de>
License: BSD-2-Clause License: BSD-2-Clause

View File

@@ -1,6 +1,6 @@
# SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carl@carlschwan.eu> # SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carl@carlschwan.eu>
# SPDX-FileCopyrightText: 2020-2021 Nicolas Fella <nicolas.fella@gmx.de> # SPDX-FileCopyrightText: 2020-2021 Nicolas Fella <nicolas.fella@gmx.de>
# SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org> # SPDX-FileCopyrightText: 2020-2021 Tobias Fella <fella@posteo.de>
# SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org> # SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
# SPDX-License-Identifier: BSD-2-Clause # SPDX-License-Identifier: BSD-2-Clause
@@ -8,19 +8,19 @@ 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 "23")
set(RELEASE_SERVICE_VERSION_MINOR "04") set(RELEASE_SERVICE_VERSION_MINOR "03")
set(RELEASE_SERVICE_VERSION_MICRO "0") set(RELEASE_SERVICE_VERSION_MICRO "70")
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})
set(KF_MIN_VERSION "5.91.0") set(KF5_MIN_VERSION "5.91.0")
set(QT_MIN_VERSION "5.15.2") set(QT_MIN_VERSION "5.15.2")
if (ANDROID) if (ANDROID)
set(QT_MIN_VERSION "5.15.8") set(QT_MIN_VERSION "5.15.8")
endif() endif()
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE) find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
@@ -57,16 +57,16 @@ set_package_properties(Qt${QT_MAJOR_VERSION} PROPERTIES
TYPE REQUIRED TYPE REQUIRED
PURPOSE "Basic application components" PURPOSE "Basic application components"
) )
find_package(KF${QT_MAJOR_VERSION} ${KF_MIN_VERSION} COMPONENTS Kirigami2 I18n Notifications Config CoreAddons Sonnet ItemModels) find_package(KF5 ${KF5_MIN_VERSION} COMPONENTS Kirigami2 I18n Notifications Config CoreAddons Sonnet ItemModels)
set_package_properties(KF${QT_MAJOR_VERSION} PROPERTIES set_package_properties(KF5 PROPERTIES
TYPE REQUIRED TYPE REQUIRED
PURPOSE "Basic application components" PURPOSE "Basic application components"
) )
set_package_properties(KF${QT_MAJOR_VERSION}Kirigami2 PROPERTIES set_package_properties(KF5Kirigami2 PROPERTIES
TYPE REQUIRED TYPE REQUIRED
PURPOSE "Kirigami application UI framework" PURPOSE "Kirigami application UI framework"
) )
find_package(KF${QT_MAJOR_VERSION}KirigamiAddons 0.7.2 REQUIRED) find_package(KF5KirigamiAddons 0.6 REQUIRED)
find_package(Qt${QT_MAJOR_VERSION}Keychain) find_package(Qt${QT_MAJOR_VERSION}Keychain)
set_package_properties(Qt${QT_MAJOR_VERSION}Keychain PROPERTIES set_package_properties(Qt${QT_MAJOR_VERSION}Keychain PROPERTIES
@@ -82,15 +82,15 @@ if(ANDROID)
) )
else() else()
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} COMPONENTS Widgets) find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} COMPONENTS Widgets)
find_package(KF${QT_MAJOR_VERSION} ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle ConfigWidgets KIO WindowSystem) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle ConfigWidgets KIO WindowSystem)
set_package_properties(KF${QT_MAJOR_VERSION}QQC2DesktopStyle PROPERTIES set_package_properties(KF5QQC2DesktopStyle PROPERTIES
TYPE RUNTIME TYPE RUNTIME
) )
ecm_find_qmlmodule(org.kde.syntaxhighlighting 1.0) ecm_find_qmlmodule(org.kde.syntaxhighlighting 1.0)
endif() endif()
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE) if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
find_package(KF${QT_MAJOR_VERSION}DBusAddons ${KF_MIN_VERSION} REQUIRED) find_package(KF5DBusAddons ${KF5_MIN_VERSION} REQUIRED)
endif() endif()
find_package(Quotient 0.6) find_package(Quotient 0.6)
@@ -124,8 +124,8 @@ find_package(QCoro${QT_MAJOR_VERSION} 0.4 COMPONENTS Core REQUIRED)
qcoro_enable_coroutines() qcoro_enable_coroutines()
find_package(KF${QT_MAJOR_VERSION}DocTools ${KF_MIN_VERSION}) find_package(KF5DocTools ${KF5_MIN_VERSION})
set_package_properties(KF${QT_MAJOR_VERSION}DocTools PROPERTIES DESCRIPTION set_package_properties(KF5DocTools PROPERTIES DESCRIPTION
"Tools to generate documentation" "Tools to generate documentation"
TYPE OPTIONAL TYPE OPTIONAL
) )
@@ -155,7 +155,7 @@ if (BUILD_TESTING AND Quotient_VERSION_MINOR GREATER 6)
add_subdirectory(autotests) add_subdirectory(autotests)
endif() endif()
if(KF${QT_MAJOR_VERSION}DocTools_FOUND) if(KF5DocTools_FOUND)
kdoctools_install(po) kdoctools_install(po)
add_subdirectory(doc) add_subdirectory(doc)
endif() endif()

View File

@@ -1,6 +1,6 @@
<!-- <!--
SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carlschwan@kde.org> SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carlschwan@kde.org>
SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org> SPDX-FileCopyrightText: 2020-2021 Tobias Fella <fella@posteo.de>
SPDX-License-Identifier: CC0-1.0 SPDX-License-Identifier: CC0-1.0
--> -->
# NeoChat # NeoChat

View File

@@ -8,9 +8,3 @@ ecm_add_test(
LINK_LIBRARIES neochat Qt::Test Quotient LINK_LIBRARIES neochat Qt::Test Quotient
TEST_NAME neochatroomtest TEST_NAME neochatroomtest
) )
ecm_add_test(
texthandlertest.cpp
LINK_LIBRARIES neochat Qt::Test
TEST_NAME texthandlertest
)

View File

@@ -136,7 +136,7 @@ void NeoChatRoomTest::initTestCase()
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->subtitleText(), QStringLiteral("@example:example.org: This is an example text message"));
} }
void NeoChatRoomTest::eventTest() void NeoChatRoomTest::eventTest()

View File

@@ -1,502 +0,0 @@
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <QObject>
#include <QTest>
#include "texthandler.h"
#include <connection.h>
#include <quotient_common.h>
#include <syncdata.h>
using namespace Quotient;
class TestRoom : public NeoChatRoom
{
public:
using NeoChatRoom::NeoChatRoom;
void update(SyncRoomData &&data, bool fromCache = false)
{
Room::updateData(std::move(data), fromCache);
}
};
class TextHandlerTest : public QObject
{
Q_OBJECT
private:
Connection *connection = nullptr;
TestRoom *room = nullptr;
private Q_SLOTS:
void initTestCase();
void allowedAttributes();
void stripDisallowedTags();
void stripDisallowedAttributes();
void emptyCodeTags();
void sendSimpleStringCase();
void sendSingleParaMarkup();
void sendMultipleSectionMarkup();
void sendBadLinks();
void sendEscapeCode();
void sendCodeClass();
void receiveStripReply();
void receivePlainTextIn();
void recieveRichInPlainOut();
void receivePlainStripHtml();
void receivePlainStripMarkup();
void receiveStripNewlines();
void receiveRichUserPill();
void receiveRichStrikethrough();
void receiveRichtextIn();
void receiveRichMxcUrl();
void receiveRichPlainUrl();
};
#ifdef QUOTIENT_07
void TextHandlerTest::initTestCase()
{
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
const auto json = QJsonDocument::fromJson(R"EVENT({
"account_data": {
"events": [
{
"content": {
"tags": {
"u.work": {
"order": 0.9
}
}
},
"type": "m.tag"
},
{
"content": {
"custom_config_key": "custom_config_value"
},
"type": "org.example.custom.room.config"
}
]
},
"ephemeral": {
"events": [
{
"content": {
"user_ids": [
"@alice:matrix.org",
"@bob:example.com"
]
},
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"type": "m.typing"
}
]
},
"state": {
"events": [
{
"content": {
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
"displayname": "Alice Margatroid",
"membership": "join",
"reason": "Looking for support"
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 1432735824653,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"state_key": "@alice:example.org",
"type": "m.room.member",
"unsigned": {
"age": 1234
}
}
]
},
"summary": {
"m.heroes": [
"@alice:example.com",
"@bob:example.com"
],
"m.invited_member_count": 0,
"m.joined_member_count": 2
},
"timeline": {
"events": [
{
"content": {
"body": "This is an **example** text message",
"format": "org.matrix.custom.html",
"formatted_body": "<b>This is an example text message</b>",
"msgtype": "m.text"
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 1432735824654,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"type": "m.room.message",
"unsigned": {
"age": 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));
}
#endif
void TextHandlerTest::allowedAttributes()
{
const QString testInputString1 = QStringLiteral("<p><span data-mx-spoiler><font color=#FFFFFF>Test</font><span></p>");
const QString testOutputString1 = QStringLiteral("<p><span data-mx-spoiler><font color=#FFFFFF>Test</font><span></p>");
// Handle urls where the href has either single (') or double (") quotes.
const QString testInputString2 = QStringLiteral("<p><a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a></p>");
const QString testOutputString2 = QStringLiteral("<p><a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a></p>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString1);
QCOMPARE(testTextHandler.handleSendText(), testOutputString1);
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString1);
testTextHandler.setData(testInputString2);
QCOMPARE(testTextHandler.handleSendText(), testOutputString2);
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString2);
}
void TextHandlerTest::stripDisallowedTags()
{
const QString testInputString = QStringLiteral("<p>Allowed</p> <span>Allowed</span> <body>Disallowed</body>");
const QString testOutputString = QStringLiteral("<p>Allowed</p> <span>Allowed</span> Disallowed");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
}
void TextHandlerTest::stripDisallowedAttributes()
{
const QString testInputString = QStringLiteral("<p style=\"font-size:50px;\" color=#FFFFFF>Test</p>");
const QString testOutputString = QStringLiteral("<p>Test</p>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
}
/**
* Make sure that empty code tags are handled.
* (this was a bug during development hence the test)
*/
void TextHandlerTest::emptyCodeTags()
{
const QString testInputString = QStringLiteral("<pre><code></code></pre>");
const QString testOutputString = QStringLiteral("<pre><code></code></pre>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
}
void TextHandlerTest::sendSimpleStringCase()
{
const QString testInputString = QStringLiteral("This data should just be put in a paragraph.");
const QString testOutputString = QStringLiteral("<p>This data should just be put in a paragraph.</p>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
}
void TextHandlerTest::sendSingleParaMarkup()
{
const QString testInputString = QStringLiteral(
"Text para with **bold**, *italic*, [link](https://kde.org), ![image](mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e), `inline code`.");
const QString testOutputString = QStringLiteral(
"<p>Text para with <strong>bold</strong>, <em>italic</em>, <a href=\"https://kde.org\">link</a>, <img "
"src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e\" alt=\"image\">, <code>inline code</code>.</p>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
}
void TextHandlerTest::sendMultipleSectionMarkup()
{
const QString testInputString =
QStringLiteral("Text para\n> blockquote\n* List 1\n* List 2\n1. one\n2. two\n# Heading 1\n## Heading 2\nhorizontal rule\n\n---\n```\ncodeblock\n```");
const QString testOutputString = QStringLiteral(
"<p>Text para</p>\n<blockquote>\n<p>blockquote</p>\n</blockquote>\n<ul>\n<li>List 1</li>\n<li>List "
"2</li>\n</ul>\n<ol>\n<li>one</li>\n<li>two</li>\n</ol>\n<h1>Heading 1</h1>\n<h2>Heading 2</h2>\n<p>horizontal "
"rule</p>\n<hr>\n<pre><code>codeblock\n</code></pre>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
}
void TextHandlerTest::sendBadLinks()
{
const QString testInputString = QStringLiteral("[link](kde.org), ![image](https://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e)");
const QString testOutputString = QStringLiteral("<p><a>link</a>, <img alt=\"image\"></p>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
}
/**
* All text between code tags is treated as plain so it should get escaped.
*/
void TextHandlerTest::sendEscapeCode()
{
const QString testInputString = QStringLiteral("```\n<p>Test <span style=\"font-size:50px;\">some</span> code</p>\n```");
const QString testOutputString =
QStringLiteral("<pre><code>&lt;p&gt;Test &lt;span style=&quot;font-size:50px;&quot;&gt;some&lt;/span&gt; code&lt;/p&gt;\n</code></pre>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
}
void TextHandlerTest::sendCodeClass()
{
const QString testInputString = QStringLiteral("```html\nsome code\n```\n<pre><code class=\"code-underline\">some more code</code></pre>");
const QString testOutputString = QStringLiteral("<pre><code class=\"language-html\">some code\n</code></pre>\n<pre><code>some more code</code></pre>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
}
void TextHandlerTest::receiveStripReply()
{
const QString testInputString = QStringLiteral(
"<mx-reply><blockquote><a href=\"https://matrix.to/#/!somewhere:example.org/$event:example.org\">In reply to</a><a "
"href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a><br />Message replied to.</blockquote></mx-reply>Reply message.");
const QString testOutputString = QStringLiteral("Reply message.");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputString);
}
void TextHandlerTest::recieveRichInPlainOut()
{
const QString testInputString = QStringLiteral("a &amp; b");
const QString testOutputString = QStringLiteral("a & b");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputString);
}
void TextHandlerTest::receivePlainTextIn()
{
const QString testInputString = QStringLiteral("<plain text in tag bracket>\nTest link https://kde.org.");
const QString testOutputStringRich = QStringLiteral("&lt;plain text in tag bracket&gt;<br>Test link <a href=\"https://kde.org\">https://kde.org</a>.");
QString testOutputStringPlain = QStringLiteral("<plain text in tag bracket>\nTest link https://kde.org.");
// Make sure quotes are maintained in a plain string.
const QString testInputString2 = QStringLiteral("last line is \"Time to switch to a new topic.\"");
const QString testOutputString2 = QStringLiteral("last line is \"Time to switch to a new topic.\"");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::PlainText), testOutputStringRich);
QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputStringPlain);
testTextHandler.setData(testInputString2);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::PlainText), testOutputString2);
QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputString2);
}
void TextHandlerTest::receiveStripNewlines()
{
const QString testInputStringPlain = QStringLiteral("Test\nmany\nnew\nlines.");
const QString testInputStringRich = QStringLiteral("Test<br>many<br />new<br>lines.");
const QString testOutputString = QStringLiteral("Test many new lines.");
const QString testInputStringPlain2 = QStringLiteral("* List\n* Items");
const QString testOutputString2 = QStringLiteral("List Items");
TextHandler testTextHandler;
testTextHandler.setData(testInputStringPlain);
QCOMPARE(testTextHandler.handleRecievePlainText(Qt::PlainText, true), testOutputString);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::PlainText, nullptr, nullptr, true), testOutputString);
testTextHandler.setData(testInputStringRich);
QCOMPARE(testTextHandler.handleRecievePlainText(Qt::RichText, true), testOutputString);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText, nullptr, nullptr, true), testOutputString);
testTextHandler.setData(testInputStringPlain2);
QCOMPARE(testTextHandler.handleRecievePlainText(Qt::RichText, true), testOutputString2);
}
/**
* For a plain text output of a received string all html is stripped except for
* code which is unescaped if it's html.
*/
void TextHandlerTest::receivePlainStripHtml()
{
const QString testInputString = QStringLiteral("<p>Test</p> <pre><code>Some code <strong>with tags</strong></code></pre>");
const QString testOutputString = QStringLiteral("Test Some code <strong>with tags</strong>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleRecievePlainText(Qt::RichText), testOutputString);
}
void TextHandlerTest::receivePlainStripMarkup()
{
const QString testInputString = QStringLiteral("**bold** `<p>inline code</p>` *italic*");
const QString testOutputString = QStringLiteral("bold <p>inline code</p> italic");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputString);
}
void TextHandlerTest::receiveRichUserPill()
{
const QString testInputString = QStringLiteral("<p><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></p>");
const QString testOutputString = QStringLiteral("<p><b><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></b></p>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
}
void TextHandlerTest::receiveRichStrikethrough()
{
const QString testInputString = QStringLiteral("<p><del>Test</del></p>");
const QString testOutputString = QStringLiteral("<p><s>Test</s></p>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
}
void TextHandlerTest::receiveRichtextIn()
{
const QString testInputString = QStringLiteral("<p>Test</p> <pre><code>Some code <strong>with tags</strong></code></pre>");
const QString testOutputString = QStringLiteral("<p>Test</p> <pre><code>Some code &lt;strong&gt;with tags&lt;/strong&gt;</code></pre>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
}
#ifdef QUOTIENT_07
void TextHandlerTest::receiveRichMxcUrl()
{
const QString testInputString = QStringLiteral(
"<img src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e\" alt=\"image\"><img src=\"mxc://kde.org/34c3464b3a1bd7f55af2d559e07d2c773c430e73\" "
"alt=\"image\">");
const QString testOutputString = QStringLiteral(
"<img "
"src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e?user_id=@bob:kde.org&room_id=%23myroom:kde.org&event_id=$143273582443PhrSn:example.org\" "
"alt=\"image\"><img "
"src=\"mxc://kde.org/34c3464b3a1bd7f55af2d559e07d2c773c430e73?user_id=@bob:kde.org&room_id=%23myroom:kde.org&event_id=$143273582443PhrSn:example.org\" "
"alt=\"image\">");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText, room, room->messageEvents().back().get()), testOutputString);
}
#endif
/**
* For when your rich input string has a plain text url left in.
*
* This test is to show that a url that is already rich will be left alone but a
* plain one will be linkified.
*/
void TextHandlerTest::receiveRichPlainUrl()
{
// This is an actual link that caused trouble which is why it's so long. Keeping
// so we can confirm consistent behaviour for complex urls.
const QString testInputStringLink1 = QStringLiteral(
"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im "
"<a "
"href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/"
"$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">Link already rich</a>");
const QString testOutputStringLink1 = QStringLiteral(
"<a "
"href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/"
"$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">https://matrix.to/#/"
"!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im</a> <a "
"href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/"
"$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">Link already rich</a>");
// Another real case. The linkification wasn't handling it when a single link
// contains what looks like and email. It was been broken into 3 but needs to
// be just single link.
const QString testInputStringLink2 = QStringLiteral("https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/");
const QString testOutputStringLink2 = QStringLiteral(
"<a "
"href=\"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/\">https://lore.kernel.org/lkml/"
"CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/</a>");
QString testInputStringEmail = QStringLiteral(R"(email@example.com <a href="mailto:email@example.com">Link already rich</a>)");
QString testOutputStringEmail =
QStringLiteral(R"(<a href="mailto:email@example.com">email@example.com</a> <a href="mailto:email@example.com">Link already rich</a>)");
QString testInputStringMxId = QStringLiteral("@user:kde.org <a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a>");
QString testOutputStringMxId = QStringLiteral(
"<b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> <b><a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a></b>");
TextHandler testTextHandler;
testTextHandler.setData(testInputStringLink1);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringLink1);
testTextHandler.setData(testInputStringLink2);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringLink2);
testTextHandler.setData(testInputStringEmail);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringEmail);
testTextHandler.setData(testInputStringMxId);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringMxId);
}
QTEST_MAIN(TextHandlerTest)
#include "texthandlertest.moc"

View File

@@ -2,7 +2,7 @@
<!-- <!--
- SPDX-License-Identifier: CC0-1.0 - SPDX-License-Identifier: CC0-1.0
- SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carlschwan@kde.org> - SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carlschwan@kde.org>
- SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org> - SPDX-FileCopyrightText: 2020-2021 Tobias Fella <fella@posteo.de>
--> -->
<component type="desktop"> <component type="desktop">
<id>org.kde.neochat</id> <id>org.kde.neochat</id>
@@ -179,7 +179,7 @@
<category>Network</category> <category>Network</category>
</categories> </categories>
<developer_name>The KDE Community</developer_name> <developer_name>The KDE Community</developer_name>
<developer_name xml:lang="ar">مجتمع كِيدِي</developer_name> <developer_name xml:lang="ar">مجتمع كدي</developer_name>
<developer_name xml:lang="az">KDE Cəmiyyəti</developer_name> <developer_name xml:lang="az">KDE Cəmiyyəti</developer_name>
<developer_name xml:lang="ca">La comunitat KDE</developer_name> <developer_name xml:lang="ca">La comunitat KDE</developer_name>
<developer_name xml:lang="ca-valencia">La comunitat KDE</developer_name> <developer_name xml:lang="ca-valencia">La comunitat KDE</developer_name>
@@ -231,7 +231,6 @@
<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.04.0" date="2023-04-20"/>
<release version="23.01" date="2023-01-30"> <release version="23.01" date="2023-01-30">
<url>https://plasma-mobile.org/2023/01/30/january-blog-post/</url> <url>https://plasma-mobile.org/2023/01/30/january-blog-post/</url>
<description> <description>

View File

@@ -1,5 +1,5 @@
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
# SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org> # SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
[Desktop Entry] [Desktop Entry]
Version=1.5 Version=1.5
Name=NeoChat Name=NeoChat

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +0,0 @@
# SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
#
# SPDX-License-Identifier: GPL-3.0-or-later
kdoctools_create_manpage(man-neochat.1.docbook 1 INSTALL_DESTINATION ${MAN_INSTALL_DIR})

View File

@@ -1,122 +0,0 @@
<?xml version="1.0" ?>
<!DOCTYPE refentry PUBLIC "-//KDE//DTD DocBook XML V4.5-Based Variant V1.1//EN" "dtd/kdedbx45.dtd" [
<!ENTITY % Turkish "INCLUDE">
]>
<!--
SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
SPDX-License-Identifier: CC-BY-SA-4.0
-->
<refentry lang="&language;">
<refentryinfo>
<title
>NeoChat Kullanıcı Kılavuzu</title>
<author
><firstname
>Carl</firstname
><surname
>Schwan</surname
> <contrib
>NeoChat man page.</contrib
> <email
>carl@carlschwan.eu</email
></author>
<date
>2022-11-01</date>
<releaseinfo
>22.09</releaseinfo>
<productname
>NeoChat</productname>
</refentryinfo>
<refmeta>
<refentrytitle>
<command
>neochat</command>
</refentrytitle>
<manvolnum
>1</manvolnum>
</refmeta>
<refnamediv>
<refname
>neochat</refname>
<refpurpose
>Matrix iletileşme protokolü ile etkileşim için istemci</refpurpose>
</refnamediv>
<!-- body begins here -->
<refsynopsisdiv id='synopsis'>
<cmdsynopsis
><command
>neochat</command
> <arg choice="opt"
><replaceable
>URI</replaceable
></arg
> </cmdsynopsis>
</refsynopsisdiv>
<refsect1 id="description">
<title
>Açıklama</title>
<para
><command
>neochat</command
>, Matrix protokolü için hem masaüstünde hem de taşınabilir aygıtlarda çalışan bir sohbet uygulamasıdır. </para>
</refsect1>
<refsect1 id="options"
><title
>Seçenekler</title>
<variablelist>
<varlistentry>
<term
><option
>URI</option
></term>
<listitem>
<para
>Bir kullanıcı veya oda için matrix URI'si; örneğin, matrix:u/kullanıcı:örnek.org ve matrix:r/kök:örnek.org. Bu, NeoChat'in verilen odayı veya konuşmayı açmayı denemesini sağlar. </para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1 id="bug">
<title
>Hata Bildirme</title>
<para
>Hataları veya özellik isteklerini <ulink url="https://bugs.kde.org/enter_bug.cgi?product=NeoChat&amp;component=General"
>https://bugs.kde.org/enter_bug.cgi?product=NeoChat&amp;component=General</ulink
> bağlantısından bildirebilirsiniz</para>
</refsect1>
<refsect1>
<title
>Ayrıca</title>
<simplelist>
<member
>Matrix üzerine sıkça sorulan soruların bir listesi: <ulink url="https://matrix.org/faq/"
>https://matrix.org/faq/</ulink
> </member>
<member
>kf5options(7)</member>
<member
>qt5options(7)</member>
</simplelist>
</refsect1>
<refsect1 id="copyright"
><title
>Telif Hakkı</title>
<para
>Telif hakkı &copy; 2020-2022 Tobias Fella </para>
<para
>Telif hakkı &copy; 2020-2022 Carl Schwan </para>
<para
>Lisans: GNU Genel Kamu Lisansa, 3. sürüm veya sonrası &lt;<ulink url="https://www.gnu.org/licenses/gpl-3.0.html"
>https://www.gnu.org/licenses/gpl-3.0.html</ulink
>&gt;</para>
</refsect1>
</refentry>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
# SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carl@carlschwan.eu> # SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carl@carlschwan.eu>
# SPDX-FileCopyrightText: 2020-2021 Nicolas Fella <nicolas.fella@gmx.de> # SPDX-FileCopyrightText: 2020-2021 Nicolas Fella <nicolas.fella@gmx.de>
# SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org> # SPDX-FileCopyrightText: 2020-2021 Tobias Fella <fella@posteo.de>
# SPDX-License-Identifier: BSD-2-Clause # SPDX-License-Identifier: BSD-2-Clause
add_library(neochat STATIC add_library(neochat STATIC
@@ -24,6 +24,7 @@ add_library(neochat STATIC
models/publicroomlistmodel.cpp models/publicroomlistmodel.cpp
models/userdirectorylistmodel.cpp models/userdirectorylistmodel.cpp
models/keywordnotificationrulemodel.cpp models/keywordnotificationrulemodel.cpp
utils.cpp
notificationsmanager.cpp notificationsmanager.cpp
models/sortfilterroomlistmodel.cpp models/sortfilterroomlistmodel.cpp
chatdocumenthandler.cpp chatdocumenthandler.cpp
@@ -46,7 +47,6 @@ add_library(neochat STATIC
models/statemodel.cpp models/statemodel.cpp
filetransferpseudojob.cpp filetransferpseudojob.cpp
models/searchmodel.cpp models/searchmodel.cpp
texthandler.cpp
) )
add_executable(neochat-app add_executable(neochat-app
@@ -79,7 +79,7 @@ if(NOT ANDROID)
else() else()
target_sources(neochat PRIVATE trayicon.cpp) target_sources(neochat PRIVATE trayicon.cpp)
endif() endif()
target_link_libraries(neochat PUBLIC KF${QT_MAJOR_VERSION}::ConfigWidgets KF${QT_MAJOR_VERSION}::WindowSystem) target_link_libraries(neochat PUBLIC KF5::ConfigWidgets KF5::WindowSystem)
target_compile_definitions(neochat PUBLIC -DHAVE_COLORSCHEME) target_compile_definitions(neochat PUBLIC -DHAVE_COLORSCHEME)
target_compile_definitions(neochat PUBLIC -DHAVE_WINDOWSYSTEM) target_compile_definitions(neochat PUBLIC -DHAVE_WINDOWSYSTEM)
endif() endif()
@@ -87,14 +87,13 @@ endif()
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE) if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
target_sources(neochat-app PRIVATE res_desktop.qrc) target_sources(neochat-app PRIVATE res_desktop.qrc)
target_compile_definitions(neochat PUBLIC -DHAVE_RUNNER) target_compile_definitions(neochat PUBLIC -DHAVE_RUNNER)
target_compile_definitions(neochat PUBLIC -DHAVE_X11)
target_sources(neochat PRIVATE runner.cpp) target_sources(neochat PRIVATE runner.cpp)
else() else()
target_sources(neochat-app PRIVATE res_android.qrc) target_sources(neochat-app PRIVATE res_android.qrc)
endif() endif()
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR}) target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR})
target_link_libraries(neochat PUBLIC Qt::Core Qt::Quick Qt::Qml Qt::Gui Qt::Multimedia Qt::Network Qt::QuickControls2 KF${QT_MAJOR_VERSION}::I18n KF${QT_MAJOR_VERSION}::Kirigami2 KF${QT_MAJOR_VERSION}::Notifications KF${QT_MAJOR_VERSION}::ConfigCore KF${QT_MAJOR_VERSION}::ConfigGui KF${QT_MAJOR_VERSION}::CoreAddons KF${QT_MAJOR_VERSION}::SonnetCore KF${QT_MAJOR_VERSION}::ItemModels Quotient cmark::cmark ${QTKEYCHAIN_LIBRARIES} QCoro::Core) target_link_libraries(neochat PUBLIC Qt::Core Qt::Quick Qt::Qml Qt::Gui Qt::Multimedia Qt::Network Qt::QuickControls2 KF5::I18n KF5::Kirigami2 KF5::Notifications KF5::ConfigCore KF5::ConfigGui KF5::CoreAddons KF5::SonnetCore KF5::ItemModels Quotient cmark::cmark ${QTKEYCHAIN_LIBRARIES} QCoro::Core)
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc) kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
if(NEOCHAT_FLATPAK) if(NEOCHAT_FLATPAK)
@@ -177,7 +176,7 @@ if(ANDROID)
"visibility" "visibility"
) )
else() else()
target_link_libraries(neochat PUBLIC Qt::Widgets KF${QT_MAJOR_VERSION}::KIOWidgets) target_link_libraries(neochat PUBLIC Qt::Widgets KF5::KIOWidgets)
install(FILES neochat.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFYRCDIR}) install(FILES neochat.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFYRCDIR})
endif() endif()
@@ -185,12 +184,12 @@ if(NOT ANDROID)
set_target_properties(neochat-app PROPERTIES OUTPUT_NAME "neochat") set_target_properties(neochat-app PROPERTIES OUTPUT_NAME "neochat")
endif() endif()
if(TARGET KF${QT_MAJOR_VERSION}::DBusAddons) if(TARGET KF5::DBusAddons)
target_link_libraries(neochat PUBLIC KF${QT_MAJOR_VERSION}::DBusAddons) target_link_libraries(neochat PUBLIC KF5::DBusAddons)
target_compile_definitions(neochat PUBLIC -DHAVE_KDBUSADDONS) target_compile_definitions(neochat PUBLIC -DHAVE_KDBUSADDONS)
endif() endif()
if (TARGET KF${QT_MAJOR_VERSION}::KIOWidgets) if (TARGET KF5::KIOWidgets)
target_compile_definitions(neochat PUBLIC -DHAVE_KIO) target_compile_definitions(neochat PUBLIC -DHAVE_KIO)
endif() endif()

View File

@@ -20,10 +20,25 @@
#include "neochatroom.h" #include "neochatroom.h"
#include "neochatuser.h" #include "neochatuser.h"
#include "roommanager.h" #include "roommanager.h"
#include "texthandler.h"
using namespace Quotient; using namespace Quotient;
QString markdownToHTML(const QString &markdown)
{
const auto str = markdown.toUtf8();
char *tmp_buf = cmark_markdown_to_html(str.constData(), str.size(), CMARK_OPT_HARDBREAKS | CMARK_OPT_UNSAFE);
const std::string html(tmp_buf);
free(tmp_buf);
auto result = QString::fromStdString(html).trimmed();
result.replace("<!-- raw HTML omitted -->", "");
return result;
}
ActionsHandler::ActionsHandler(QObject *parent) ActionsHandler::ActionsHandler(QObject *parent)
: QObject(parent) : QObject(parent)
{ {
@@ -154,10 +169,7 @@ void ActionsHandler::handleMessage(const QString &text, QString handledText, con
} }
handledText = CustomEmojiModel::instance().preprocessText(handledText); handledText = CustomEmojiModel::instance().preprocessText(handledText);
TextHandler textHandler; handledText = markdownToHTML(handledText);
textHandler.setData(handledText);
handledText = textHandler.handleSendText();
if (handledText.count("<p>") == 1 && handledText.count("</p>") == 1) { if (handledText.count("<p>") == 1 && handledText.count("</p>") == 1) {
handledText.remove("<p>"); handledText.remove("<p>");
handledText.remove("</p>"); handledText.remove("</p>");

View File

@@ -50,3 +50,5 @@ private:
QString handleMentions(QString handledText, const bool &isEdit = false); QString handleMentions(QString handledText, const bool &isEdit = false);
void handleMessage(const QString &text, QString handledText, const bool &isEdit = false); void handleMessage(const QString &text, QString handledText, const bool &isEdit = false);
}; };
QString markdownToHTML(const QString &markdown);

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2021 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2021 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
#include "blurhashimageprovider.h" #include "blurhashimageprovider.h"

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2021 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2021 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once #pragma once

View File

@@ -61,21 +61,9 @@ QString Clipboard::saveImage(QString localPath) const
void Clipboard::saveText(QString message) void Clipboard::saveText(QString message)
{ {
static QRegularExpression re(QStringLiteral("<[^>]*>")); QRegularExpression re("<[^>]*>");
auto *mineData = new QMimeData; // ownership is transferred to clipboard auto *mineData = new QMimeData; // ownership is transferred to clipboard
mineData->setHtml(message); mineData->setHtml(message);
mineData->setText(message.replace(re, QString())); mineData->setText(message.replace(re, ""));
m_clipboard->setMimeData(mineData); m_clipboard->setMimeData(mineData);
} }
void Clipboard::setImage(const QUrl &url)
{
if (url.isLocalFile()) {
QImage img(url.path());
auto *mimeData = new QMimeData;
mimeData->setImageData(img);
if (!img.isNull()) {
m_clipboard->setMimeData(mimeData);
}
}
}

View File

@@ -26,7 +26,6 @@ public:
Q_INVOKABLE QString saveImage(QString localPath = {}) const; Q_INVOKABLE QString saveImage(QString localPath = {}) const;
Q_INVOKABLE void saveText(QString message); Q_INVOKABLE void saveText(QString message);
Q_INVOKABLE void setImage(const QUrl &image);
private: private:
QClipboard *m_clipboard; QClipboard *m_clipboard;

View File

@@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: 2018-2019 Black Hat <bhat@encom.eu.org> // SPDX-FileCopyrightText: 2018-2019 Black Hat <bhat@encom.eu.org>
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
#include "controller.h" #include "controller.h"
@@ -41,7 +41,6 @@
#include <csapi/content-repo.h> #include <csapi/content-repo.h>
#include <csapi/logout.h> #include <csapi/logout.h>
#include <csapi/profile.h> #include <csapi/profile.h>
#include <jobs/downloadfilejob.h>
#include <qt_connection_util.h> #include <qt_connection_util.h>
#ifdef QUOTIENT_07 #ifdef QUOTIENT_07
@@ -170,9 +169,7 @@ void Controller::handleNotifications(QPointer<Quotient::Connection> connection)
auto room = connection->room(notification["room_id"].toString()); auto room = connection->room(notification["room_id"].toString());
// If room exists, room is NOT active OR the application is NOT active, show notification // If room exists, room is NOT active OR the application is NOT active, show notification
if (room if (room && !(room->id() == RoomManager::instance().currentRoom()->id() && QGuiApplication::applicationState() == Qt::ApplicationActive)) {
&& !(RoomManager::instance().currentRoom() && room->id() == RoomManager::instance().currentRoom()->id()
&& QGuiApplication::applicationState() == Qt::ApplicationActive)) {
// The room might have been deleted (for example rejected invitation). // The room might have been deleted (for example rejected invitation).
auto sender = room->user(notification["event"].toObject()["sender"].toString()); auto sender = room->user(notification["event"].toObject()["sender"].toString());
@@ -611,11 +608,6 @@ void Controller::setActiveConnection(Connection *connection)
m_isOnline = true; m_isOnline = true;
Q_EMIT isOnlineChanged(true); Q_EMIT isOnlineChanged(true);
}); });
connect(connection, &Connection::requestFailed, this, [=](BaseJob *job) {
if (dynamic_cast<DownloadFileJob *>(job) && job->jsonData()["errcode"].toString() == "M_TOO_LARGE"_ls) {
RoomManager::instance().warning(i18n("File too large to download."), i18n("Contact your matrix server administrator for support."));
}
});
} else { } else {
NeoChatConfig::self()->setActiveConnection(QString()); NeoChatConfig::self()->setActiveConnection(QString());
} }
@@ -644,17 +636,9 @@ NeochatDeleteDeviceJob::NeochatDeleteDeviceJob(const QString &deviceId, const Om
void Controller::createRoom(const QString &name, const QString &topic) void Controller::createRoom(const QString &name, const QString &topic)
{ {
auto createRoomJob = m_connection->createRoom(Connection::PublishRoom, "", name, topic, QStringList()); auto createRoomJob = m_connection->createRoom(Connection::PublishRoom, "", name, topic, QStringList());
connect(createRoomJob, &CreateRoomJob::failure, this, [this, createRoomJob] { Quotient::CreateRoomJob::connect(createRoomJob, &CreateRoomJob::failure, [this, createRoomJob] {
Q_EMIT errorOccured(i18n("Room creation failed: \"%1\"", createRoomJob->errorString())); Q_EMIT errorOccured(i18n("Room creation failed: \"%1\"", createRoomJob->errorString()));
}); });
connectSingleShot(
this,
&Controller::roomAdded,
this,
[this](NeoChatRoom *room) {
RoomManager::instance().enterRoom(room);
},
Qt::QueuedConnection);
} }
bool Controller::isOnline() const bool Controller::isOnline() const

View File

@@ -151,7 +151,6 @@ Q_SIGNALS:
void keyVerificationAccept(const QString &commitment); void keyVerificationAccept(const QString &commitment);
void keyVerificationKey(const QString &sas); void keyVerificationKey(const QString &sas);
void activeConnectionIndexChanged(); void activeConnectionIndexChanged();
void roomAdded(NeoChatRoom *room);
public Q_SLOTS: public Q_SLOTS:
void logout(Quotient::Connection *conn, bool serverSideLogout); void logout(Quotient::Connection *conn, bool serverSideLogout);

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "filetransferpseudojob.h" #include "filetransferpseudojob.h"

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "login.h" #include "login.h"

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once

View File

@@ -157,7 +157,7 @@ int main(int argc, char *argv[])
KAboutLicense::GPL_V3, KAboutLicense::GPL_V3,
i18n("© 2018-2020 Black Hat, 2020-2022 KDE Community")); i18n("© 2018-2020 Black Hat, 2020-2022 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"));
about.addAuthor(i18n("Tobias Fella"), i18n("Maintainer"), QStringLiteral("tobias.fella@kde.org"), QStringLiteral("https://tobiasfella.de")); about.addAuthor(i18n("Tobias Fella"), i18n("Maintainer"), QStringLiteral("fella@posteo.de"), 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"));
about.addCredit(i18n("Alexey Rusakov"), i18n("Maintainer of Quotient"), QStringLiteral("Kitsune-Ral@users.sf.net")); about.addCredit(i18n("Alexey Rusakov"), i18n("Maintainer of Quotient"), QStringLiteral("Kitsune-Ral@users.sf.net"));

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
#include "actionsmodel.h" #include "actionsmodel.h"
@@ -115,7 +115,7 @@ QVector<ActionsModel::Action> actions{
QStringLiteral("spoiler"), QStringLiteral("spoiler"),
[](const QString &text, NeoChatRoom *room) { [](const QString &text, NeoChatRoom *room) {
// Ideally, we would just return rainbowText and let that do the rest, but the colors don't survive markdownToHTML. // Ideally, we would just return rainbowText and let that do the rest, but the colors don't survive markdownToHTML.
room->postMessage(QStringLiteral("/spoiler %1").arg(text), room->postMessage(QStringLiteral("/rainbow %1").arg(text),
QStringLiteral("<span data-mx-spoiler>%1</span>").arg(text), QStringLiteral("<span data-mx-spoiler>%1</span>").arg(text),
RoomMessageEvent::MsgType::Text, RoomMessageEvent::MsgType::Text,
room->chatBoxReplyId(), room->chatBoxReplyId(),
@@ -209,40 +209,6 @@ QVector<ActionsModel::Action> actions{
kli18n("<room alias or id>"), kli18n("<room alias or id>"),
kli18n("Joins the given room"), kli18n("Joins the given room"),
}, },
#ifdef QUOTIENT_07
Action{
QStringLiteral("knock"),
[](const QString &text, NeoChatRoom *room) {
auto parts = text.split(QLatin1String(" "));
QString roomName = parts[0];
QRegularExpression roomRegex(QStringLiteral(R"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)"));
auto regexMatch = roomRegex.match(roomName);
if (!regexMatch.hasMatch()) {
Q_EMIT room->showMessage(NeoChatRoom::Error,
i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text));
return QString();
}
auto targetRoom = text.startsWith(QLatin1Char('!')) ? room->connection()->room(text) : room->connection()->roomByAlias(text);
if (targetRoom) {
RoomManager::instance().enterRoom(dynamic_cast<NeoChatRoom *>(targetRoom));
return QString();
}
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Knocking room <roomname>.", "Knocking room %1.", text));
auto connection = Controller::instance().activeConnection();
const auto knownServer = roomName.mid(roomName.indexOf(":") + 1);
if (parts.length() >= 2) {
RoomManager::instance().knockRoom(connection, roomName, parts[1], QStringList{knownServer});
} else {
RoomManager::instance().knockRoom(connection, roomName, QString(), QStringList{knownServer});
}
return QString();
},
false,
std::nullopt,
kli18n("<room alias or id> [<reason>]"),
kli18n("Requests to join the given room"),
},
#endif
Action{ Action{
QStringLiteral("j"), QStringLiteral("j"),
[](const QString &text, NeoChatRoom *room) { [](const QString &text, NeoChatRoom *room) {

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once #pragma once

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
#include "collapsestateproxymodel.h" #include "collapsestateproxymodel.h"

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once #pragma once

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
#include "completionmodel.h" #include "completionmodel.h"
@@ -145,7 +145,7 @@ void CompletionModel::updateCompletion()
m_filterModel->setFullText(m_fullText); m_filterModel->setFullText(m_fullText);
m_filterModel->setFilterText(m_text); m_filterModel->setFilterText(m_text);
m_filterModel->invalidate(); m_filterModel->invalidate();
} else if (text().startsWith(QLatin1Char(':')) && !text()[1].isUpper() } else if (text().startsWith(QLatin1Char(':'))
&& (m_fullText.indexOf(QLatin1Char(':'), 1) == -1 && (m_fullText.indexOf(QLatin1Char(':'), 1) == -1
|| (m_fullText.indexOf(QLatin1Char(' ')) != -1 && m_fullText.indexOf(QLatin1Char(':'), 1) > m_fullText.indexOf(QLatin1Char(' '), 1)))) { || (m_fullText.indexOf(QLatin1Char(' ')) != -1 && m_fullText.indexOf(QLatin1Char(':'), 1) > m_fullText.indexOf(QLatin1Char(' '), 1)))) {
m_filterModel->setSourceModel(m_emojiModel); m_filterModel->setSourceModel(m_emojiModel);

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once #pragma once

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
#include "completionproxymodel.h" #include "completionproxymodel.h"

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once #pragma once

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "devicesmodel.h" #include "devicesmodel.h"

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once

View File

@@ -27,6 +27,7 @@
#include <KLocalizedString> #include <KLocalizedString>
#include "neochatuser.h" #include "neochatuser.h"
#include "utils.h"
using namespace Quotient; using namespace Quotient;
@@ -53,9 +54,6 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
roles[UserMarkerRole] = "userMarker"; roles[UserMarkerRole] = "userMarker";
roles[ShowAuthorRole] = "showAuthor"; roles[ShowAuthorRole] = "showAuthor";
roles[ShowSectionRole] = "showSection"; roles[ShowSectionRole] = "showSection";
roles[ReadMarkersRole] = "readMarkers";
roles[ReadMarkersStringRole] = "readMarkersString";
roles[ShowReadMarkersRole] = "showReadMarkers";
roles[ReactionRole] = "reaction"; roles[ReactionRole] = "reaction";
roles[IsEditedRole] = "isEdited"; roles[IsEditedRole] = "isEdited";
roles[SourceRole] = "source"; roles[SourceRole] = "source";
@@ -66,9 +64,10 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
roles[VerifiedRole] = "verified"; roles[VerifiedRole] = "verified";
roles[DisplayNameForInitialsRole] = "displayNameForInitials"; roles[DisplayNameForInitialsRole] = "displayNameForInitials";
roles[AuthorDisplayNameRole] = "authorDisplayName"; roles[AuthorDisplayNameRole] = "authorDisplayName";
roles[IsNameChangeRole] = "isNameChange";
roles[IsAvatarChangeRole] = "isAvatarChange";
roles[IsRedactedRole] = "isRedacted"; roles[IsRedactedRole] = "isRedacted";
roles[GenericDisplayRole] = "genericDisplay"; roles[GenericDisplayRole] = "genericDisplay";
roles[IsPendingRole] = "isPending";
return roles; return roles;
} }
@@ -177,7 +176,6 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
}); });
connect(m_currentRoom, &Room::pendingEventAdded, this, &MessageEventModel::endInsertRows); connect(m_currentRoom, &Room::pendingEventAdded, this, &MessageEventModel::endInsertRows);
connect(m_currentRoom, &Room::pendingEventAboutToMerge, this, [this](RoomEvent *, int i) { connect(m_currentRoom, &Room::pendingEventAboutToMerge, this, [this](RoomEvent *, int i) {
Q_EMIT dataChanged(index(i, 0), index(i, 0), {IsPendingRole});
if (i == 0) { if (i == 0) {
return; // No need to move anything, just refresh return; // No need to move anything, just refresh
} }
@@ -216,12 +214,6 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
} }
refreshEventRoles(eventId, {ReactionRole, Qt::DisplayRole}); refreshEventRoles(eventId, {ReactionRole, Qt::DisplayRole});
}); });
connect(m_currentRoom, &Room::changed, this, [this]() {
for (auto it = m_currentRoom->messageEvents().rbegin(); it != m_currentRoom->messageEvents().rend(); ++it) {
auto event = it->event();
refreshEventRoles(event->id(), {ReadMarkersRole, ReadMarkersStringRole});
}
});
connect(m_currentRoom, &Room::newFileTransfer, this, &MessageEventModel::refreshEvent); connect(m_currentRoom, &Room::newFileTransfer, this, &MessageEventModel::refreshEvent);
connect(m_currentRoom, &Room::fileTransferProgress, this, &MessageEventModel::refreshEvent); connect(m_currentRoom, &Room::fileTransferProgress, this, &MessageEventModel::refreshEvent);
connect(m_currentRoom, &Room::fileTransferCompleted, this, &MessageEventModel::refreshEvent); connect(m_currentRoom, &Room::fileTransferCompleted, this, &MessageEventModel::refreshEvent);
@@ -577,7 +569,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
} }
if (role == HighlightRole) { if (role == HighlightRole) {
return !m_currentRoom->isDirectChat() && m_currentRoom->isEventHighlighted(&evt); return m_currentRoom->isEventHighlighted(&evt);
} }
if (role == FileMimetypeIcon) { if (role == FileMimetypeIcon) {
@@ -605,25 +597,12 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
if (role == SpecialMarksRole) { if (role == SpecialMarksRole) {
if (isPending) { if (isPending) {
// A pending event with an m.new_content key will be merged into the
// original event so don't show.
if (evt.contentJson().contains("m.new_content")) {
return EventStatus::Hidden;
}
return pendingIt->deliveryStatus(); return pendingIt->deliveryStatus();
} }
if (evt.isStateEvent() && !NeoChatConfig::self()->showStateEvent()) { auto *memberEvent = timelineIt->viewAs<RoomMemberEvent>();
return EventStatus::Hidden; if (memberEvent) {
} if ((memberEvent->isJoin() || memberEvent->isLeave()) && !NeoChatConfig::self()->showLeaveJoinEvent()) {
if (auto roomMemberEvent = eventCast<const RoomMemberEvent>(&evt)) {
if ((roomMemberEvent->isJoin() || roomMemberEvent->isLeave()) && !NeoChatConfig::self()->showLeaveJoinEvent()) {
return EventStatus::Hidden;
} else if (roomMemberEvent->isRename() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave() && !NeoChatConfig::self()->showRename()) {
return EventStatus::Hidden;
} else if (roomMemberEvent->isAvatarUpdate() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave()
&& !NeoChatConfig::self()->showAvatarUpdate()) {
return EventStatus::Hidden; return EventStatus::Hidden;
} }
} }
@@ -810,65 +789,6 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
return false; return false;
} }
if (role == ReadMarkersRole) {
#ifdef QUOTIENT_07
auto userIds = room()->userIdsAtEvent(evt.id());
userIds.remove(m_currentRoom->localUser()->id());
#else
auto userIds = room()->usersAtEventId(evt.id());
userIds.removeAll(m_currentRoom->localUser());
#endif
QVariantList users;
users.reserve(userIds.size());
for (const auto &userId : userIds) {
#ifdef QUOTIENT_07
auto user = static_cast<NeoChatUser *>(m_currentRoom->user(userId));
#else
auto user = static_cast<NeoChatUser *>(userId);
#endif
users += userAtEvent(user, m_currentRoom, evt);
}
return users;
}
if (role == ReadMarkersStringRole) {
#ifdef QUOTIENT_07
auto userIds = room()->userIdsAtEvent(evt.id());
userIds.remove(m_currentRoom->localUser()->id());
#else
auto userIds = room()->usersAtEventId(evt.id());
userIds.removeAll(m_currentRoom->localUser());
#endif
/**
* The string ends up in the form
* "x users: user1DisplayName, user2DisplayName, etc."
*/
QString readMarkersString = i18np("1 user: ", "%1 users: ", userIds.size());
for (const auto &userId : userIds) {
#ifdef QUOTIENT_07
auto user = static_cast<NeoChatUser *>(m_currentRoom->user(userId));
#else
auto user = static_cast<NeoChatUser *>(userId);
#endif
readMarkersString += user->displayname(m_currentRoom) + i18nc("list separator", ", ");
}
readMarkersString.chop(2);
return readMarkersString;
}
if (role == ShowReadMarkersRole) {
#ifdef QUOTIENT_07
auto userIds = room()->userIdsAtEvent(evt.id());
userIds.remove(m_currentRoom->localUser()->id());
#else
auto userIds = room()->usersAtEventId(evt.id());
userIds.removeAll(m_currentRoom->localUser());
#endif
return userIds.size() > 0;
}
if (role == ReactionRole) { if (role == ReactionRole) {
const auto &annotations = m_currentRoom->relatedEvents(evt, EventRelation::Annotation()); const auto &annotations = m_currentRoom->relatedEvents(evt, EventRelation::Annotation());
if (annotations.isEmpty()) { if (annotations.isEmpty()) {
@@ -969,12 +889,23 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
} }
} }
if (role == IsRedactedRole) { if (role == IsNameChangeRole) {
return evt.isRedacted(); auto roomMemberEvent = eventCast<const RoomMemberEvent>(&evt);
if (roomMemberEvent) {
return roomMemberEvent->isRename();
}
return false;
} }
if (role == IsPendingRole) { if (role == IsAvatarChangeRole) {
return row < m_currentRoom->pendingEvents().size(); auto roomMemberEvent = eventCast<const RoomMemberEvent>(&evt);
if (roomMemberEvent) {
return roomMemberEvent->isAvatarUpdate();
}
return false;
}
if (role == IsRedactedRole) {
return evt.isRedacted();
} }
return {}; return {};
@@ -1044,9 +975,6 @@ QVariant MessageEventModel::getLatestMessageFromIndex(const int baseline)
for (auto it = timelineBottom; it != limit; ++it) { for (auto it = timelineBottom; it != limit; ++it) {
auto evt = it->event(); auto evt = it->event();
auto e = eventCast<const RoomMessageEvent>(evt); auto e = eventCast<const RoomMessageEvent>(evt);
if (!e) {
continue;
}
auto content = (*it)->contentJson(); auto content = (*it)->contentJson();

View File

@@ -57,9 +57,6 @@ public:
ShowAuthorRole, ShowAuthorRole,
ShowSectionRole, ShowSectionRole,
ReadMarkersRole, /**< QVariantList of users at the event for read marker tracking. */
ReadMarkersStringRole, /**< QString with the display name and mxID of the users at the event. */
ShowReadMarkersRole, /**< bool with whether there are any other user read markers to be shown. */
ReactionRole, ReactionRole,
IsEditedRole, IsEditedRole,
@@ -73,8 +70,9 @@ public:
DisplayNameForInitialsRole, DisplayNameForInitialsRole,
// The displayname for the event's sender; for name change events, the old displayname // The displayname for the event's sender; for name change events, the old displayname
AuthorDisplayNameRole, AuthorDisplayNameRole,
IsNameChangeRole,
IsAvatarChangeRole,
IsRedactedRole, IsRedactedRole,
IsPendingRole,
LastRole, // Keep this last LastRole, // Keep this last
}; };
Q_ENUM(EventRoles) Q_ENUM(EventRoles)

View File

@@ -11,20 +11,23 @@ using namespace Quotient;
MessageFilterModel::MessageFilterModel(QObject *parent) MessageFilterModel::MessageFilterModel(QObject *parent)
: QSortFilterProxyModel(parent) : QSortFilterProxyModel(parent)
{ {
connect(NeoChatConfig::self(), &NeoChatConfig::ShowStateEventChanged, this, [this] {
invalidateFilter();
});
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLeaveJoinEventChanged, this, [this] { connect(NeoChatConfig::self(), &NeoChatConfig::ShowLeaveJoinEventChanged, this, [this] {
invalidateFilter(); beginResetModel();
endResetModel();
}); });
connect(NeoChatConfig::self(), &NeoChatConfig::ShowRenameChanged, this, [this] { connect(NeoChatConfig::self(), &NeoChatConfig::ShowRenameChanged, this, [this] {
invalidateFilter(); beginResetModel();
endResetModel();
}); });
connect(NeoChatConfig::self(), &NeoChatConfig::ShowAvatarUpdateChanged, this, [this] { connect(NeoChatConfig::self(), &NeoChatConfig::ShowAvatarUpdateChanged, this, [this] {
invalidateFilter(); beginResetModel();
endResetModel();
}); });
connect(NeoChatConfig::self(), &NeoChatConfig::ShowDeletedMessagesChanged, this, [this] { connect(NeoChatConfig::self(), &NeoChatConfig::ShowDeletedMessagesChanged, this, [this] {
invalidateFilter(); beginResetModel();
endResetModel();
}); });
} }
@@ -32,11 +35,18 @@ bool MessageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour
{ {
const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
const int specialMarks = index.data(MessageEventModel::SpecialMarksRole).toInt();
if (index.data(MessageEventModel::IsNameChangeRole).toBool() && !NeoChatConfig::self()->showRename()) {
return false;
}
if (index.data(MessageEventModel::IsAvatarChangeRole).toBool() && !NeoChatConfig::self()->showAvatarUpdate()) {
return false;
}
if (index.data(MessageEventModel::IsRedactedRole).toBool() && !NeoChatConfig::self()->showDeletedMessages()) { if (index.data(MessageEventModel::IsRedactedRole).toBool() && !NeoChatConfig::self()->showDeletedMessages()) {
return false; return false;
} }
const int specialMarks = index.data(MessageEventModel::SpecialMarksRole).toInt();
if (specialMarks == EventStatus::Hidden || specialMarks == EventStatus::Replaced) { if (specialMarks == EventStatus::Hidden || specialMarks == EventStatus::Replaced) {
return false; return false;
} }
@@ -47,5 +57,9 @@ bool MessageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour
return false; return false;
} }
if (!NeoChatConfig::self()->showLeaveJoinEvent() && eventType == MessageEventModel::State) {
return false;
}
return true; return true;
} }

View File

@@ -3,7 +3,6 @@
#include "roomlistmodel.h" #include "roomlistmodel.h"
#include "controller.h"
#include "neochatconfig.h" #include "neochatconfig.h"
#include "neochatroom.h" #include "neochatroom.h"
#include "roommanager.h" #include "roommanager.h"
@@ -144,7 +143,6 @@ void RoomListModel::doAddRoom(Room *r)
m_rooms.append(room); m_rooms.append(room);
connectRoomSignals(room); connectRoomSignals(room);
Q_EMIT roomAdded(room); Q_EMIT roomAdded(room);
Q_EMIT Controller::instance().roomAdded(room);
} else { } else {
qCritical() << "Attempt to add nullptr to the room list"; qCritical() << "Attempt to add nullptr to the room list";
Q_ASSERT(false); Q_ASSERT(false);
@@ -180,12 +178,29 @@ void RoomListModel::connectRoomSignals(NeoChatRoom *room)
#ifndef QUOTIENT_07 #ifndef QUOTIENT_07
connect(room, &Room::notificationCountChanged, this, &RoomListModel::handleNotifications); connect(room, &Room::notificationCountChanged, this, &RoomListModel::handleNotifications);
#endif #endif
connect(room, &Room::highlightCountChanged, this, [this, room] {
if (room->highlightCount() == 0) {
return;
}
if (room->timelineSize() == 0) {
return;
}
auto *lastEvent = room->lastEvent();
#ifndef QUOTIENT_07 if (!lastEvent) {
return;
}
if (!lastEvent->isStateEvent()) {
return;
}
User *sender = room->user(lastEvent->senderId());
if (sender == room->localUser()) {
return;
}
Q_EMIT newHighlight(room->id(), lastEvent->id(), room->displayName(), sender->displayname(), room->eventToString(*lastEvent), room->avatar(128));
});
connect(room, &Room::notificationCountChanged, this, &RoomListModel::refreshNotificationCount); connect(room, &Room::notificationCountChanged, this, &RoomListModel::refreshNotificationCount);
#else
connect(room, &Room::unreadStatsChanged, this, &RoomListModel::refreshNotificationCount);
#endif
} }
#ifndef QUOTIENT_07 #ifndef QUOTIENT_07
@@ -214,13 +229,10 @@ void RoomListModel::handleNotifications()
continue; continue;
} }
oldNotifications += notification["event"].toObject()["event_id"].toString(); oldNotifications += notification["event"].toObject()["event_id"].toString();
auto room = m_connection->room(notification["room_id"].toString()); auto room = m_connection->room(notification["room_id"].toString());
auto currentRoom = RoomManager::instance().currentRoom();
bool roomIsActive = currentRoom && room->id() == currentRoom->id();
// If room exists, room is NOT active OR the application is NOT active, show notification // If room exists, room is NOT active OR the application is NOT active, show notification
if (room && !(roomIsActive && QGuiApplication::applicationState() == Qt::ApplicationActive)) { if (room && !(room->id() == RoomManager::instance().currentRoom()->id() && QGuiApplication::applicationState() == Qt::ApplicationActive)) {
// The room might have been deleted (for example rejected invitation). // The room might have been deleted (for example rejected invitation).
auto sender = room->user(notification["event"].toObject()["sender"].toString()); auto sender = room->user(notification["event"].toObject()["sender"].toString());
@@ -396,7 +408,7 @@ QVariant RoomListModel::data(const QModelIndex &index, int role) const
return m_categoryVisibility.value(data(index, CategoryRole).toInt(), true); return m_categoryVisibility.value(data(index, CategoryRole).toInt(), true);
} }
if (role == SubtitleTextRole) { if (role == SubtitleTextRole) {
return room->lastEventToString(Qt::PlainText, true); return room->subtitleText();
} }
if (role == AvatarImageRole) { if (role == AvatarImageRole) {
return room->avatar(128); return room->avatar(128);

View File

@@ -116,4 +116,5 @@ Q_SIGNALS:
void notificationCountChanged(); void notificationCountChanged();
void roomAdded(NeoChatRoom *_t1); void roomAdded(NeoChatRoom *_t1);
void newHighlight(const QString &_t1, const QString &_t2, const QString &_t3, const QString &_t4, const QString &_t5, const QImage &_t6);
}; };

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
#include "searchmodel.h" #include "searchmodel.h"

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once #pragma once

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "sortfilterroomlistmodel.h" #include "sortfilterroomlistmodel.h"

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
#include "statemodel.h" #include "statemodel.h"

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once #pragma once

View File

@@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Kitsune Ral <Kitsune-Ral@users.sf.net> // SPDX-FileCopyrightText: Kitsune Ral <Kitsune-Ral@users.sf.net>
// SPDX-FileCopyrightText: Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.1-or-later // SPDX-License-Identifier: LGPL-2.1-or-later
#include "neochataccountregistry.h" #include "neochataccountregistry.h"

View File

@@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: 2020 Kitsune Ral <Kitsune-Ral@users.sf.net> // SPDX-FileCopyrightText: 2020 Kitsune Ral <Kitsune-Ral@users.sf.net>
// SPDX-FileCopyrightText: Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.1-or-later // SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once #pragma once

View File

@@ -29,6 +29,10 @@
<label>Merge Room Lists</label> <label>Merge Room Lists</label>
<default>false</default> <default>false</default>
</entry> </entry>
<entry name="ShowLeaveJoinEvent" type="bool">
<label>Show leave and join events in the timeline</label>
<default>true</default>
</entry>
<entry name="AllowQuickEdit" type="bool"> <entry name="AllowQuickEdit" type="bool">
<label>Use s/text/replacement syntax to edit your last message.</label> <label>Use s/text/replacement syntax to edit your last message.</label>
<default>false</default> <default>false</default>
@@ -37,6 +41,9 @@
<label>"Show your messages on the right</label> <label>"Show your messages on the right</label>
<default>true</default> <default>true</default>
</entry> </entry>
<entry name="RoomListPageWidth" type="int">
<default>-1</default>
</entry>
<entry name="RoomDrawerWidth" type="int"> <entry name="RoomDrawerWidth" type="int">
<default>-1</default> <default>-1</default>
</entry> </entry>
@@ -68,14 +75,6 @@
<label>Use a compact room list layout</label> <label>Use a compact room list layout</label>
<default>false</default> <default>false</default>
</entry> </entry>
<entry name="ShowStateEvent" type="bool">
<label>Show state events in the timeline</label>
<default>true</default>
</entry>
<entry name="ShowLeaveJoinEvent" type="bool">
<label>Show leave and join events in the timeline</label>
<default>true</default>
</entry>
<entry name="ShowRename" type="bool"> <entry name="ShowRename" type="bool">
<label>Show rename events in the timeline</label> <label>Show rename events in the timeline</label>
<default>true</default> <default>true</default>
@@ -109,10 +108,6 @@
<label>Show avatar in the room drawer</label> <label>Show avatar in the room drawer</label>
<default>true</default> <default>true</default>
</entry> </entry>
<entry name="Collapsed" type="bool">
<label>Save the collapsed state of the room list</label>
<default>false</default>
</entry>
</group> </group>
<group name="NetworkProxy"> <group name="NetworkProxy">
<entry name="ProxyType" type="Enum"> <entry name="ProxyType" type="Enum">

View File

@@ -13,11 +13,9 @@
#include <QMediaMetaData> #include <QMediaMetaData>
#include <QMediaPlayer> #include <QMediaPlayer>
#include <jobs/basejob.h>
#include <qcoro/qcorosignal.h> #include <qcoro/qcorosignal.h>
#include <connection.h> #include <connection.h>
#include <csapi/account-data.h>
#include <csapi/directory.h> #include <csapi/directory.h>
#include <csapi/pushrules.h> #include <csapi/pushrules.h>
#include <csapi/redaction.h> #include <csapi/redaction.h>
@@ -49,7 +47,7 @@
#endif #endif
#include "filetransferpseudojob.h" #include "filetransferpseudojob.h"
#include "stickerevent.h" #include "stickerevent.h"
#include "texthandler.h" #include "utils.h"
#ifndef Q_OS_ANDROID #ifndef Q_OS_ANDROID
#include <KIO/Job> #include <KIO/Job>
@@ -100,14 +98,6 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
Q_EMIT canEncryptRoomChanged(); Q_EMIT canEncryptRoomChanged();
}); });
connect(connection, &Connection::capabilitiesLoaded, this, &NeoChatRoom::maxRoomVersionChanged); connect(connection, &Connection::capabilitiesLoaded, this, &NeoChatRoom::maxRoomVersionChanged);
connect(this, &Room::changed, this, [this]() {
Q_EMIT defaultUrlPreviewStateChanged();
});
connect(this, &Room::accountDataChanged, this, [this](QString type) {
if (type == "org.matrix.room.preview_urls") {
Q_EMIT urlPreviewEnabledChanged();
}
});
} }
void NeoChatRoom::uploadFile(const QUrl &url, const QString &body) void NeoChatRoom::uploadFile(const QUrl &url, const QString &body)
@@ -215,7 +205,7 @@ void NeoChatRoom::sendTypingNotification(bool isTyping)
connection()->callApi<SetTypingJob>(BackgroundRequest, localUser()->id(), id(), isTyping, 10000); connection()->callApi<SetTypingJob>(BackgroundRequest, localUser()->id(), id(), isTyping, 10000);
} }
const RoomEvent *NeoChatRoom::lastEvent() const const RoomEvent *NeoChatRoom::lastEvent(bool ignoreStateEvent) const
{ {
for (auto timelineItem = messageEvents().rbegin(); timelineItem < messageEvents().rend(); timelineItem++) { for (auto timelineItem = messageEvents().rbegin(); timelineItem < messageEvents().rend(); timelineItem++) {
const RoomEvent *event = timelineItem->get(); const RoomEvent *event = timelineItem->get();
@@ -227,21 +217,8 @@ const RoomEvent *NeoChatRoom::lastEvent() const
continue; continue;
} }
if (event->isStateEvent() && !NeoChatConfig::self()->showStateEvent()) { if (event->isStateEvent()
continue; && (ignoreStateEvent || !NeoChatConfig::self()->showLeaveJoinEvent() || static_cast<const StateEventBase &>(*event).repeatsState())) {
}
if (auto roomMemberEvent = eventCast<const RoomMemberEvent>(event)) {
if ((roomMemberEvent->isJoin() || roomMemberEvent->isLeave()) && !NeoChatConfig::self()->showLeaveJoinEvent()) {
continue;
} else if (roomMemberEvent->isRename() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave() && !NeoChatConfig::self()->showRename()) {
continue;
} else if (roomMemberEvent->isAvatarUpdate() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave()
&& !NeoChatConfig::self()->showAvatarUpdate()) {
continue;
}
}
if (event->isStateEvent() && static_cast<const StateEventBase &>(*event).repeatsState()) {
continue; continue;
} }
@@ -255,14 +232,6 @@ const RoomEvent *NeoChatRoom::lastEvent() const
continue; continue;
} }
#ifdef QUOTIENT_07
if (auto lastEvent = eventCast<const StateEvent>(event)) {
#else
if (auto lastEvent = eventCast<const StateEventBase>(event)) {
#endif
return lastEvent;
}
if (auto lastEvent = eventCast<const RoomMessageEvent>(event)) { if (auto lastEvent = eventCast<const RoomMessageEvent>(event)) {
return lastEvent; return lastEvent;
} }
@@ -288,11 +257,10 @@ bool NeoChatRoom::lastEventIsSpoiler() const
return false; return false;
} }
QString NeoChatRoom::lastEventToString(Qt::TextFormat format, bool stripNewlines) const QString NeoChatRoom::lastEventToString() const
{ {
if (auto event = lastEvent()) { if (auto event = lastEvent()) {
return roomMembername(event->senderId()) + (event->isStateEvent() ? QLatin1String(" ") : QLatin1String(": ")) return roomMembername(event->senderId()) + (event->isStateEvent() ? " " : ": ") + eventToString(*event);
+ eventToString(*event, format, stripNewlines);
} }
return QLatin1String(""); return QLatin1String("");
} }
@@ -353,7 +321,7 @@ QDateTime NeoChatRoom::lastActiveTime()
return QDateTime(); return QDateTime();
} }
if (auto event = lastEvent()) { if (auto event = lastEvent(true)) {
return event->originTimestamp(); return event->originTimestamp();
} }
@@ -361,6 +329,45 @@ QDateTime NeoChatRoom::lastActiveTime()
return messageEvents().rbegin()->get()->originTimestamp(); return messageEvents().rbegin()->get()->originTimestamp();
} }
QString NeoChatRoom::subtitleText()
{
static const QRegularExpression blockquote("(\r\n\t|\n|\r\t|)> ");
static const QRegularExpression heading("(\r\n\t|\n|\r\t|)\\#{1,6} ");
static const QRegularExpression newlines("(\r\n\t|\n|\r\t|\r\n)");
static const QRegularExpression bold1("(\\*\\*|__)(?=\\S)([^\\r]*\\S)\\1");
static const QRegularExpression bold2("(\\*|_)(?=\\S)([^\\r]*\\S)\\1");
static const QRegularExpression strike1("~~(.*)~~");
static const QRegularExpression strike2("~(.*)~");
static const QRegularExpression del("<del>(.*)</del>");
static const QRegularExpression multileLineCode("```([^```]+)```");
static const QRegularExpression singleLinecode("`([^`]+)`");
QString subtitle = lastEventToString().size() == 0 ? topic() : lastEventToString();
subtitle
// replace blockquote, i.e. '> text'
.replace(blockquote, " ")
// replace headings, i.e. "# text"
.replace(heading, " ")
// replace newlines
.replace(newlines, " ")
// replace '**text**' and '__text__'
.replace(bold1, "\\2")
// replace '*text*' and '_text_'
.replace(bold2, "\\2")
// replace '~~text~~'
.replace(strike1, "\\1")
// replace '~text~'
.replace(strike2, "\\1")
// replace '<del>text</del>'
.replace(del, "\\1")
// replace '```code```'
.replace(multileLineCode, "\\1")
// replace '`code`'
.replace(singleLinecode, "\\1");
return subtitle.size() > 0 ? subtitle : QStringLiteral(" ");
}
int NeoChatRoom::savedTopVisibleIndex() const int NeoChatRoom::savedTopVisibleIndex() const
{ {
return firstDisplayedMarker() == historyEdge() ? 0 : int(firstDisplayedMarker() - messageEvents().rbegin()); return firstDisplayedMarker() == historyEdge() ? 0 : int(firstDisplayedMarker() - messageEvents().rbegin());
@@ -444,7 +451,7 @@ QString NeoChatRoom::avatarMediaId() const
return {}; return {};
} }
QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format, bool stripNewlines) const QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format, bool removeReply) const
{ {
const bool prettyPrint = (format == Qt::RichText); const bool prettyPrint = (format == Qt::RichText);
@@ -455,48 +462,58 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format,
return visit( return visit(
#endif #endif
evt, evt,
[this, format, stripNewlines](const RoomMessageEvent &e) { [this, prettyPrint, removeReply](const RoomMessageEvent &e) {
using namespace MessageEventContent; using namespace MessageEventContent;
TextHandler textHandler; // 1. prettyPrint/HTML
if (prettyPrint && e.hasTextContent() && e.mimeType().name() != "text/plain") {
auto htmlBody = static_cast<const TextContent *>(e.content())->body;
if (removeReply) {
htmlBody.remove(utils::removeRichReplyRegex);
}
htmlBody.replace(utils::userPillRegExp, R"(<b class="user-pill">\1</b>)");
htmlBody.replace(utils::strikethroughRegExp, "<s>\\1</s>");
auto url = connection()->homeserver();
auto base = url.scheme() + QStringLiteral("://") + url.host() + (url.port() != -1 ? ':' + QString::number(url.port()) : QString());
htmlBody.replace(utils::mxcImageRegExp, QStringLiteral(R"(<img \1 src="%1/_matrix/media/r0/download/\2/\3" \4 > )").arg(base));
return htmlBody;
}
if (e.hasFileContent()) { if (e.hasFileContent()) {
auto fileCaption = e.content()->fileInfo()->originalName; auto fileCaption = e.content()->fileInfo()->originalName.toHtmlEscaped();
if (fileCaption.isEmpty()) { if (fileCaption.isEmpty()) {
fileCaption = e.plainBody(); fileCaption = prettyPrint ? Quotient::prettyPrint(e.plainBody()) : e.plainBody();
} else if (e.content()->fileInfo()->originalName != e.plainBody()) { } else if (e.content()->fileInfo()->originalName != e.plainBody()) {
fileCaption = e.plainBody() + " | " + fileCaption; fileCaption = e.plainBody() + " | " + fileCaption;
} }
textHandler.setData(fileCaption); return !fileCaption.isEmpty() ? fileCaption : i18n("a file");
return !fileCaption.isEmpty() ? textHandler.handleRecievePlainText(Qt::PlainText, stripNewlines) : i18n("a file");
} }
QString body; // 2. prettyPrint/text 3. plainText/HTML 4. plainText/text
if (e.hasTextContent() && e.content()) { QString plainBody;
body = static_cast<const TextContent *>(e.content())->body; if (e.hasTextContent() && e.content() && e.mimeType().name() == "text/plain") { // 2/4
} else { plainBody = static_cast<const TextContent *>(e.content())->body;
body = e.plainBody(); } else { // 3
plainBody = e.plainBody();
} }
textHandler.setData(body); if (prettyPrint) {
if (removeReply) {
Qt::TextFormat inputFormat; plainBody.remove(utils::removeReplyRegex);
if (e.mimeType().name() == "text/plain") { }
inputFormat = Qt::PlainText; return Quotient::prettyPrint(plainBody);
} else {
inputFormat = Qt::RichText;
} }
if (removeReply) {
if (format == Qt::RichText) { return plainBody.remove(utils::removeReplyRegex);
return textHandler.handleRecieveRichText(inputFormat, this, &e, stripNewlines);
} else {
return textHandler.handleRecievePlainText(inputFormat, stripNewlines);
} }
return plainBody;
}, },
[](const StickerEvent &e) { [](const StickerEvent &e) {
return e.body(); return e.body();
}, },
[this, prettyPrint](const RoomMemberEvent &e) { [this](const RoomMemberEvent &e) {
// FIXME: Rewind to the name that was at the time of this event // FIXME: Rewind to the name that was at the time of this event
auto subjectName = this->htmlSafeMemberName(e.userId()); auto subjectName = this->htmlSafeMemberName(e.userId());
if (e.membership() == MembershipType::Leave) { if (e.membership() == MembershipType::Leave) {
@@ -509,11 +526,8 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format,
#endif #endif
} }
} }
subjectName = QStringLiteral("<a href=\"https://matrix.to/#/%1\" style=\"color: %2\">%3</a>")
if (prettyPrint) { .arg(e.userId(), static_cast<NeoChatUser *>(user(e.userId()))->color().name(), subjectName);
subjectName = QStringLiteral("<a href=\"https://matrix.to/#/%1\" style=\"color: %2\">%3</a>")
.arg(e.userId(), static_cast<NeoChatUser *>(user(e.userId()))->color().name(), subjectName);
}
// The below code assumes senderName output in AuthorRole // The below code assumes senderName output in AuthorRole
switch (e.membership()) { switch (e.membership()) {
@@ -604,12 +618,8 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format,
[](const RoomNameEvent &e) { [](const RoomNameEvent &e) {
return (e.name().isEmpty()) ? i18n("cleared the room name") : i18n("set the room name to: %1", e.name().toHtmlEscaped()); return (e.name().isEmpty()) ? i18n("cleared the room name") : i18n("set the room name to: %1", e.name().toHtmlEscaped());
}, },
[prettyPrint, stripNewlines](const RoomTopicEvent &e) { [prettyPrint](const RoomTopicEvent &e) {
return (e.topic().isEmpty()) ? i18n("cleared the topic") return (e.topic().isEmpty()) ? i18n("cleared the topic") : i18n("set the topic to: %1", prettyPrint ? Quotient::prettyPrint(e.topic()) : e.topic());
: i18n("set the topic to: %1",
prettyPrint ? Quotient::prettyPrint(e.topic())
: stripNewlines ? e.topic().replace(u'\n', u' ')
: e.topic());
}, },
[](const RoomAvatarEvent &) { [](const RoomAvatarEvent &) {
return i18n("changed the room avatar"); return i18n("changed the room avatar");
@@ -975,7 +985,7 @@ bool NeoChatRoom::canSendState(const QString &eventType) const
auto currentPl = plEvent->powerLevelForUser(localUser()->id()); auto currentPl = plEvent->powerLevelForUser(localUser()->id());
#ifndef QUOTIENT_07 #ifndef QUOTIENT_07
if (eventType == "m.room.history_visibility" || eventType == "org.matrix.room.preview_urls") { if (eventType == "m.room.history_visibility") {
return false; return false;
} else { } else {
return currentPl >= pl; return currentPl >= pl;
@@ -1065,91 +1075,6 @@ void NeoChatRoom::setHistoryVisibility(const QString &historyVisibilityRule)
// Not emitting historyVisibilityChanged() here, since that would override the change in the UI with the *current* value, which is not the *new* value. // Not emitting historyVisibilityChanged() here, since that would override the change in the UI with the *current* value, which is not the *new* value.
} }
bool NeoChatRoom::defaultUrlPreviewState() const
{
#ifdef QUOTIENT_07
auto urlPreviewsDisabled = currentState().get("org.matrix.room.preview_urls");
#else
auto urlPreviewsDisabled = getCurrentState("org.matrix.room.preview_urls");
#endif
// Some rooms will not have this state event set so check for a nullptr return.
if (urlPreviewsDisabled != nullptr) {
return !urlPreviewsDisabled->contentJson()["disable"].toBool();
} else {
return false;
}
}
void NeoChatRoom::setDefaultUrlPreviewState(const bool &defaultUrlPreviewState)
{
if (!canSendState("org.matrix.room.preview_urls")) {
qWarning() << "Power level too low to set the default URL preview state for the room";
return;
}
/**
* Note the org.matrix.room.preview_urls room state event is completely undocumented
* so here it is because I'm nice.
*
* Also note this is a different event to org.matrix.room.preview_urls for room
* account data, because even though it has the same name and content it's totally different.
*
* {
* "content": {
* "disable": false
* },
* "origin_server_ts": 1673115224071,
* "sender": "@bob:kde.org",
* "state_key": "",
* "type": "org.matrix.room.preview_urls",
* "unsigned": {
* "replaces_state": "replaced_event_id",
* "prev_content": {
* "disable": true
* },
* "prev_sender": "@jeff:kde.org",
* "age": 99
* },
* "event_id": "$event_id",
* "room_id": "!room_id:kde.org"
* }
*
* You just have to set disable to true to disable URL previews by default.
*/
#ifdef QUOTIENT_07
setState("org.matrix.room.preview_urls", "", QJsonObject{{"disable", !defaultUrlPreviewState}});
#else
qWarning() << "Quotient 0.7 required to set room default url preview setting";
return;
#endif
}
bool NeoChatRoom::urlPreviewEnabled() const
{
if (hasAccountData("org.matrix.room.preview_urls")) {
return !accountData("org.matrix.room.preview_urls")->contentJson()["disable"].toBool();
} else {
return defaultUrlPreviewState();
}
}
void NeoChatRoom::setUrlPreviewEnabled(const bool &urlPreviewEnabled)
{
/**
* Once again this is undocumented and even though the name and content are the
* same this is a different event to the org.matrix.room.preview_urls room state event.
*
* {
* "content": {
* "disable": true
* }
* "type": "org.matrix.room.preview_urls",
* }
*/
connection()->callApi<SetAccountDataPerRoomJob>(localUser()->id(), id(), "org.matrix.room.preview_urls", QJsonObject{{"disable", !urlPreviewEnabled}});
}
void NeoChatRoom::setUserPowerLevel(const QString &userID, const int &powerLevel) void NeoChatRoom::setUserPowerLevel(const QString &userID, const int &powerLevel)
{ {
if (joinedCount() <= 1) { if (joinedCount() <= 1) {
@@ -1189,12 +1114,6 @@ void NeoChatRoom::setUserPowerLevel(const QString &userID, const int &powerLevel
} }
} }
int NeoChatRoom::getUserPowerLevel(const QString &userId) const
{
auto powerLevelEvent = getCurrentState<RoomPowerLevelsEvent>();
return powerLevelEvent->powerLevelForUser(userId);
}
int NeoChatRoom::powerLevel(const QString &eventName, const bool &isStateEvent) const int NeoChatRoom::powerLevel(const QString &eventName, const bool &isStateEvent) const
{ {
#ifdef QUOTIENT_07 #ifdef QUOTIENT_07
@@ -1910,7 +1829,3 @@ int NeoChatRoom::maxRoomVersion() const
} }
return maxVersion; return maxVersion;
} }
NeoChatUser *NeoChatRoom::directChatRemoteUser() const
{
return dynamic_cast<NeoChatUser *>(connection()->directChatUsers(this)[0]);
}

View File

@@ -52,19 +52,6 @@ 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)
Q_PROPERTY(QString historyVisibility READ historyVisibility WRITE setHistoryVisibility NOTIFY historyVisibilityChanged) Q_PROPERTY(QString historyVisibility READ historyVisibility WRITE setHistoryVisibility NOTIFY historyVisibilityChanged)
/**
* @brief Set the default URL preview state for room members.
*
* Assumed false if the org.matrix.room.preview_urls state message has never been
* set. Can only be set if the calling user has a high enough power level.
*/
Q_PROPERTY(bool defaultUrlPreviewState READ defaultUrlPreviewState WRITE setDefaultUrlPreviewState NOTIFY defaultUrlPreviewStateChanged)
/**
* @brief Enable URL previews for the local user.
*/
Q_PROPERTY(bool urlPreviewEnabled READ urlPreviewEnabled WRITE setUrlPreviewEnabled NOTIFY urlPreviewEnabledChanged)
// Properties for the various permission levels for the room // Properties for the various permission levels for the room
Q_PROPERTY(int defaultUserPowerLevel READ defaultUserPowerLevel WRITE setDefaultUserPowerLevel NOTIFY defaultUserPowerLevelChanged) Q_PROPERTY(int defaultUserPowerLevel READ defaultUserPowerLevel WRITE setDefaultUserPowerLevel NOTIFY defaultUserPowerLevelChanged)
Q_PROPERTY(int invitePowerLevel READ invitePowerLevel WRITE setInvitePowerLevel NOTIFY invitePowerLevelChanged) Q_PROPERTY(int invitePowerLevel READ invitePowerLevel WRITE setInvitePowerLevel NOTIFY invitePowerLevelChanged)
@@ -112,7 +99,6 @@ class NeoChatRoom : public Quotient::Room
* Only returns main integer room versions (i.e. no msc room versions). * Only returns main integer room versions (i.e. no msc room versions).
*/ */
Q_PROPERTY(int maxRoomVersion READ maxRoomVersion NOTIFY maxRoomVersionChanged) Q_PROPERTY(int maxRoomVersion READ maxRoomVersion NOTIFY maxRoomVersionChanged)
Q_PROPERTY(NeoChatUser *directChatRemoteUser READ directChatRemoteUser CONSTANT)
public: public:
enum MessageType { enum MessageType {
@@ -131,13 +117,13 @@ public:
/// This function respect the showLeaveJoinEvent setting and discard /// This function respect the showLeaveJoinEvent setting and discard
/// other not interesting events. This function can return an empty pointer /// other not interesting events. This function can return an empty pointer
/// when the room is empty of RoomMessageEvent. /// when the room is empty of RoomMessageEvent.
[[nodiscard]] const Quotient::RoomEvent *lastEvent() const; [[nodiscard]] const Quotient::RoomEvent *lastEvent(bool ignoreStateEvent = false) const;
/// Convenient way to get the last event but in a string format. /// Convenient way to get the last event but in a string format.
/// ///
/// \see lastEvent /// \see lastEvent
/// \see lastEventIsSpoiler /// \see lastEventIsSpoiler
[[nodiscard]] QString lastEventToString(Qt::TextFormat format = Qt::PlainText, bool stripNewlines = false) const; [[nodiscard]] QString lastEventToString() const;
/// Convenient way to check if the last event looks like it has spoilers. /// Convenient way to check if the last event looks like it has spoilers.
/// ///
@@ -150,6 +136,12 @@ public:
/// \see lastEvent /// \see lastEvent
[[nodiscard]] QDateTime lastActiveTime(); [[nodiscard]] QDateTime lastActiveTime();
/// Get subtitle text for room
///
/// Fetches last event and removes markdown formatting
/// \see lastEventToString
[[nodiscard]] QString subtitleText();
[[nodiscard]] bool isSpace(); [[nodiscard]] bool isSpace();
bool isEventHighlighted(const Quotient::RoomEvent *e) const; bool isEventHighlighted(const Quotient::RoomEvent *e) const;
@@ -160,20 +152,6 @@ public:
[[nodiscard]] QString historyVisibility() const; [[nodiscard]] QString historyVisibility() const;
void setHistoryVisibility(const QString &historyVisibilityRule); void setHistoryVisibility(const QString &historyVisibilityRule);
[[nodiscard]] bool defaultUrlPreviewState() const;
void setDefaultUrlPreviewState(const bool &defaultUrlPreviewState);
[[nodiscard]] bool urlPreviewEnabled() const;
void setUrlPreviewEnabled(const bool &urlPreviewEnabled);
/**
* @brief Get the power level for the given user ID in the room.
*
* Returns the default value for a user in the room if they have no escalated
* privileges or if they are not a member so membership should be known before using.
*/
Q_INVOKABLE [[nodiscard]] int getUserPowerLevel(const QString &userId) const;
Q_INVOKABLE void setUserPowerLevel(const QString &userID, const int &powerLevel); Q_INVOKABLE void setUserPowerLevel(const QString &userID, const int &powerLevel);
[[nodiscard]] int powerLevel(const QString &eventName, const bool &isStateEvent = false) const; [[nodiscard]] int powerLevel(const QString &eventName, const bool &isStateEvent = false) const;
@@ -275,7 +253,7 @@ public:
[[nodiscard]] QString avatarMediaId() const; [[nodiscard]] QString avatarMediaId() const;
[[nodiscard]] QString eventToString(const Quotient::RoomEvent &evt, Qt::TextFormat format = Qt::PlainText, bool stripNewlines = false) const; [[nodiscard]] QString eventToString(const Quotient::RoomEvent &evt, Qt::TextFormat format = Qt::PlainText, bool removeReply = true) const;
[[nodiscard]] QString eventToGenericString(const Quotient::RoomEvent &evt) const; [[nodiscard]] QString eventToGenericString(const Quotient::RoomEvent &evt) const;
Q_INVOKABLE [[nodiscard]] bool containsUser(const QString &userID) const; Q_INVOKABLE [[nodiscard]] bool containsUser(const QString &userID) const;
@@ -352,7 +330,6 @@ public:
#endif #endif
int maxRoomVersion() const; int maxRoomVersion() const;
NeoChatUser *directChatRemoteUser() const;
private: private:
QSet<const Quotient::RoomEvent *> highlights; QSet<const Quotient::RoomEvent *> highlights;
@@ -408,8 +385,6 @@ Q_SIGNALS:
void canEncryptRoomChanged(); void canEncryptRoomChanged();
void joinRuleChanged(); void joinRuleChanged();
void historyVisibilityChanged(); void historyVisibilityChanged();
void defaultUrlPreviewStateChanged();
void urlPreviewEnabledChanged();
void maxRoomVersionChanged(); void maxRoomVersionChanged();
void defaultUserPowerLevelChanged(); void defaultUserPowerLevelChanged();
void invitePowerLevelChanged(); void invitePowerLevelChanged();

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "notificationsmanager.h" #include "notificationsmanager.h"
@@ -22,11 +22,11 @@
#include <jobs/basejob.h> #include <jobs/basejob.h>
#include <user.h> #include <user.h>
#include "actionshandler.h"
#include "controller.h" #include "controller.h"
#include "neochatconfig.h" #include "neochatconfig.h"
#include "neochatroom.h" #include "neochatroom.h"
#include "roommanager.h" #include "roommanager.h"
#include "texthandler.h"
#include "windowcontroller.h" #include "windowcontroller.h"
using namespace Quotient; using namespace Quotient;
@@ -85,9 +85,7 @@ void NotificationsManager::postNotification(NeoChatRoom *room,
std::unique_ptr<KNotificationReplyAction> replyAction(new KNotificationReplyAction(i18n("Reply"))); std::unique_ptr<KNotificationReplyAction> replyAction(new KNotificationReplyAction(i18n("Reply")));
replyAction->setPlaceholderText(i18n("Reply...")); replyAction->setPlaceholderText(i18n("Reply..."));
connect(replyAction.get(), &KNotificationReplyAction::replied, this, [room, replyEventId](const QString &text) { connect(replyAction.get(), &KNotificationReplyAction::replied, this, [room, replyEventId](const QString &text) {
TextHandler textHandler; room->postMessage(text, markdownToHTML(text), RoomMessageEvent::MsgType::Text, replyEventId, QString());
textHandler.setData(text);
room->postMessage(text, textHandler.handleSendText(), RoomMessageEvent::MsgType::Text, replyEventId, QString());
}); });
notification->setReplyAction(std::move(replyAction)); notification->setReplyAction(std::move(replyAction));
} }

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
#include "pollevent.h" #include "pollevent.h"

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once #pragma once

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
#include "pollhandler.h" #include "pollhandler.h"

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once #pragma once

View File

@@ -143,7 +143,7 @@ QQC2.Control {
Keys.onEnterPressed: { Keys.onEnterPressed: {
if (completionMenu.visible) { if (completionMenu.visible) {
completionMenu.complete() completionMenu.complete()
} else if (event.modifiers & Qt.ShiftModifier || Kirigami.Settings.isMobile) { } else if (event.modifiers & Qt.ShiftModifier) {
textField.insert(cursorPosition, "\n") textField.insert(cursorPosition, "\n")
} else { } else {
chatBar.postMessage(); chatBar.postMessage();
@@ -152,7 +152,7 @@ QQC2.Control {
Keys.onReturnPressed: { Keys.onReturnPressed: {
if (completionMenu.visible) { if (completionMenu.visible) {
completionMenu.complete() completionMenu.complete()
} else if (event.modifiers & Qt.ShiftModifier || Kirigami.Settings.isMobile) { } else if (event.modifiers & Qt.ShiftModifier) {
textField.insert(cursorPosition, "\n") textField.insert(cursorPosition, "\n")
} else { } else {
chatBar.postMessage(); chatBar.postMessage();
@@ -185,13 +185,6 @@ QQC2.Control {
repeatTimer.stop() repeatTimer.stop()
} }
} }
Keys.onShortcutOverride: {
// Accept the event only when there was something to cancel. Otherwise, let the event go to the RoomPage.
if (cancelButton.visible && event.key === Qt.Key_Escape) {
cancelButton.action.trigger();
event.accepted = true;
}
}
Loader { Loader {
id: paneLoader id: paneLoader
@@ -277,6 +270,7 @@ QQC2.Control {
currentRoom.chatBoxAttachmentPath = ""; currentRoom.chatBoxAttachmentPath = "";
root.forceActiveFocus() root.forceActiveFocus()
} }
shortcut: "Escape"
} }
QQC2.ToolTip.text: text QQC2.ToolTip.text: text
QQC2.ToolTip.visible: hovered QQC2.ToolTip.visible: hovered

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15 import QtQuick 2.15

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15 import QtQuick 2.15

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15 import QtQuick 2.15

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