diff --git a/vector/src/main/java/im/vector/app/features/command/Command.kt b/vector/src/main/java/im/vector/app/features/command/Command.kt index db429f9e58..c1b30b2744 100644 --- a/vector/src/main/java/im/vector/app/features/command/Command.kt +++ b/vector/src/main/java/im/vector/app/features/command/Command.kt @@ -28,8 +28,11 @@ enum class Command(val command: String, val parameters: String, @StringRes val d EMOTE("/me", "", R.string.command_description_emote), BAN_USER("/ban", " [reason]", R.string.command_description_ban_user), UNBAN_USER("/unban", " [reason]", R.string.command_description_unban_user), + IGNORE_USER("/ignore", " [reason]", R.string.command_description_ignore_user), + UNIGNORE_USER("/unignore", "", R.string.command_description_unignore_user), SET_USER_POWER_LEVEL("/op", " []", R.string.command_description_op_user), RESET_USER_POWER_LEVEL("/deop", "", R.string.command_description_deop_user), + ROOM_NAME("/roomname", " [reason]", R.string.command_description_room_name), INVITE("/invite", " [reason]", R.string.command_description_invite_user), JOIN_ROOM("/join", " [reason]", R.string.command_description_join_room), PART("/part", " [reason]", R.string.command_description_part_room), @@ -42,8 +45,10 @@ enum class Command(val command: String, val parameters: String, @StringRes val d CLEAR_SCALAR_TOKEN("/clear_scalar_token", "", R.string.command_description_clear_scalar_token), SPOILER("/spoiler", "", R.string.command_description_spoiler), POLL("/poll", "Question | Option 1 | Option 2 ...", R.string.command_description_poll), - SHRUG("/shrug", "", R.string.command_description_shrug), + SHRUG("/shrug", "[]", R.string.command_description_shrug), + LENNY("/lenny", "[]", R.string.command_description_lenny), PLAIN("/plain", "", R.string.command_description_plain), + WHOIS("/whois", "", R.string.command_description_whois), DISCARD_SESSION("/discardsession", "", R.string.command_description_discard_session); val length diff --git a/vector/src/main/java/im/vector/app/features/command/CommandParser.kt b/vector/src/main/java/im/vector/app/features/command/CommandParser.kt index 94de6bf265..fd7d587c1c 100644 --- a/vector/src/main/java/im/vector/app/features/command/CommandParser.kt +++ b/vector/src/main/java/im/vector/app/features/command/CommandParser.kt @@ -102,7 +102,7 @@ object CommandParser { ParsedCommand.SendRainbowEmote(message) } - Command.JOIN_ROOM.command -> { + Command.JOIN_ROOM.command -> { if (messageParts.size >= 2) { val roomAlias = messageParts[1] @@ -120,7 +120,7 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.JOIN_ROOM) } } - Command.PART.command -> { + Command.PART.command -> { if (messageParts.size >= 2) { val roomAlias = messageParts[1] @@ -138,7 +138,16 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.PART) } } - Command.INVITE.command -> { + Command.ROOM_NAME.command -> { + val newRoomName = textMessage.substring(Command.ROOM_NAME.command.length).trim() + + if (newRoomName.isNotEmpty()) { + ParsedCommand.ChangeRoomName(newRoomName) + } else { + ParsedCommand.ErrorSyntax(Command.ROOM_NAME) + } + } + Command.INVITE.command -> { if (messageParts.size >= 2) { val userId = messageParts[1] @@ -183,7 +192,7 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.KICK_USER) } } - Command.BAN_USER.command -> { + Command.BAN_USER.command -> { if (messageParts.size >= 2) { val userId = messageParts[1] @@ -201,7 +210,7 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.BAN_USER) } } - Command.UNBAN_USER.command -> { + Command.UNBAN_USER.command -> { if (messageParts.size >= 2) { val userId = messageParts[1] @@ -219,7 +228,33 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.UNBAN_USER) } } - Command.SET_USER_POWER_LEVEL.command -> { + Command.IGNORE_USER.command -> { + if (messageParts.size == 2) { + val userId = messageParts[1] + + if (MatrixPatterns.isUserId(userId)) { + ParsedCommand.IgnoreUser(userId) + } else { + ParsedCommand.ErrorSyntax(Command.IGNORE_USER) + } + } else { + ParsedCommand.ErrorSyntax(Command.IGNORE_USER) + } + } + Command.UNIGNORE_USER.command -> { + if (messageParts.size == 2) { + val userId = messageParts[1] + + if (MatrixPatterns.isUserId(userId)) { + ParsedCommand.UnignoreUser(userId) + } else { + ParsedCommand.ErrorSyntax(Command.UNIGNORE_USER) + } + } else { + ParsedCommand.ErrorSyntax(Command.UNIGNORE_USER) + } + } + Command.SET_USER_POWER_LEVEL.command -> { if (messageParts.size == 3) { val userId = messageParts[1] if (MatrixPatterns.isUserId(userId)) { @@ -252,7 +287,7 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.SET_USER_POWER_LEVEL) } } - Command.MARKDOWN.command -> { + Command.MARKDOWN.command -> { if (messageParts.size == 2) { when { "on".equals(messageParts[1], true) -> ParsedCommand.SetMarkdown(true) @@ -263,23 +298,28 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.MARKDOWN) } } - Command.CLEAR_SCALAR_TOKEN.command -> { + Command.CLEAR_SCALAR_TOKEN.command -> { if (messageParts.size == 1) { ParsedCommand.ClearScalarToken } else { ParsedCommand.ErrorSyntax(Command.CLEAR_SCALAR_TOKEN) } } - Command.SPOILER.command -> { + Command.SPOILER.command -> { val message = textMessage.substring(Command.SPOILER.command.length).trim() ParsedCommand.SendSpoiler(message) } - Command.SHRUG.command -> { + Command.SHRUG.command -> { val message = textMessage.substring(Command.SHRUG.command.length).trim() ParsedCommand.SendShrug(message) } - Command.POLL.command -> { + Command.LENNY.command -> { + val message = textMessage.substring(Command.LENNY.command.length).trim() + + ParsedCommand.SendLenny(message) + } + Command.POLL.command -> { val rawCommand = textMessage.substring(Command.POLL.command.length).trim() val split = rawCommand.split("|").map { it.trim() } if (split.size > 2) { @@ -288,10 +328,23 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.POLL) } } - Command.DISCARD_SESSION.command -> { + Command.DISCARD_SESSION.command -> { ParsedCommand.DiscardSession } - else -> { + Command.WHOIS.command -> { + if (messageParts.size == 2) { + val userId = messageParts[1] + + if (MatrixPatterns.isUserId(userId)) { + ParsedCommand.ShowUser(userId) + } else { + ParsedCommand.ErrorSyntax(Command.WHOIS) + } + } else { + ParsedCommand.ErrorSyntax(Command.WHOIS) + } + } + else -> { // Unknown command ParsedCommand.ErrorUnknownSlashCommand(slashCommand) } diff --git a/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt b/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt index bdfa7779fb..54043f343a 100644 --- a/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt +++ b/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt @@ -41,7 +41,10 @@ sealed class ParsedCommand { class SendRainbowEmote(val message: CharSequence) : ParsedCommand() class BanUser(val userId: String, val reason: String?) : ParsedCommand() class UnbanUser(val userId: String, val reason: String?) : ParsedCommand() + class IgnoreUser(val userId: String) : ParsedCommand() + class UnignoreUser(val userId: String) : ParsedCommand() class SetUserPowerLevel(val userId: String, val powerLevel: Int?) : ParsedCommand() + class ChangeRoomName(val name: String) : ParsedCommand() class Invite(val userId: String, val reason: String?) : ParsedCommand() class Invite3Pid(val threePid: ThreePid) : ParsedCommand() class JoinRoom(val roomAlias: String, val reason: String?) : ParsedCommand() @@ -53,6 +56,8 @@ sealed class ParsedCommand { object ClearScalarToken : ParsedCommand() class SendSpoiler(val message: String) : ParsedCommand() class SendShrug(val message: CharSequence) : ParsedCommand() + class SendLenny(val message: CharSequence) : ParsedCommand() class SendPoll(val question: String, val options: List) : ParsedCommand() - object DiscardSession: ParsedCommand() + object DiscardSession : ParsedCommand() + class ShowUser(val userId: String) : ParsedCommand() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 51aeda2aab..a9c2307566 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -359,6 +359,7 @@ class RoomDetailFragment @Inject constructor( is RoomDetailViewEvents.SendMessageResult -> renderSendMessageResult(it) is RoomDetailViewEvents.ShowE2EErrorMessage -> displayE2eError(it.withHeldCode) RoomDetailViewEvents.DisplayPromptForIntegrationManager -> displayPromptForIntegrationManager() + is RoomDetailViewEvents.OpenRoomMemberProfile -> openRoomMemberProfile(it.userId) is RoomDetailViewEvents.OpenStickerPicker -> openStickerPicker(it) is RoomDetailViewEvents.DisplayEnableIntegrationsWarning -> displayDisabledIntegrationDialog() is RoomDetailViewEvents.OpenIntegrationManager -> openIntegrationManager() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt index 29ed43f17d..b747075622 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt @@ -64,14 +64,16 @@ sealed class RoomDetailViewEvents : VectorViewEvents { abstract class SendMessageResult : RoomDetailViewEvents() - object DisplayPromptForIntegrationManager: RoomDetailViewEvents() + object DisplayPromptForIntegrationManager : RoomDetailViewEvents() - object DisplayEnableIntegrationsWarning: RoomDetailViewEvents() + object DisplayEnableIntegrationsWarning : RoomDetailViewEvents() - data class OpenStickerPicker(val widget: Widget): RoomDetailViewEvents() + data class OpenRoomMemberProfile(val userId: String) : RoomDetailViewEvents() - object OpenIntegrationManager: RoomDetailViewEvents() - object OpenActiveWidgetBottomSheet: RoomDetailViewEvents() + data class OpenStickerPicker(val widget: Widget) : RoomDetailViewEvents() + + object OpenIntegrationManager : RoomDetailViewEvents() + object OpenActiveWidgetBottomSheet : RoomDetailViewEvents() data class RequestNativeWidgetPermission(val widget: Widget, val domain: String, val grantedEvents: RoomDetailViewEvents) : RoomDetailViewEvents() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 1b5e928843..2e7109b5e7 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -571,6 +571,10 @@ class RoomDetailViewModel @AssistedInject constructor( _viewEvents.post(RoomDetailViewEvents.MessageSent) popDraft() } + is ParsedCommand.ChangeRoomName -> { + handleChangeRoomNameSlashCommand(slashCommandResult) + popDraft() + } is ParsedCommand.Invite -> { handleInviteSlashCommand(slashCommandResult) popDraft() @@ -593,12 +597,20 @@ class RoomDetailViewModel @AssistedInject constructor( if (slashCommandResult.enable) R.string.markdown_has_been_enabled else R.string.markdown_has_been_disabled)) popDraft() } + is ParsedCommand.BanUser -> { + handleBanSlashCommand(slashCommandResult) + popDraft() + } is ParsedCommand.UnbanUser -> { handleUnbanSlashCommand(slashCommandResult) popDraft() } - is ParsedCommand.BanUser -> { - handleBanSlashCommand(slashCommandResult) + is ParsedCommand.IgnoreUser -> { + handleIgnoreSlashCommand(slashCommandResult) + popDraft() + } + is ParsedCommand.UnignoreUser -> { + handleUnignoreSlashCommand(slashCommandResult) popDraft() } is ParsedCommand.KickUser -> { @@ -641,14 +653,12 @@ class RoomDetailViewModel @AssistedInject constructor( popDraft() } is ParsedCommand.SendShrug -> { - val sequence = buildString { - append("¯\\_(ツ)_/¯") - if (slashCommandResult.message.isNotEmpty()) { - append(" ") - append(slashCommandResult.message) - } - } - room.sendTextMessage(sequence) + sendPrefixedMessage("¯\\_(ツ)_/¯", slashCommandResult.message) + _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) + popDraft() + } + is ParsedCommand.SendLenny -> { + sendPrefixedMessage("( ͡° ͜ʖ ͡°)", slashCommandResult.message) _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) popDraft() } @@ -665,6 +675,11 @@ class RoomDetailViewModel @AssistedInject constructor( handleChangeDisplayNameSlashCommand(slashCommandResult) popDraft() } + is ParsedCommand.ShowUser -> { + _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) + handleWhoisSlashCommand(slashCommandResult) + popDraft() + } is ParsedCommand.DiscardSession -> { if (room.isEncrypted()) { session.cryptoService().discardOutboundSession(room.roomId) @@ -786,6 +801,12 @@ class RoomDetailViewModel @AssistedInject constructor( } } + private fun handleChangeRoomNameSlashCommand(changeRoomName: ParsedCommand.ChangeRoomName) { + launchSlashCommandFlow { + room.updateName(changeRoomName.name, it) + } + } + private fun handleInviteSlashCommand(invite: ParsedCommand.Invite) { launchSlashCommandFlow { room.invite(invite.userId, invite.reason, it) @@ -833,6 +854,33 @@ class RoomDetailViewModel @AssistedInject constructor( } } + private fun handleIgnoreSlashCommand(ignore: ParsedCommand.IgnoreUser) { + launchSlashCommandFlow { + session.ignoreUserIds(listOf(ignore.userId), it) + } + } + + private fun handleUnignoreSlashCommand(unignore: ParsedCommand.UnignoreUser) { + launchSlashCommandFlow { + session.unIgnoreUserIds(listOf(unignore.userId), it) + } + } + + private fun handleWhoisSlashCommand(whois: ParsedCommand.ShowUser) { + _viewEvents.post(RoomDetailViewEvents.OpenRoomMemberProfile(whois.userId)) + } + + private fun sendPrefixedMessage(prefix: String, message: CharSequence) { + val sequence = buildString { + append(prefix) + if (message.isNotEmpty()) { + append(" ") + append(message) + } + } + room.sendTextMessage(sequence) + } + private fun launchSlashCommandFlow(lambda: (MatrixCallback) -> Unit) { _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) val matrixCallback = object : MatrixCallback { diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index c025054f98..f87b45cd05 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1287,8 +1287,11 @@ Displays action Bans user with given id Unbans user with given id + Ignores a user, hiding their messages from you + Stops ignoring a user, showing their messages going forward Define the power level of a user Deops user with given id + Sets the room name Invites user with given id to current room Joins room with given alias Leave room @@ -1297,6 +1300,7 @@ Changes your display nickname On/Off markdown To fix Matrix Apps management + Displays information about a user Markdown has been enabled. Markdown has been disabled. @@ -2063,6 +2067,7 @@ Element may crash more often when an unexpected error occurs Prepends ¯\\_(ツ)_/¯ to a plain-text message + Prepends ( ͡° ͜ʖ ͡°) to a plain-text message "Enable encryption" "Once enabled, encryption cannot be disabled."