Make single newlines cause a <br> by default
This should match what people expect from a chat application much better. The biggest reason not to do this, is because some people might paste markdown documents. For those people there is now a /cmark command, which disables most of our extensions to cmark, including the newline behaviour. There is a long discussion on the Fediverse and on Github linked below. Mastodon https://fosstodon.org/@deepbluev7/109771668066978726 fixes #757
This commit is contained in:
parent
9f529075f0
commit
0c3d46795b
@ -73,6 +73,8 @@ CommandCompleter::data(const QModelIndex &index, int role) const
|
|||||||
return QString("/rotate-megolm-session");
|
return QString("/rotate-megolm-session");
|
||||||
case Md:
|
case Md:
|
||||||
return QString("/md ");
|
return QString("/md ");
|
||||||
|
case Cmark:
|
||||||
|
return QString("/cmark ");
|
||||||
case Plain:
|
case Plain:
|
||||||
return QString("/plain ");
|
return QString("/plain ");
|
||||||
case Rainbow:
|
case Rainbow:
|
||||||
@ -140,6 +142,8 @@ CommandCompleter::data(const QModelIndex &index, int role) const
|
|||||||
return tr("/rotate-megolm-session");
|
return tr("/rotate-megolm-session");
|
||||||
case Md:
|
case Md:
|
||||||
return tr("/md [message]");
|
return tr("/md [message]");
|
||||||
|
case Cmark:
|
||||||
|
return tr("/cmark [message]");
|
||||||
case Plain:
|
case Plain:
|
||||||
return tr("/plain [message]");
|
return tr("/plain [message]");
|
||||||
case Rainbow:
|
case Rainbow:
|
||||||
@ -206,6 +210,9 @@ CommandCompleter::data(const QModelIndex &index, int role) const
|
|||||||
return tr("Rotate the current symmetric encryption key.");
|
return tr("Rotate the current symmetric encryption key.");
|
||||||
case Md:
|
case Md:
|
||||||
return tr("Send a markdown formatted message (ignoring the global setting).");
|
return tr("Send a markdown formatted message (ignoring the global setting).");
|
||||||
|
case Cmark:
|
||||||
|
return tr(
|
||||||
|
"Send a commonmark formatted message disabling most extensions compared to /md.");
|
||||||
case Plain:
|
case Plain:
|
||||||
return tr("Send an unformatted message (ignoring the global setting).");
|
return tr("Send an unformatted message (ignoring the global setting).");
|
||||||
case Rainbow:
|
case Rainbow:
|
||||||
|
@ -40,6 +40,7 @@ public:
|
|||||||
ResetState,
|
ResetState,
|
||||||
RotateMegolmSession,
|
RotateMegolmSession,
|
||||||
Md,
|
Md,
|
||||||
|
Cmark,
|
||||||
Plain,
|
Plain,
|
||||||
Rainbow,
|
Rainbow,
|
||||||
RainbowMe,
|
RainbowMe,
|
||||||
|
@ -6,11 +6,13 @@
|
|||||||
|
|
||||||
#include "UserDirectoryModel.h"
|
#include "UserDirectoryModel.h"
|
||||||
|
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
#include <mtx/responses/users.hpp>
|
||||||
|
|
||||||
#include "Cache.h"
|
#include "Cache.h"
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
#include <QSharedPointer>
|
|
||||||
#include "MatrixClient.h"
|
#include "MatrixClient.h"
|
||||||
#include "mtx/responses/users.hpp"
|
|
||||||
|
|
||||||
UserDirectoryModel::UserDirectoryModel(QObject *parent)
|
UserDirectoryModel::UserDirectoryModel(QObject *parent)
|
||||||
: QAbstractListModel{parent}
|
: QAbstractListModel{parent}
|
||||||
@ -49,7 +51,7 @@ UserDirectoryModel::fetchMore(const QModelIndex &)
|
|||||||
|
|
||||||
nhlog::net()->debug("Fetching users from mtxclient...");
|
nhlog::net()->debug("Fetching users from mtxclient...");
|
||||||
std::string searchTerm = userSearchString_;
|
std::string searchTerm = userSearchString_;
|
||||||
searchingUsers_ = true;
|
searchingUsers_ = true;
|
||||||
emit searchingUsersChanged();
|
emit searchingUsersChanged();
|
||||||
auto job = QSharedPointer<FetchUsersFromDirectoryJob>::create();
|
auto job = QSharedPointer<FetchUsersFromDirectoryJob>::create();
|
||||||
connect(job.data(),
|
connect(job.data(),
|
||||||
@ -88,7 +90,8 @@ UserDirectoryModel::data(const QModelIndex &index, int role) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
UserDirectoryModel::displaySearchResults(std::vector<mtx::responses::User> results, const std::string &searchTerm)
|
UserDirectoryModel::displaySearchResults(std::vector<mtx::responses::User> results,
|
||||||
|
const std::string &searchTerm)
|
||||||
{
|
{
|
||||||
if (searchTerm != this->userSearchString_)
|
if (searchTerm != this->userSearchString_)
|
||||||
return;
|
return;
|
||||||
|
@ -18,11 +18,12 @@ class FetchUsersFromDirectoryJob final : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit FetchUsersFromDirectoryJob(QObject *p = nullptr)
|
explicit FetchUsersFromDirectoryJob(QObject *p = nullptr)
|
||||||
: QObject(p)
|
: QObject(p)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
signals:
|
signals:
|
||||||
void fetchedSearchResults(std::vector<mtx::responses::User> results, const std::string &searchTerm);
|
void
|
||||||
|
fetchedSearchResults(std::vector<mtx::responses::User> results, const std::string &searchTerm);
|
||||||
};
|
};
|
||||||
class UserDirectoryModel : public QAbstractListModel
|
class UserDirectoryModel : public QAbstractListModel
|
||||||
{
|
{
|
||||||
@ -65,5 +66,6 @@ public slots:
|
|||||||
bool searchingUsers() const { return searchingUsers_; }
|
bool searchingUsers() const { return searchingUsers_; }
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void displaySearchResults(std::vector<mtx::responses::User> results, const std::string &searchTerm);
|
void
|
||||||
|
displaySearchResults(std::vector<mtx::responses::User> results, const std::string &searchTerm);
|
||||||
};
|
};
|
||||||
|
@ -901,19 +901,24 @@ process_strikethrough(cmark_node *node)
|
|||||||
cmark_iter_free(iter);
|
cmark_iter_free(iter);
|
||||||
}
|
}
|
||||||
QString
|
QString
|
||||||
utils::markdownToHtml(const QString &text, bool rainbowify_)
|
utils::markdownToHtml(const QString &text, bool rainbowify_, bool noExtensions)
|
||||||
{
|
{
|
||||||
const auto str = text.toUtf8();
|
const auto str = text.toUtf8();
|
||||||
cmark_node *const node = cmark_parse_document(str.constData(), str.size(), CMARK_OPT_UNSAFE);
|
cmark_node *const node = cmark_parse_document(str.constData(), str.size(), CMARK_OPT_UNSAFE);
|
||||||
|
|
||||||
process_strikethrough(node);
|
if (!noExtensions) {
|
||||||
process_spoilers(node);
|
process_strikethrough(node);
|
||||||
|
process_spoilers(node);
|
||||||
|
|
||||||
if (rainbowify_) {
|
if (rainbowify_) {
|
||||||
rainbowify(node);
|
rainbowify(node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *tmp_buf = cmark_render_html(node, CMARK_OPT_UNSAFE);
|
const char *tmp_buf = cmark_render_html(
|
||||||
|
node,
|
||||||
|
// by default make single linebreaks <br> tags
|
||||||
|
noExtensions ? CMARK_OPT_UNSAFE : (CMARK_OPT_UNSAFE | CMARK_OPT_HARDBREAKS));
|
||||||
// Copy the null terminated output buffer.
|
// Copy the null terminated output buffer.
|
||||||
std::string html(tmp_buf);
|
std::string html(tmp_buf);
|
||||||
|
|
||||||
@ -921,7 +926,11 @@ utils::markdownToHtml(const QString &text, bool rainbowify_)
|
|||||||
free((char *)tmp_buf);
|
free((char *)tmp_buf);
|
||||||
cmark_node_free(node);
|
cmark_node_free(node);
|
||||||
|
|
||||||
auto result = linkifyMessage(escapeBlacklistedHtml(QString::fromStdString(html))).trimmed();
|
auto result = escapeBlacklistedHtml(QString::fromStdString(html)).trimmed();
|
||||||
|
|
||||||
|
if (!noExtensions) {
|
||||||
|
result = linkifyMessage(std::move(result)).trimmed();
|
||||||
|
}
|
||||||
|
|
||||||
if (result.count(QStringLiteral("<p>")) == 1 && result.startsWith(QLatin1String("<p>")) &&
|
if (result.count(QStringLiteral("<p>")) == 1 && result.startsWith(QLatin1String("<p>")) &&
|
||||||
result.endsWith(QLatin1String("</p>"))) {
|
result.endsWith(QLatin1String("</p>"))) {
|
||||||
|
@ -276,7 +276,7 @@ linkifyMessage(const QString &body);
|
|||||||
|
|
||||||
//! Convert the input markdown text to html.
|
//! Convert the input markdown text to html.
|
||||||
QString
|
QString
|
||||||
markdownToHtml(const QString &text, bool rainbowify = false);
|
markdownToHtml(const QString &text, bool rainbowify = false, bool noExtensions = false);
|
||||||
|
|
||||||
//! Escape every html tag, that was not whitelisted
|
//! Escape every html tag, that was not whitelisted
|
||||||
QString
|
QString
|
||||||
|
@ -465,6 +465,14 @@ InputBar::message(const QString &msg, MarkdownOverride useMarkdown, bool rainbow
|
|||||||
text.formatted_body = "";
|
text.formatted_body = "";
|
||||||
else
|
else
|
||||||
text.format = "org.matrix.custom.html";
|
text.format = "org.matrix.custom.html";
|
||||||
|
} else if (useMarkdown == MarkdownOverride::CMARK) {
|
||||||
|
// disable all markdown extensions
|
||||||
|
text.formatted_body = utils::markdownToHtml(msg, rainbowify, true).toStdString();
|
||||||
|
// keep everything as it was
|
||||||
|
text.body = msg.trimmed().toStdString();
|
||||||
|
|
||||||
|
// always send formatted
|
||||||
|
text.format = "org.matrix.custom.html";
|
||||||
}
|
}
|
||||||
|
|
||||||
text.relations = generateRelations();
|
text.relations = generateRelations();
|
||||||
@ -802,6 +810,8 @@ InputBar::command(const QString &command, QString args)
|
|||||||
cache::dropOutboundMegolmSession(room->roomId().toStdString());
|
cache::dropOutboundMegolmSession(room->roomId().toStdString());
|
||||||
} else if (command == QLatin1String("md")) {
|
} else if (command == QLatin1String("md")) {
|
||||||
message(args, MarkdownOverride::ON);
|
message(args, MarkdownOverride::ON);
|
||||||
|
} else if (command == QLatin1String("cmark")) {
|
||||||
|
message(args, MarkdownOverride::CMARK);
|
||||||
} else if (command == QLatin1String("plain")) {
|
} else if (command == QLatin1String("plain")) {
|
||||||
message(args, MarkdownOverride::OFF);
|
message(args, MarkdownOverride::OFF);
|
||||||
} else if (command == QLatin1String("rainbow")) {
|
} else if (command == QLatin1String("rainbow")) {
|
||||||
|
@ -40,6 +40,7 @@ enum class MarkdownOverride
|
|||||||
NOT_SPECIFIED, // no override set
|
NOT_SPECIFIED, // no override set
|
||||||
ON,
|
ON,
|
||||||
OFF,
|
OFF,
|
||||||
|
CMARK,
|
||||||
};
|
};
|
||||||
|
|
||||||
class InputVideoSurface final : public QAbstractVideoSurface
|
class InputVideoSurface final : public QAbstractVideoSurface
|
||||||
|
Loading…
Reference in New Issue
Block a user