[network/ruqola] src/core: Avoid blank line for unavailable quoted messages

Till Adam <[email protected]> Wed, 5 Aug 2026 10:07:41 +0000 (UTC)
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 2743dcd2538616c4f80993a5613196597938fb14 by Till Adam.
Committed on 05/08/2026 at 07:46.
Pushed by tilladam into branch 'master'.

Avoid blank line for unavailable quoted messages

M  +2    -0    src/core/autotests/textconvertertest.cpp
M  +17   -8    src/core/textconverter.cpp

https://invent.kde.org/network/ruqola/-/commit/2743dcd2538616c4f80993a5613196597938fb14

diff --git a/src/core/autotests/textconvertertest.cpp b/src/core/autotests/textconvertertest.cpp
index 500cfb945b..918ed1477e 100644
--- a/src/core/autotests/textconvertertest.cpp
+++ b/src/core/autotests/textconvertertest.cpp
@@ -65,6 +65,8 @@ void TextConverterTest::shouldConvertText_data()
                                     "<p><a href=\"http://www.kde.org\">http://www.kde.org</a> <a href=\"http://www.kde.org\">http://www.kde.org</a></p>\n");
     QTest::newRow("named-url") << QStringLiteral("[example.com](http://example.com)")
                                << QStringLiteral("<p><a href=\"http://example.com\">example.com</a></p>\n");
+    QTest::newRow("unresolved quoted message") << QStringLiteral("[ ](https://example.com/channel/general?msg=quotedMessageId)\nVisible message")
+                                               << QStringLiteral("<p>Visible message</p>\n");
     QTest::newRow("bold") << QStringLiteral("*bla*") << QStringLiteral("<p><strong>bla</strong></p>\n");
     QTest::newRow("multi star") << QStringLiteral("**bla**") << QStringLiteral("<p><strong>bla</strong></p>\n");
     QTest::newRow("multi star2") << QStringLiteral("***bla***") << QStringLiteral("<p><strong><strong>bla</strong></strong></p>\n");
diff --git a/src/core/textconverter.cpp b/src/core/textconverter.cpp
index ed62cd7afc..d683b44eab 100644
--- a/src/core/textconverter.cpp
+++ b/src/core/textconverter.cpp
@@ -37,13 +37,23 @@ QString TextConverter::convertMessageText(const TextConverter::ConvertMessageTex
     QString str = settings.str;
 
     // TODO we need to look at room name too as we can have it when we use "direct reply"
-    if (str.contains("[ ](http"_L1)
+    const qsizetype quoteMarkerStart = str.indexOf("[ ](http"_L1);
+    QString quoteUrl;
+    if (quoteMarkerStart >= 0) {
+        const qsizetype startPos = str.indexOf(u'(', quoteMarkerStart);
+        const qsizetype endPos = str.indexOf(u')', startPos);
+        quoteUrl = str.mid(startPos + 1, endPos - startPos - 1);
+        // The empty Markdown link is metadata used to identify a quoted message, not visible
+        // message content. Remove it even when the referenced message is unavailable or the
+        // configured recursion limit prevents rendering the quote; otherwise it becomes an
+        // empty first QTextDocument block and leaves a full blank line above the message.
+        str.remove(quoteMarkerStart, endPos - quoteMarkerStart + 1);
+    }
+
+    if (!quoteUrl.isEmpty()
         && (settings.maximumRecursiveQuotedText == -1 || (settings.maximumRecursiveQuotedText > recusiveIndex))) { // ## is there a better way?
-        const int startPos = str.indexOf(u'(');
-        const int endPos = str.indexOf(u')');
-        const QString url = str.mid(startPos + 1, endPos - startPos - 1);
         // URL example https://HOSTNAME/channel/all?msg=3BR34NSG5x7ZfBa22
-        const QByteArray messageId = url.mid(url.indexOf("msg="_L1) + 4).toLatin1();
+        const QByteArray messageId = quoteUrl.mid(quoteUrl.indexOf("msg="_L1) + 4).toLatin1();
         // qCDebug(RUQOLA_TEXTTOHTML_LOG) << "Extracted messageId" << messageId;
         auto it = std::find_if(settings.allMessages.cbegin(), settings.allMessages.cend(), [messageId](const Message &msg) {
             return msg.messageId() == messageId;
@@ -63,11 +73,10 @@ QString TextConverter::convertMessageText(const TextConverter::ConvertMessageTex
             recusiveIndex++;
             const QString text = TextConverter::convertMessageText(newSetting, needUpdateMessageId, recusiveIndex, numberOfTextSearched, hightLightStringIndex);
             Utils::QuotedRichTextInfo info;
-            info.url = url;
+            info.url = quoteUrl;
             info.richText = text;
             info.displayTime = (*it).dateTime();
 
-            str = str.left(startPos - 3) + str.mid(endPos + 1);
             auto newsettings = new TextConverter::ConvertMessageTextSettings{
                 str.toHtmlEscaped(),
                 settings.userName,
@@ -104,7 +113,7 @@ QString TextConverter::convertMessageText(const TextConverter::ConvertMessageTex
                     const QString text =
                         TextConverter::convertMessageText(newSetting, needUpdateMessageId, recusiveIndex, numberOfTextSearched, hightLightStringIndex);
                     Utils::QuotedRichTextInfo info;
-                    info.url = url;
+                    info.url = quoteUrl;
                     info.richText = text;
                     info.displayTime = msg->dateTime();
                     auto newsettings = new TextConverter::ConvertMessageTextSettings{