diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 5c982270..100ed1d7 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -159,17 +159,8 @@ Item { } 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; - } + text: qsTr("The command /%1 is not recognized and will be sent as part of your message").arg(room ? room.input.currentCommand : "") + isVisible: room ? room.input.containsInvalidCommand : false } ReplyPopup { diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp index 5184ba94..6c882cd4 100644 --- a/src/timeline/InputBar.cpp +++ b/src/timeline/InputBar.cpp @@ -224,8 +224,9 @@ InputBar::insertMimeData(const QMimeData *md) } void -InputBar::updateAtRoom(const QString &t) +InputBar::updateTextContentProperties(const QString &t) { + // check for @room bool roomMention = false; if (t.size() > 4) { @@ -249,6 +250,54 @@ InputBar::updateAtRoom(const QString &t) this->containsAtRoom_ = roomMention; emit containsAtRoomChanged(); } + + // check for invalid commands + auto commandName = getCommandAndArgs().first; + bool hasInvalidCommand{}; + if (!commandName.isNull() && '/' + commandName != text()) { + 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")}; + hasInvalidCommand = !validCommands.contains(commandName); + } else + hasInvalidCommand = false; + + if (containsInvalidCommand_ != hasInvalidCommand) { + containsInvalidCommand_ = hasInvalidCommand; + emit containsInvalidCommandChanged(); + } + if (currentCommand_ != commandName) { + currentCommand_ = commandName; + emit currentCommandChanged(); + } } void @@ -263,7 +312,7 @@ InputBar::setText(const QString &newText) if (history_.size() == INPUT_HISTORY_SIZE) history_.pop_back(); - updateAtRoom(QLatin1String("")); + updateTextContentProperties(QLatin1String("")); emit textChanged(newText); } void @@ -284,7 +333,7 @@ InputBar::updateState(int selectionStart_, history_.front() = text_; history_index_ = 0; - updateAtRoom(text_); + updateTextContentProperties(text_); // disabled, as it moves the cursor to the end // emit textChanged(text_); } @@ -312,7 +361,7 @@ InputBar::previousText() else if (text().isEmpty()) history_index_--; - updateAtRoom(text()); + updateTextContentProperties(text()); return text(); } @@ -323,7 +372,7 @@ InputBar::nextText() if (history_index_ >= INPUT_HISTORY_SIZE) history_index_ = 0; - updateAtRoom(text()); + updateTextContentProperties(text()); return text(); } @@ -341,20 +390,11 @@ InputBar::send() auto wasEdit = !room->edit().isEmpty(); - if (text().startsWith('/')) { - int command_end = text().indexOf(QRegularExpression(QStringLiteral("\\s"))); - if (command_end == -1) - command_end = text().size(); - auto name = text().mid(1, command_end - 1); - auto args = text().mid(command_end + 1); - if (name.isEmpty() || name == QLatin1String("/")) { - message(args); - } else { - command(name, args); - } - } else { + auto [commandName, args] = getCommandAndArgs(); + if (commandName.isNull()) message(text()); - } + else + command(commandName, args); if (!wasEdit) { history_.push_front(QLatin1String("")); @@ -716,6 +756,24 @@ InputBar::video(const QString &filename, room->sendMessageEvent(video, mtx::events::EventType::RoomMessage); } +QPair +InputBar::getCommandAndArgs() const +{ + if (!text().startsWith('/')) + return {{}, text()}; + + int command_end = text().indexOf(QRegularExpression(QStringLiteral("\\s"))); + if (command_end == -1) + command_end = text().size(); + auto name = text().mid(1, command_end - 1); + auto args = text().mid(command_end + 1); + if (name.isEmpty() || name == QLatin1String("/")) { + return {{}, text()}; + } else { + return {name, args}; + } +} + void InputBar::sticker(CombinedImagePackModel *model, int row) { diff --git a/src/timeline/InputBar.h b/src/timeline/InputBar.h index 80ad7f47..7385d272 100644 --- a/src/timeline/InputBar.h +++ b/src/timeline/InputBar.h @@ -173,6 +173,9 @@ class InputBar final : public QObject Q_OBJECT Q_PROPERTY(bool uploading READ uploading NOTIFY uploadingChanged) Q_PROPERTY(bool containsAtRoom READ containsAtRoom NOTIFY containsAtRoomChanged) + Q_PROPERTY( + bool containsInvalidCommand READ containsInvalidCommand NOTIFY containsInvalidCommandChanged) + Q_PROPERTY(QString currentCommand READ currentCommand NOTIFY currentCommandChanged) Q_PROPERTY(QString text READ text NOTIFY textChanged) Q_PROPERTY(QVariantList uploads READ uploads NOTIFY uploadsChanged) @@ -198,6 +201,8 @@ public slots: void setText(const QString &newText); [[nodiscard]] bool containsAtRoom() const { return containsAtRoom_; } + bool containsInvalidCommand() const { return containsInvalidCommand_; } + QString currentCommand() const { return currentCommand_; } void send(); bool tryPasteAttachment(bool fromMouse); @@ -225,6 +230,8 @@ signals: void textChanged(QString newText); void uploadingChanged(bool value); void containsAtRoomChanged(); + void containsInvalidCommandChanged(); + void currentCommandChanged(); void uploadsChanged(); private: @@ -267,6 +274,7 @@ private: const QSize &thumbnailDimensions, const QString &blurhash); + QPair getCommandAndArgs() const; mtx::common::Relations generateRelations() const; void startUploadFromPath(const QString &path); @@ -280,7 +288,7 @@ private: } } - void updateAtRoom(const QString &t); + void updateTextContentProperties(const QString &t); QTimer typingRefresh_; QTimer typingTimeout_; @@ -288,8 +296,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 uploading_ = false; + bool containsAtRoom_ = false; + bool containsInvalidCommand_ = false; + QString currentCommand_; using UploadHandle = std::unique_ptr; std::vector unconfirmedUploads; diff --git a/src/ui/NhekoGlobalObject.cpp b/src/ui/NhekoGlobalObject.cpp index 3cb6a8a8..a6f9abe7 100644 --- a/src/ui/NhekoGlobalObject.cpp +++ b/src/ui/NhekoGlobalObject.cpp @@ -190,63 +190,3 @@ 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 1ea2c109..b7a7a637 100644 --- a/src/ui/NhekoGlobalObject.h +++ b/src/ui/NhekoGlobalObject.h @@ -72,9 +72,6 @@ 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();