Add support for slash commands (under Feature Flag) (#6482)

* Add support for slash commands

* Update screenshots

* Rename module `slash` to `slashcommands`

* Rename `SlashCommand` to `SlashCommandService`

* Introduce MsgType in order to send text message with a different msgtype value.

* Format file and add parameter names, add default values and cleanup

* Add isSupported parameter to filter out unsupported yet commands.

* Slash commands: disable suggestions if the feature is disabled.

* Fix sending shrug command.

* Add missing test on SuggestionsProcessor

* Add tests on MessageComposerPresenter about slash command.

* Fix import ordering

* Add missing tests on CommandExecutor

* Add missing tests in MarkdownTextEditorStateTest

* Slash commands: Improve code when sending message with prefix.

* Slash commands: Add support for /unflip

---------

Co-authored-by: ElementBot <android@element.io>
This commit is contained in:
Benoit Marty 2026-04-02 16:15:32 +02:00 committed by GitHub
parent f08d1ed686
commit a77662421c
65 changed files with 3038 additions and 86 deletions

View file

@ -14,6 +14,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.matrix.api.core.RoomAlias
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.slashcommands.api.SlashCommandSuggestion
@Immutable
sealed interface ResolvedSuggestion {
@ -32,4 +33,8 @@ sealed interface ResolvedSuggestion {
size = size,
)
}
data class Command(
val command: SlashCommandSuggestion,
) : ResolvedSuggestion
}

View file

@ -61,21 +61,29 @@ class MarkdownTextEditorState(
}
is ResolvedSuggestion.Member -> {
val currentText = SpannableStringBuilder(text.value())
val mentionSpan = mentionSpanProvider.createUserMentionSpan(resolvedSuggestion.roomMember.userId)
val userId = resolvedSuggestion.roomMember.userId
val mentionSpan = mentionSpanProvider.createUserMentionSpan(userId)
currentText.replace(suggestion.start, suggestion.end, "@ ")
val end = suggestion.start + 1
currentText.setSpan(mentionSpan, suggestion.start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
this.text.update(currentText, true)
this.selection = IntRange(end + 1, end + 1)
text.update(currentText, true)
selection = IntRange(end + 1, end + 1)
}
is ResolvedSuggestion.Alias -> {
val currentText = SpannableStringBuilder(text.value())
val mentionSpan = mentionSpanProvider.createRoomMentionSpan(resolvedSuggestion.roomAlias.toRoomIdOrAlias())
val roomAlias = resolvedSuggestion.roomAlias
val mentionSpan = mentionSpanProvider.createRoomMentionSpan(roomAlias.toRoomIdOrAlias())
currentText.replace(suggestion.start, suggestion.end, "# ")
val end = suggestion.start + 1
currentText.setSpan(mentionSpan, suggestion.start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
this.text.update(currentText, true)
this.selection = IntRange(end + 1, end + 1)
text.update(currentText, true)
selection = IntRange(end + 1, end + 1)
}
is ResolvedSuggestion.Command -> {
// Just insert the command text
text.update("${resolvedSuggestion.command.command} ", true)
val length = resolvedSuggestion.command.command.length + 1
selection = IntRange(length, length)
}
}
}