diff --git a/resources/qml/MessageInputWarning.qml b/resources/qml/MessageInputWarning.qml index af65ebc1..9b0b0907 100644 --- a/resources/qml/MessageInputWarning.qml +++ b/resources/qml/MessageInputWarning.qml @@ -11,6 +11,7 @@ Rectangle { id: warningRoot required property string text + property color bubbleColor: Nheko.theme.error implicitHeight: visible ? warningDisplay.implicitHeight + 4 * Nheko.paddingSmall : 0 height: implicitHeight @@ -22,9 +23,9 @@ Rectangle { visible: warningRoot.visible // 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) + color: Qt.rgba(bubbleColor.r, bubbleColor.g, bubbleColor.b, 0.3) border.width: 1 - border.color: Nheko.theme.error + border.color: bubbleColor radius: 3 anchors.fill: parent anchors.margins: visible ? Nheko.paddingSmall : 0 diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index f0e71c60..abda16b9 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -160,7 +160,13 @@ Item { MessageInputWarning { text: qsTr("The command /%1 is not recognized and will be sent as part of your message").arg(room ? room.input.currentCommand : "") - visible: room ? room.input.containsInvalidCommand : false + visible: room ? room.input.containsInvalidCommand && !room.input.containsIncompleteCommand : false + } + + MessageInputWarning { + text: qsTr("/%1 looks like an incomplete command. To send it anyway, add a space to the end of your message.").arg(room ? room.input.currentCommand : "") + visible: room ? room.input.containsIncompleteCommand : false + bubbleColor: Nheko.theme.orange } ReplyPopup { diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp index fb61ce0d..b27128e0 100644 --- a/src/timeline/InputBar.cpp +++ b/src/timeline/InputBar.cpp @@ -252,7 +252,7 @@ InputBar::updateTextContentProperties(const QString &t) } // check for invalid commands - auto commandName = getCommandAndArgs().first; + auto commandName = getCommandAndArgs(t).first; static const QSet validCommands{QStringLiteral("me"), QStringLiteral("react"), QStringLiteral("join"), @@ -284,14 +284,18 @@ InputBar::updateTextContentProperties(const QString &t) QStringLiteral("goto"), QStringLiteral("converttodm"), QStringLiteral("converttoroom")}; - bool hasInvalidCommand = - !commandName.isNull() && '/' + commandName != text() && !validCommands.contains(commandName); + bool hasInvalidCommand = !commandName.isNull() && !validCommands.contains(commandName); + bool hasIncompleteCommand = hasInvalidCommand && '/' + commandName == t; bool signalsChanged{false}; if (containsInvalidCommand_ != hasInvalidCommand) { containsInvalidCommand_ = hasInvalidCommand; signalsChanged = true; } + if (containsIncompleteCommand_ != hasIncompleteCommand) { + containsIncompleteCommand_ = hasIncompleteCommand; + signalsChanged = true; + } if (currentCommand_ != commandName) { currentCommand_ = commandName; signalsChanged = true; @@ -299,6 +303,7 @@ InputBar::updateTextContentProperties(const QString &t) if (signalsChanged) { emit currentCommandChanged(); emit containsInvalidCommandChanged(); + emit containsIncompleteCommandChanged(); } } @@ -392,9 +397,11 @@ InputBar::send() auto wasEdit = !room->edit().isEmpty(); - if (auto [commandName, args] = getCommandAndArgs(); commandName.isEmpty()) - message(text()); - else if (!command(commandName, args)) + auto [commandName, args] = getCommandAndArgs(); + updateTextContentProperties(text()); + if (containsIncompleteCommand_) + return; + if (commandName.isEmpty() || !command(commandName, args)) message(text()); if (!wasEdit) { @@ -758,9 +765,8 @@ InputBar::video(const QString &filename, } QPair -InputBar::getCommandAndArgs() const +InputBar::getCommandAndArgs(const QString ¤tText) const { - const auto currentText = text(); if (!currentText.startsWith('/')) return {{}, currentText}; diff --git a/src/timeline/InputBar.h b/src/timeline/InputBar.h index 94aedaf6..acafd964 100644 --- a/src/timeline/InputBar.h +++ b/src/timeline/InputBar.h @@ -175,6 +175,8 @@ class InputBar final : public QObject Q_PROPERTY(bool containsAtRoom READ containsAtRoom NOTIFY containsAtRoomChanged) Q_PROPERTY( bool containsInvalidCommand READ containsInvalidCommand NOTIFY containsInvalidCommandChanged) + Q_PROPERTY(bool containsIncompleteCommand READ containsIncompleteCommand NOTIFY + containsIncompleteCommandChanged) Q_PROPERTY(QString currentCommand READ currentCommand NOTIFY currentCommandChanged) Q_PROPERTY(QString text READ text NOTIFY textChanged) Q_PROPERTY(QVariantList uploads READ uploads NOTIFY uploadsChanged) @@ -202,6 +204,7 @@ public slots: [[nodiscard]] bool containsAtRoom() const { return containsAtRoom_; } bool containsInvalidCommand() const { return containsInvalidCommand_; } + bool containsIncompleteCommand() const { return containsIncompleteCommand_; } QString currentCommand() const { return currentCommand_; } void send(); @@ -231,6 +234,7 @@ signals: void uploadingChanged(bool value); void containsAtRoomChanged(); void containsInvalidCommandChanged(); + void containsIncompleteCommandChanged(); void currentCommandChanged(); void uploadsChanged(); @@ -274,7 +278,8 @@ private: const QSize &thumbnailDimensions, const QString &blurhash); - QPair getCommandAndArgs() const; + QPair getCommandAndArgs() const { return getCommandAndArgs(text()); } + QPair getCommandAndArgs(const QString ¤tText) const; mtx::common::Relations generateRelations() const; void startUploadFromPath(const QString &path); @@ -296,9 +301,10 @@ private: std::deque history_; std::size_t history_index_ = 0; int selectionStart = 0, selectionEnd = 0, cursorPosition = 0; - bool uploading_ = false; - bool containsAtRoom_ = false; - bool containsInvalidCommand_ = false; + bool uploading_ = false; + bool containsAtRoom_ = false; + bool containsInvalidCommand_ = false; + bool containsIncompleteCommand_ = false; QString currentCommand_; using UploadHandle = std::unique_ptr;