diff --git a/resources/qml/MessageInput.qml b/resources/qml/MessageInput.qml
index f31123e5..f6fe03c5 100644
--- a/resources/qml/MessageInput.qml
+++ b/resources/qml/MessageInput.qml
@@ -13,6 +13,8 @@ import im.nheko 1.0
Rectangle {
id: inputBar
+ readonly property string text: messageInput.text
+
color: Nheko.colors.window
Layout.fillWidth: true
Layout.preferredHeight: row.implicitHeight
diff --git a/resources/qml/NotificationWarning.qml b/resources/qml/MessageInputWarning.qml
similarity index 73%
rename from resources/qml/NotificationWarning.qml
rename to resources/qml/MessageInputWarning.qml
index 13d2cb23..c2db2daa 100644
--- a/resources/qml/NotificationWarning.qml
+++ b/resources/qml/MessageInputWarning.qml
@@ -8,14 +8,20 @@ import QtQuick.Layouts 1.2
import im.nheko 1.0
Item {
- implicitHeight: warningRect.visible ? warningDisplay.implicitHeight + 2 * Nheko.paddingSmall : 0
+ id: warningRoot
+
+ required property string text
+ required property bool isVisible
+
+ implicitHeight: isVisible ? warningDisplay.implicitHeight + 2 * Nheko.paddingSmall : 0
height: implicitHeight
Layout.fillWidth: true
+ Layout.margins: isVisible ? Nheko.paddingSmall : 0
Rectangle {
id: warningRect
- visible: (room && room.permissions.canPingRoom() && room.input.containsAtRoom)
+ visible: warningRoot.isVisible
// TODO: Qt.alpha() would make more sense but it wasn't working...
color: Qt.rgba(Nheko.theme.error.r, Nheko.theme.error.g, Nheko.theme.error.b, 0.3)
border.width: 1
@@ -31,7 +37,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter
anchors.margins: Nheko.paddingSmall
color: Nheko.colors.text
- text: qsTr("You are about to notify the whole room")
+ text: warningRoot.text
textFormat: Text.PlainText
}
diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index e836f60f..5c982270 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -153,13 +153,30 @@ Item {
UploadBox {
}
- NotificationWarning {
+ MessageInputWarning {
+ text: qsTr("You are about to notify the whole room")
+ isVisible: (room && room.permissions.canPingRoom() && room.input.containsAtRoom)
+ }
+
+ MessageInputWarning {
+ text: qsTr("The command /%1 is not recognized and will be sent as part of your message").arg(Nheko.getCommandFromText(input.text))
+ isVisible: {
+ if (!input.text)
+ return false;
+
+ let command = Nheko.getCommandFromText(input.text);
+ if (Nheko.isInvalidCommand(command) && ("/" + command !== input.text))
+ return true;
+ else
+ return false;
+ }
}
ReplyPopup {
}
MessageInput {
+ id: input
}
}
diff --git a/resources/res.qrc b/resources/res.qrc
index 88159d40..5fbd00d7 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -123,7 +123,7 @@
qml/ForwardCompleter.qml
qml/SelfVerificationCheck.qml
qml/TypingIndicator.qml
- qml/NotificationWarning.qml
+ qml/MessageInputWarning.qml
qml/components/AdaptiveLayout.qml
qml/components/AdaptiveLayoutElement.qml
qml/components/AvatarListTile.qml
diff --git a/src/CompletionProxyModel.h b/src/CompletionProxyModel.h
index e0f00788..90daf7ad 100644
--- a/src/CompletionProxyModel.h
+++ b/src/CompletionProxyModel.h
@@ -184,6 +184,8 @@ public slots:
void setSearchString(const QString &s);
QString searchString() const { return searchString_; }
+ bool hasCompletion() const { return rowCount() > 0; }
+
signals:
void newSearchString(QString);
diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp
index 7d964bb5..5184ba94 100644
--- a/src/timeline/InputBar.cpp
+++ b/src/timeline/InputBar.cpp
@@ -846,6 +846,8 @@ InputBar::command(const QString &command, QString args)
cache::getMembers(this->room->roomId().toStdString(), 0, -1));
} else if (command == QLatin1String("converttoroom")) {
utils::removeDirectFromRoom(this->room->roomId());
+ } else {
+ message("/" + command + " " + args);
}
}
diff --git a/src/ui/NhekoGlobalObject.cpp b/src/ui/NhekoGlobalObject.cpp
index a6f9abe7..3cb6a8a8 100644
--- a/src/ui/NhekoGlobalObject.cpp
+++ b/src/ui/NhekoGlobalObject.cpp
@@ -190,3 +190,63 @@ Nheko::setWindowRole([[maybe_unused]] QWindow *win, [[maybe_unused]] QString new
QXcbWindowFunctions::setWmWindowRole(win, newRole.toUtf8());
#endif
}
+
+QString
+Nheko::getCommandFromText(const QString &text)
+{
+ if (text.startsWith('/')) {
+ int command_end = text.indexOf(QRegularExpression(QStringLiteral("\\s")));
+ if (command_end == -1)
+ command_end = text.size();
+ auto command = text.mid(1, command_end - 1);
+ if (command.isEmpty() || command == QLatin1String("/"))
+ return {};
+ else {
+ return command;
+ }
+ } else
+ return {};
+}
+
+bool
+Nheko::isInvalidCommand(QString command) const
+{
+ if (command.size() <= 0)
+ return false;
+
+ static const QStringList validCommands{QStringLiteral("/me"),
+ QStringLiteral("/react"),
+ QStringLiteral("/join"),
+ QStringLiteral("/knock"),
+ QStringLiteral("/part"),
+ QStringLiteral("/leave"),
+ QStringLiteral("/invite"),
+ QStringLiteral("/kick"),
+ QStringLiteral("/ban"),
+ QStringLiteral("/unban"),
+ QStringLiteral("/redact"),
+ QStringLiteral("/roomnick"),
+ QStringLiteral("/shrug"),
+ QStringLiteral("/fliptable"),
+ QStringLiteral("/unfliptable"),
+ QStringLiteral("/sovietflip"),
+ QStringLiteral("/clear-timeline"),
+ QStringLiteral("/reset-state"),
+ QStringLiteral("/rotate-megolm-session"),
+ QStringLiteral("/md"),
+ QStringLiteral("/cmark"),
+ QStringLiteral("/plain"),
+ QStringLiteral("/rainbow"),
+ QStringLiteral("/rainbowme"),
+ QStringLiteral("/notice"),
+ QStringLiteral("/rainbownotice"),
+ QStringLiteral("/confetti"),
+ QStringLiteral("/rainbowconfetti"),
+ QStringLiteral("/goto"),
+ QStringLiteral("/converttodm"),
+ QStringLiteral("/converttoroom")};
+
+ if (!command.startsWith('/'))
+ command.prepend('/');
+ return !validCommands.contains(command);
+}
diff --git a/src/ui/NhekoGlobalObject.h b/src/ui/NhekoGlobalObject.h
index b7a7a637..1ea2c109 100644
--- a/src/ui/NhekoGlobalObject.h
+++ b/src/ui/NhekoGlobalObject.h
@@ -72,6 +72,9 @@ public:
Q_INVOKABLE void setTransientParent(QWindow *window, QWindow *parentWindow) const;
Q_INVOKABLE void setWindowRole(QWindow *win, QString newRole) const;
+ Q_INVOKABLE QString getCommandFromText(const QString &text);
+ Q_INVOKABLE bool isInvalidCommand(QString command) const;
+
public slots:
void updateUserProfile();