diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index 0a52904af..bea7e4fe5 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -76,3 +76,9 @@ ecm_add_test( LINK_LIBRARIES neochat Qt::Test TEST_NAME reactionmodeltest ) + +ecm_add_test( + linkpreviewertest.cpp + LINK_LIBRARIES neochat Qt::Test + TEST_NAME linkpreviewertest +) diff --git a/autotests/data/test-invalidmatrixtolink-event.json b/autotests/data/test-invalidmatrixtolink-event.json new file mode 100644 index 000000000..c7b27a5a0 --- /dev/null +++ b/autotests/data/test-invalidmatrixtolink-event.json @@ -0,0 +1,14 @@ +{ + "content": { + "body": "https://matrix.to/#/@alice:example.org", + "msgtype": "m.text" + }, + "event_id": "$validlink1:example.org", + "origin_server_ts": 1432735824654, + "room_id": "!test:example.org", + "sender": "@example:example.org", + "type": "m.room.message", + "unsigned": { + "age": 1234 + } +} diff --git a/autotests/data/test-invalidmxclink-event.json b/autotests/data/test-invalidmxclink-event.json new file mode 100644 index 000000000..dfb19dbbc --- /dev/null +++ b/autotests/data/test-invalidmxclink-event.json @@ -0,0 +1,14 @@ +{ + "content": { + "body": "mxc://example.org/SEsfnsuifSDFSSEF", + "msgtype": "m.text" + }, + "event_id": "$validlink1:example.org", + "origin_server_ts": 1432735824654, + "room_id": "!test:example.org", + "sender": "@example:example.org", + "type": "m.room.message", + "unsigned": { + "age": 1234 + } +} diff --git a/autotests/data/test-invalidnospacelink-event.json b/autotests/data/test-invalidnospacelink-event.json new file mode 100644 index 000000000..f4e7523a1 --- /dev/null +++ b/autotests/data/test-invalidnospacelink-event.json @@ -0,0 +1,14 @@ +{ + "content": { + "body": "testhttps://kde.org", + "msgtype": "m.text" + }, + "event_id": "$validlink1:example.org", + "origin_server_ts": 1432735824654, + "room_id": "!test:example.org", + "sender": "@example:example.org", + "type": "m.room.message", + "unsigned": { + "age": 1234 + } +} diff --git a/autotests/data/test-linkpreviewerintial-sync.json b/autotests/data/test-linkpreviewerintial-sync.json new file mode 100644 index 000000000..70366d1dc --- /dev/null +++ b/autotests/data/test-linkpreviewerintial-sync.json @@ -0,0 +1,24 @@ +{ + "timeline": { + "events": [ + { + "content": { + "body": "https://kde.org", + "format": "org.matrix.custom.html", + "formatted_body": "https://kde.org", + "msgtype": "m.text" + }, + "origin_server_ts": 1704648567967, + "sender": "@example:example.org", + "type": "m.room.message", + "unsigned": { + "age": 112 + }, + "event_id": "$validlink:example.org", + "room_id": "!test:example.org" + } + ], + "limited": true, + "prev_batch": "t34-23535_0_0" + } +} diff --git a/autotests/data/test-linkpreviewerreplace-sync.json b/autotests/data/test-linkpreviewerreplace-sync.json new file mode 100644 index 000000000..ffbe8e245 --- /dev/null +++ b/autotests/data/test-linkpreviewerreplace-sync.json @@ -0,0 +1,35 @@ +{ + "timeline": { + "events": [ + { + "content": { + "body": "* ", + "format": "org.matrix.custom.html", + "formatted_body": "no link", + "m.new_content": { + "body": "", + "format": "org.matrix.custom.html", + "formatted_body": "no link", + "msgtype": "m.text" + }, + "m.relates_to": { + "event_id": "$validlink:example.org", + "rel_type": "m.replace" + }, + "msgtype": "m.text", + "type": "m.room.message" + }, + "origin_server_ts": 1704648614969, + "sender": "@example:example.org", + "type": "m.room.message", + "unsigned": { + "age": 65 + }, + "event_id": "$nolink:example.org", + "room_id": "!test:example.org" + } + ], + "limited": true, + "prev_batch": "t34-23535_0_0" + } +} diff --git a/autotests/data/test-multiplelink-event.json b/autotests/data/test-multiplelink-event.json new file mode 100644 index 000000000..73d5c7c15 --- /dev/null +++ b/autotests/data/test-multiplelink-event.json @@ -0,0 +1,14 @@ +{ + "content": { + "body": "www.example.org https://kde.org", + "msgtype": "m.text" + }, + "event_id": "$validlink1:example.org", + "origin_server_ts": 1432735824654, + "room_id": "!test:example.org", + "sender": "@example:example.org", + "type": "m.room.message", + "unsigned": { + "age": 1234 + } +} diff --git a/autotests/data/test-validplainlink-event.json b/autotests/data/test-validplainlink-event.json new file mode 100644 index 000000000..4a326cd11 --- /dev/null +++ b/autotests/data/test-validplainlink-event.json @@ -0,0 +1,14 @@ +{ + "content": { + "body": "https://kde.org", + "msgtype": "m.text" + }, + "event_id": "$validlink1:example.org", + "origin_server_ts": 1432735824654, + "room_id": "!test:example.org", + "sender": "@example:example.org", + "type": "m.room.message", + "unsigned": { + "age": 1234 + } +} diff --git a/autotests/data/test-validplainwwwlink-event.json b/autotests/data/test-validplainwwwlink-event.json new file mode 100644 index 000000000..b586b419f --- /dev/null +++ b/autotests/data/test-validplainwwwlink-event.json @@ -0,0 +1,14 @@ +{ + "content": { + "body": "www.example.org", + "msgtype": "m.text" + }, + "event_id": "$validlink1:example.org", + "origin_server_ts": 1432735824654, + "room_id": "!test:example.org", + "sender": "@example:example.org", + "type": "m.room.message", + "unsigned": { + "age": 1234 + } +} diff --git a/autotests/data/test-validrichlink-event.json b/autotests/data/test-validrichlink-event.json new file mode 100644 index 000000000..c8e793b80 --- /dev/null +++ b/autotests/data/test-validrichlink-event.json @@ -0,0 +1,16 @@ +{ + "content": { + "body": "[Rich Link](https://kde.org)", + "format": "org.matrix.custom.html", + "formatted_body": "Rich Link", + "msgtype": "m.text" + }, + "event_id": "$validlink1:example.org", + "origin_server_ts": 1432735824654, + "room_id": "!test:example.org", + "sender": "@example:example.org", + "type": "m.room.message", + "unsigned": { + "age": 1234 + } +} diff --git a/autotests/eventhandlertest.cpp b/autotests/eventhandlertest.cpp index 43c58e9d7..61ff738ed 100644 --- a/autotests/eventhandlertest.cpp +++ b/autotests/eventhandlertest.cpp @@ -65,8 +65,6 @@ private Q_SLOTS: void nullSubtitle(); void mediaInfo(); void nullMediaInfo(); - void linkPreviewer(); - void nullLinkPreviewer(); void hasReply(); void nullHasReply(); void replyId(); @@ -399,28 +397,6 @@ void EventHandlerTest::nullMediaInfo() QCOMPARE(noEventHandler.getMediaInfo(), QVariantMap()); } -void EventHandlerTest::linkPreviewer() -{ - auto event = room->messageEvents().at(2).get(); - eventHandler.setEvent(event); - - QCOMPARE(eventHandler.getLinkPreviewer()->url(), QUrl("https://kde.org"_ls)); - - event = room->messageEvents().at(0).get(); - eventHandler.setEvent(event); - - QCOMPARE(eventHandler.getLinkPreviewer(), nullptr); -} - -void EventHandlerTest::nullLinkPreviewer() -{ - QTest::ignoreMessage(QtWarningMsg, "getLinkPreviewer called with m_room set to nullptr."); - QCOMPARE(emptyHandler.getLinkPreviewer(), nullptr); - - QTest::ignoreMessage(QtWarningMsg, "getLinkPreviewer called with m_event set to nullptr."); - QCOMPARE(noEventHandler.getLinkPreviewer(), nullptr); -} - void EventHandlerTest::hasReply() { auto event = room->messageEvents().at(5).get(); diff --git a/autotests/linkpreviewertest.cpp b/autotests/linkpreviewertest.cpp new file mode 100644 index 000000000..487b58355 --- /dev/null +++ b/autotests/linkpreviewertest.cpp @@ -0,0 +1,104 @@ +// SPDX-FileCopyrightText: 2023 James Graham +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +#include +#include + +#include "linkpreviewer.h" + +#include +#include +#include + +#include "utils.h" + +#include "testutils.h" + +using namespace Quotient; + +class LinkPreviewerTest : public QObject +{ + Q_OBJECT + +private: + Connection *connection = nullptr; + TestUtils::TestRoom *room = nullptr; + +private Q_SLOTS: + void initTestCase(); + + void linkPreviewsMatch_data(); + void linkPreviewsMatch(); + + void linkPreviewsReject_data(); + void linkPreviewsReject(); + + void editedLink(); +}; + +void LinkPreviewerTest::initTestCase() +{ + connection = Connection::makeMockConnection(QStringLiteral("@bob:example.org")); + room = new TestUtils::TestRoom(connection, QStringLiteral("!test:example.org")); +} + +void LinkPreviewerTest::linkPreviewsMatch_data() +{ + QTest::addColumn("eventSource"); + QTest::addColumn("testOutputLink"); + + QTest::newRow("plainHttps") << QStringLiteral("test-validplainlink-event.json") << QUrl("https://kde.org"_ls); + QTest::newRow("richHttps") << QStringLiteral("test-validrichlink-event.json") << QUrl("https://kde.org"_ls); + QTest::newRow("plainWww") << QStringLiteral("test-validplainwwwlink-event.json") << QUrl("www.example.org"_ls); + QTest::newRow("multipleHttps") << QStringLiteral("test-multiplelink-event.json") << QUrl("www.example.org"_ls); +} + +void LinkPreviewerTest::linkPreviewsMatch() +{ + QFETCH(QString, eventSource); + QFETCH(QUrl, testOutputLink); + + auto event = TestUtils::loadEventFromFile(eventSource); + auto linkPreviewer = LinkPreviewer(room, event.get()); + + QCOMPARE(linkPreviewer.empty(), false); + QCOMPARE(linkPreviewer.url(), testOutputLink); +} + +void LinkPreviewerTest::linkPreviewsReject_data() +{ + QTest::addColumn("eventSource"); + + QTest::newRow("mxc") << QStringLiteral("test-invalidmxclink-event.json"); + QTest::newRow("matrixTo") << QStringLiteral("test-invalidmatrixtolink-event.json"); + QTest::newRow("noSpace") << QStringLiteral("test-invalidnospacelink-event.json"); +} + +void LinkPreviewerTest::linkPreviewsReject() +{ + QFETCH(QString, eventSource); + + auto event = TestUtils::loadEventFromFile(eventSource); + auto linkPreviewer = LinkPreviewer(room, event.get()); + + QCOMPARE(linkPreviewer.empty(), true); + QCOMPARE(linkPreviewer.url(), QUrl()); +} + +void LinkPreviewerTest::editedLink() +{ + room->syncNewEvents(QStringLiteral("test-linkpreviewerintial-sync.json")); + auto event = eventCast(room->messageEvents().at(0).get()); + auto linkPreviewer = LinkPreviewer(room, event); + + QCOMPARE(linkPreviewer.empty(), false); + QCOMPARE(linkPreviewer.url(), QUrl("https://kde.org"_ls)); + + room->syncNewEvents(QStringLiteral("test-linkpreviewerreplace-sync.json")); + + QCOMPARE(linkPreviewer.empty(), true); + QCOMPARE(linkPreviewer.url(), QUrl()); +} + +QTEST_MAIN(LinkPreviewerTest) +#include "linkpreviewertest.moc" diff --git a/autotests/testutils.h b/autotests/testutils.h index b32d9cd43..bebf8f7e4 100644 --- a/autotests/testutils.h +++ b/autotests/testutils.h @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2023 James Graham // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL +#include #include #include "neochatroom.h" @@ -38,4 +39,17 @@ public: } } }; + +template +inline Quotient::event_ptr_tt loadEventFromFile(const QString &eventFileName) +{ + if (!eventFileName.isEmpty()) { + QFile testEventFile; + testEventFile.setFileName(QLatin1String(DATA_DIR) + u'/' + eventFileName); + testEventFile.open(QIODevice::ReadOnly); + auto testSyncJson = QJsonDocument::fromJson(testEventFile.readAll()).object(); + return Quotient::loadEvent(testSyncJson); + } + return nullptr; +} } diff --git a/autotests/texthandlertest.cpp b/autotests/texthandlertest.cpp index 5eff65e66..223f03c0e 100644 --- a/autotests/texthandlertest.cpp +++ b/autotests/texthandlertest.cpp @@ -64,11 +64,6 @@ private Q_SLOTS: void receiveRichEdited(); void receiveLineSeparator(); void receiveRichCodeUrl(); - - void linkPreviewsMatch_data(); - void linkPreviewsMatch(); - void linkPreviewsReject_data(); - void linkPreviewsReject(); }; void TextHandlerTest::initTestCase() @@ -523,53 +518,6 @@ void TextHandlerTest::receiveLineSeparator() QCOMPARE(textHandler.handleRecievePlainText(Qt::PlainText, true), QStringLiteral("foo bar")); } -void TextHandlerTest::linkPreviewsMatch_data() -{ - QTest::addColumn("testInputString"); - QTest::addColumn>("testOutputLinks"); - - QTest::newRow("plainHttps") << QStringLiteral("https://kde.org") << QList({QUrl("https://kde.org"_ls)}); - QTest::newRow("richHttps") << QStringLiteral("Rich Link") << QList({QUrl("https://kde.org"_ls)}); - QTest::newRow("plainWww") << QStringLiteral("www.example.org") << QList({QUrl("www.example.org"_ls)}); - QTest::newRow("multipleHttps") << QStringLiteral("https://kde.org www.example.org") - << QList({ - QUrl("https://kde.org"_ls), - QUrl("www.example.org"_ls), - }); -} - -void TextHandlerTest::linkPreviewsMatch() -{ - QFETCH(QString, testInputString); - QFETCH(QList, testOutputLinks); - - TextHandler testTextHandler; - testTextHandler.setData(testInputString); - - QCOMPARE(testTextHandler.getLinkPreviews(), testOutputLinks); -} - -void TextHandlerTest::linkPreviewsReject_data() -{ - QTest::addColumn("testInputString"); - QTest::addColumn>("testOutputLinks"); - - QTest::newRow("mxc") << QStringLiteral("mxc://example.org/SEsfnsuifSDFSSEF") << QList(); - QTest::newRow("matrixTo") << QStringLiteral("https://matrix.to/#/@alice:example.org") << QList(); - QTest::newRow("noSpace") << QStringLiteral("testhttps://kde.org") << QList(); -} - -void TextHandlerTest::linkPreviewsReject() -{ - QFETCH(QString, testInputString); - QFETCH(QList, testOutputLinks); - - TextHandler testTextHandler; - testTextHandler.setData(testInputString); - - QCOMPARE(testTextHandler.getLinkPreviews(), testOutputLinks); -} - void TextHandlerTest::receiveRichCodeUrl() { auto input = QStringLiteral("https://kde.org"); diff --git a/src/eventhandler.cpp b/src/eventhandler.cpp index 1d30cea56..6b337366e 100644 --- a/src/eventhandler.cpp +++ b/src/eventhandler.cpp @@ -777,43 +777,6 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const EventContent::FileInfo return mediaInfo; } -QSharedPointer EventHandler::getLinkPreviewer() const -{ - if (m_room == nullptr) { - qCWarning(EventHandling) << "getLinkPreviewer called with m_room set to nullptr."; - return nullptr; - } - if (m_event == nullptr) { - qCWarning(EventHandling) << "getLinkPreviewer called with m_event set to nullptr."; - return nullptr; - } - if (!m_event->is()) { - return nullptr; - } - - QString text; - auto event = eventCast(m_event); - if (event->hasTextContent()) { - auto textContent = static_cast(event->content()); - if (textContent) { - text = textContent->body; - } else { - text = event->plainBody(); - } - } else { - text = event->plainBody(); - } - TextHandler textHandler; - textHandler.setData(text); - - QList links = textHandler.getLinkPreviews(); - if (links.size() > 0) { - return QSharedPointer(new LinkPreviewer(nullptr, m_room, links.size() > 0 ? links[0] : QUrl())); - } else { - return nullptr; - } -} - bool EventHandler::hasReply() const { if (m_event == nullptr) { diff --git a/src/eventhandler.h b/src/eventhandler.h index 93aa792da..54378696c 100644 --- a/src/eventhandler.h +++ b/src/eventhandler.h @@ -231,16 +231,6 @@ public: */ QVariantMap getMediaInfo() const; - /** - * @brief Return a LinkPreviewer object for the event. - * - * A nullptr will be returned for any event that doesn't have any links so the - * return should be null checked and an empty LinkPreviewer provided if null. - * - * @sa LinkPreviewer - */ - QSharedPointer getLinkPreviewer() const; - /** * @brief Whether the event is a reply to another in the timeline. */ diff --git a/src/linkpreviewer.cpp b/src/linkpreviewer.cpp index 03e8fc96f..c82560089 100644 --- a/src/linkpreviewer.cpp +++ b/src/linkpreviewer.cpp @@ -3,25 +3,37 @@ #include "linkpreviewer.h" -#include "controller.h" - #include #include +#include #include "neochatconfig.h" #include "neochatroom.h" +#include "utils.h" using namespace Quotient; -LinkPreviewer::LinkPreviewer(QObject *parent, const NeoChatRoom *room, const QUrl &url) - : QObject(parent) +LinkPreviewer::LinkPreviewer(const NeoChatRoom *room, const Quotient::RoomMessageEvent *event) + : QObject(nullptr) , m_currentRoom(room) + , m_event(event) , m_loaded(false) - , m_url(url) + , m_url(linkPreview(event)) { - loadUrlPreview(); - if (m_currentRoom) { + connect(this, &LinkPreviewer::urlChanged, this, &LinkPreviewer::emptyChanged); + + if (m_event != nullptr && m_currentRoom != nullptr) { + loadUrlPreview(); connect(m_currentRoom, &NeoChatRoom::urlPreviewEnabledChanged, this, &LinkPreviewer::loadUrlPreview); + // Make sure that we react to edits + connect(m_currentRoom, &NeoChatRoom::replacedEvent, this, [this](const Quotient::RoomEvent *newEvent) { + if (m_event->id() == newEvent->id()) { + m_event = eventCast(newEvent); + m_url = linkPreview(m_event); + Q_EMIT urlChanged(); + loadUrlPreview(); + } + }); } connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, &LinkPreviewer::loadUrlPreview); } @@ -51,15 +63,6 @@ QUrl LinkPreviewer::url() const return m_url; } -void LinkPreviewer::setUrl(QUrl url) -{ - if (url != m_url) { - m_url = url; - urlChanged(); - loadUrlPreview(); - } -} - void LinkPreviewer::loadUrlPreview() { if (!m_currentRoom || !NeoChatConfig::showLinkPreview() || !m_currentRoom->urlPreviewEnabled()) { @@ -98,4 +101,38 @@ bool LinkPreviewer::empty() const return m_url.isEmpty(); } +QUrl LinkPreviewer::linkPreview(const Quotient::RoomMessageEvent *event) +{ + if (event == nullptr) { + return {}; + } + + QString text; + if (event->hasTextContent()) { + auto textContent = static_cast(event->content()); + if (textContent) { + text = textContent->body; + } else { + text = event->plainBody(); + } + } else { + text = event->plainBody(); + } + + auto data = text.remove(TextRegex::removeRichReply); + auto linksMatch = TextRegex::url.globalMatch(data); + while (linksMatch.hasNext()) { + auto link = linksMatch.next().captured(); + if (!link.contains(QStringLiteral("matrix.to"))) { + return QUrl(link); + } + } + return {}; +} + +bool LinkPreviewer::hasPreviewableLinks(const Quotient::RoomMessageEvent *event) +{ + return !linkPreview(event).isEmpty(); +} + #include "moc_linkpreviewer.cpp" diff --git a/src/linkpreviewer.h b/src/linkpreviewer.h index 00c4cf1f2..5e85f422f 100644 --- a/src/linkpreviewer.h +++ b/src/linkpreviewer.h @@ -7,6 +7,11 @@ #include #include +namespace Quotient +{ +class RoomMessageEvent; +} + class NeoChatRoom; /** @@ -25,7 +30,7 @@ class LinkPreviewer : public QObject /** * @brief The URL to get the preview for. */ - Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged) + Q_PROPERTY(QUrl url READ url NOTIFY urlChanged) /** * @brief Whether the preview information has been loaded. @@ -55,18 +60,25 @@ class LinkPreviewer : public QObject Q_PROPERTY(bool empty READ empty NOTIFY emptyChanged) public: - explicit LinkPreviewer(QObject *parent = nullptr, const NeoChatRoom *room = nullptr, const QUrl &url = {}); + explicit LinkPreviewer(const NeoChatRoom *room = nullptr, const Quotient::RoomMessageEvent *event = nullptr); [[nodiscard]] QUrl url() const; - void setUrl(QUrl); [[nodiscard]] bool loaded() const; [[nodiscard]] QString title() const; [[nodiscard]] QString description() const; [[nodiscard]] QUrl imageSource() const; [[nodiscard]] bool empty() const; + /** + * @brief Whether the given event has at least 1 pre-viewable link. + * + * A link is only pre-viewable if it is http, https or something starting with www. + */ + static bool hasPreviewableLinks(const Quotient::RoomMessageEvent *event); + private: - const NeoChatRoom *m_currentRoom = nullptr; + const NeoChatRoom *m_currentRoom; + const Quotient::RoomMessageEvent *m_event; bool m_loaded; QString m_title = QString(); @@ -76,6 +88,14 @@ private: void loadUrlPreview(); + /** + * @brief Return the link to be previewed from the given event. + * + * This function is designed to give only links that should be previewed so + * http, https or something starting with www. The first valid link is returned. + */ + static QUrl linkPreview(const Quotient::RoomMessageEvent *event); + Q_SIGNALS: void loadedChanged(); void titleChanged(); diff --git a/src/models/messageeventmodel.cpp b/src/models/messageeventmodel.cpp index 0e6e48f31..f93f98e65 100644 --- a/src/models/messageeventmodel.cpp +++ b/src/models/messageeventmodel.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only #include "messageeventmodel.h" +#include "linkpreviewer.h" #include "messageeventmodel_logging.h" #include "neochatconfig.h" @@ -22,6 +23,7 @@ #include "eventhandler.h" #include "events/pollevent.h" #include "models/reactionmodel.h" +#include "texthandler.h" using namespace Quotient; @@ -237,6 +239,10 @@ void MessageEventModel::setRoom(NeoChatRoom *room) }); connect(m_currentRoom, &Room::replacedEvent, this, [this](const RoomEvent *newEvent) { refreshLastUserEvents(refreshEvent(newEvent->id()) - timelineBaseIndex()); + const RoomMessageEvent *message = eventCast(newEvent); + if (message != nullptr) { + createEventObjects(message); + } }); connect(m_currentRoom, &Room::updatedEvent, this, [this](const QString &eventId) { if (eventId.isEmpty()) { // How did we get here? @@ -720,14 +726,14 @@ void MessageEventModel::createEventObjects(const Quotient::RoomMessageEvent *eve { auto eventId = event->id(); - EventHandler eventHandler; - eventHandler.setRoom(m_currentRoom); - eventHandler.setEvent(event); - - if (auto linkPreviewer = eventHandler.getLinkPreviewer()) { - m_linkPreviewers[eventId] = linkPreviewer; + if (m_linkPreviewers.contains(eventId)) { + if (!LinkPreviewer::hasPreviewableLinks(event)) { + m_linkPreviewers.remove(eventId); + } } else { - m_linkPreviewers.remove(eventId); + if (LinkPreviewer::hasPreviewableLinks(event)) { + m_linkPreviewers[eventId] = QSharedPointer(new LinkPreviewer(m_currentRoom, event)); + } } // ReactionModel handles updates to add and remove reactions, we only need to diff --git a/src/texthandler.cpp b/src/texthandler.cpp index ac3aa643d..503e149bc 100644 --- a/src/texthandler.cpp +++ b/src/texthandler.cpp @@ -493,18 +493,4 @@ QString TextHandler::linkifyUrls(QString stringIn) return stringIn; } -QList TextHandler::getLinkPreviews() -{ - auto data = m_data.remove(TextRegex::removeRichReply); - auto linksMatch = TextRegex::url.globalMatch(data); - QList links; - while (linksMatch.hasNext()) { - auto link = linksMatch.next().captured(); - if (!link.contains(QStringLiteral("matrix.to"))) { - links += QUrl(link); - } - } - return links; -} - #include "moc_texthandler.cpp" diff --git a/src/texthandler.h b/src/texthandler.h index eac83e0f9..45ccb111b 100644 --- a/src/texthandler.h +++ b/src/texthandler.h @@ -11,29 +11,9 @@ #include "neochatroom.h" -namespace TextRegex +namespace Quotient { -static const QRegularExpression endTagType{QStringLiteral("(>| )")}; -static const QRegularExpression attributeData{QStringLiteral("['\"](.*?)['\"]")}; -static const QRegularExpression removeReply{QStringLiteral("> <.*?>.*?\\n\\n"), QRegularExpression::DotMatchesEverythingOption}; -static const QRegularExpression removeRichReply{QStringLiteral(".*?"), QRegularExpression::DotMatchesEverythingOption}; -static const QRegularExpression codePill{QStringLiteral("
]*>(.*?)
"), QRegularExpression::DotMatchesEverythingOption}; -static const QRegularExpression userPill{QStringLiteral("(.*?)"), QRegularExpression::DotMatchesEverythingOption}; -static const QRegularExpression blockQuote{QStringLiteral("
\n?(?:

)?(.*?)(?:

)?\n?
"), - QRegularExpression::DotMatchesEverythingOption}; -static const QRegularExpression strikethrough{QStringLiteral("(.*?)"), QRegularExpression::DotMatchesEverythingOption}; -static const QRegularExpression mxcImage{QStringLiteral(R"AAA()AAA")}; -static const QRegularExpression plainUrl( - QStringLiteral( - R"((*SKIP)(*F)|\b((www\.(?!\.)(?!(\w|\.|-)+@)|(https?|ftp):(//)?\w|(magnet|matrix):)(&(?![lg]t;)|[^&\s<>'"])+(&(?![lg]t;)|[^&!,.\s<>'"\]):])))"), - QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption); -static const QRegularExpression - url(QStringLiteral(R"(\b((www\.(?!\.)(?!(\w|\.|-)+@)|https?:(//)?\w)(&(?![lg]t;)|[^&\s<>'"])+(&(?![lg]t;)|[^&!,.\s<>'"\]):])))"), - QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption); -static const QRegularExpression emailAddress(QStringLiteral(R"((*SKIP)(*F)|\b(mailto:)?((\w|\.|-)+@(\w|\.|-)+\.\w+\b))"), - QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption); -static const QRegularExpression mxId(QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"), - QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption); +class RoomMessageEvent; } /** @@ -114,14 +94,6 @@ public: */ QString handleRecievePlainText(Qt::TextFormat inputFormat = Qt::PlainText, const bool &stripNewlines = false); - /** - * @brief Return a list of links that can be previewed. - * - * This function is designed to give only links that should be previewed so - * http, https or something starting with www. - */ - QList getLinkPreviews(); - private: QString m_data; diff --git a/src/utils.h b/src/utils.h index 1c7ddc97d..66d47d775 100644 --- a/src/utils.h +++ b/src/utils.h @@ -4,6 +4,7 @@ #include #include #include +#include namespace Utils { @@ -21,3 +22,28 @@ inline QColor getUserColor(qreal hueF) return QColor::fromHslF(hueF, 1, -0.7 * lightness + 0.9, 1); } } + +namespace TextRegex +{ +static const QRegularExpression endTagType{QStringLiteral("(>| )")}; +static const QRegularExpression attributeData{QStringLiteral("['\"](.*?)['\"]")}; +static const QRegularExpression removeReply{QStringLiteral("> <.*?>.*?\\n\\n"), QRegularExpression::DotMatchesEverythingOption}; +static const QRegularExpression removeRichReply{QStringLiteral(".*?"), QRegularExpression::DotMatchesEverythingOption}; +static const QRegularExpression codePill{QStringLiteral("
]*>(.*?)
"), QRegularExpression::DotMatchesEverythingOption}; +static const QRegularExpression userPill{QStringLiteral("(.*?)"), QRegularExpression::DotMatchesEverythingOption}; +static const QRegularExpression blockQuote{QStringLiteral("
\n?(?:

)?(.*?)(?:

)?\n?
"), + QRegularExpression::DotMatchesEverythingOption}; +static const QRegularExpression strikethrough{QStringLiteral("(.*?)"), QRegularExpression::DotMatchesEverythingOption}; +static const QRegularExpression mxcImage{QStringLiteral(R"AAA()AAA")}; +static const QRegularExpression plainUrl( + QStringLiteral( + R"((*SKIP)(*F)|\b((www\.(?!\.)(?!(\w|\.|-)+@)|(https?|ftp):(//)?\w|(magnet|matrix):)(&(?![lg]t;)|[^&\s<>'"])+(&(?![lg]t;)|[^&!,.\s<>'"\]):])))"), + QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption); +static const QRegularExpression + url(QStringLiteral(R"(\b((www\.(?!\.)(?!(\w|\.|-)+@)|https?:(//)?\w)(&(?![lg]t;)|[^&\s<>'"])+(&(?![lg]t;)|[^&!,.\s<>'"\]):])))"), + QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption); +static const QRegularExpression emailAddress(QStringLiteral(R"((*SKIP)(*F)|\b(mailto:)?((\w|\.|-)+@(\w|\.|-)+\.\w+\b))"), + QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption); +static const QRegularExpression mxId(QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"), + QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption); +}