diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 38cded4a3..1aadc203f 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -627,6 +627,7 @@ Tap the + to start adding people."; "room_command_reset_user_power_level_description" = "Deops user with given id"; "room_command_change_room_topic_description" = "Sets the room topic"; "room_command_discard_session_description" = "Forces the current outbound group session in an encrypted room to be discarded"; +"room_command_error_unknown_command" = "Invalid or unhandled command"; // MARK: Threads "room_thread_title" = "Thread"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index e48764f1d..db052a786 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -5231,6 +5231,10 @@ public class VectorL10n: NSObject { public static var roomCommandEmoteDescription: String { return VectorL10n.tr("Vector", "room_command_emote_description") } + /// Invalid or unhandled command + public static var roomCommandErrorUnknownCommand: String { + return VectorL10n.tr("Vector", "room_command_error_unknown_command") + } /// Invites user with given id to current room public static var roomCommandInviteUserDescription: String { return VectorL10n.tr("Vector", "room_command_invite_user_description") diff --git a/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarView.h b/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarView.h index e366ae239..abd67ec7e 100644 --- a/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarView.h +++ b/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarView.h @@ -102,6 +102,14 @@ typedef enum : NSUInteger */ - (void)roomInputToolbarView:(MXKRoomInputToolbarView *)toolbarView sendFormattedTextMessage:(NSString *)formattedTextMessage withRawText:(NSString *)rawText; +/** + Tells the delegate that the user wants to send a command. + + @param toolbarView the room input toolbar view. + @param commandText the command to send. + */ +- (void)roomInputToolbarView:(MXKRoomInputToolbarView *)toolbarView sendCommand:(NSString *)commandText; + /** Tells the delegate that the user wants to display the send media actions. diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 274e7d437..7646a135e 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5190,6 +5190,27 @@ static CGSize kThreadListBarButtonItemImageSize; }]; } +- (void)roomInputToolbarView:(MXKRoomInputToolbarView *)toolbarView sendCommand:(NSString *)commandText +{ + // Create before sending the message in case of a discussion (direct chat) + MXWeakify(self); + [self createDiscussionIfNeeded:^(BOOL readyToSend) { + MXStrongifyAndReturnIfNil(self); + + if (readyToSend) { + if (![self sendAsIRCStyleCommandIfPossible:commandText]) + { + // Display an error for unknown command + UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil + message:[VectorL10n roomCommandErrorUnknownCommand] + preferredStyle:UIAlertControllerStyleAlert]; + [alert addAction:[UIAlertAction actionWithTitle:[VectorL10n ok] style:UIAlertActionStyleDefault handler:nil]]; + [self presentViewController:alert animated:YES completion:nil]; + } + } + }]; +} + - (void)roomInputToolbarViewShowSendMediaActions:(MXKRoomInputToolbarView *)toolbarView { NSMutableArray *actionItems = [NSMutableArray new]; diff --git a/Riot/Modules/Room/RoomViewController.swift b/Riot/Modules/Room/RoomViewController.swift index c94111be4..727ca8f80 100644 --- a/Riot/Modules/Room/RoomViewController.swift +++ b/Riot/Modules/Room/RoomViewController.swift @@ -107,7 +107,7 @@ extension RoomViewController { "event_id": eventModified.eventId ]) }) - } else if !self.send(asIRCStyleCommandIfPossible: rawTextMsg) { + } else { roomDataSource.sendFormattedTextMessage(rawTextMsg, html: htmlMsg) { response in switch response { case .success: diff --git a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift index 9bc02c21e..ad488897f 100644 --- a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift +++ b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift @@ -338,7 +338,30 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp } private func sendWysiwygMessage(content: WysiwygComposerContent) { - delegate?.roomInputToolbarView?(self, sendFormattedTextMessage: content.html, withRawText: content.markdown) + if content.markdown.prefix(while: { $0 == "/" }).count == 1 { + let commandText: String + if content.markdown.hasPrefix(MXKSlashCommand.emote.cmd) { + // `/me` command works with markdown content + commandText = content.markdown + } else if #available(iOS 15.0, *) { + // Other commands should see pills replaced by matrix identifiers + commandText = PillsFormatter.stringByReplacingPills(in: self.wysiwygViewModel.textView.attributedText, mode: .identifier) + } else { + // Without Pills support, just use the raw text for command + commandText = self.wysiwygViewModel.textView.text + } + + // Fix potential command failures due to trailing characters + // or NBSP that are not properly handled by the command interpreter + let sanitizedCommand = commandText + .trimmingCharacters(in: .whitespacesAndNewlines) + .replacingOccurrences(of: String.nbsp, with: " ") + + delegate?.roomInputToolbarView?(self, sendCommand: sanitizedCommand) + } else { + delegate?.roomInputToolbarView?(self, sendFormattedTextMessage: content.html, withRawText: content.markdown) + } + if isMaximised { minimise() }