Supporting command in threads
This commit is contained in:
parent
8c539426e6
commit
4160688f83
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.api.session.room.model.relation
|
|||
import androidx.lifecycle.LiveData
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
|
@ -136,6 +137,8 @@ interface RelationService {
|
|||
* @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present
|
||||
*/
|
||||
fun replyInThread(rootThreadEventId: String,
|
||||
replyInThreadText: CharSequence,
|
||||
autoMarkdown: Boolean = false): Cancelable?
|
||||
replyInThreadText: CharSequence,
|
||||
msgType: String = MessageType.MSGTYPE_TEXT,
|
||||
autoMarkdown: Boolean = false,
|
||||
formattedText: String? = null): Cancelable?
|
||||
}
|
||||
|
|
|
@ -159,12 +159,15 @@ internal class DefaultRelationService @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun replyInThread(rootThreadEventId: String, replyInThreadText: CharSequence, autoMarkdown: Boolean): Cancelable {
|
||||
override fun replyInThread(rootThreadEventId: String, replyInThreadText: CharSequence, msgType: String, autoMarkdown: Boolean, formattedText: String?): Cancelable {
|
||||
val event = eventFactory.createThreadTextEvent(
|
||||
rootThreadEventId = rootThreadEventId,
|
||||
roomId = roomId,
|
||||
text = replyInThreadText.toString(),
|
||||
autoMarkdown = autoMarkdown)
|
||||
msgType = msgType,
|
||||
autoMarkdown = autoMarkdown,
|
||||
formattedText = formattedText
|
||||
)
|
||||
// .also {
|
||||
// saveLocalEcho(it)
|
||||
// }
|
||||
|
|
|
@ -343,13 +343,21 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
/**
|
||||
* Creates a thread event related to the already existing event
|
||||
*/
|
||||
fun createThreadTextEvent(rootThreadEventId: String, roomId:String, text: String, autoMarkdown: Boolean): Event =
|
||||
createEvent(
|
||||
roomId,
|
||||
EventType.MESSAGE,
|
||||
createTextContent(text, autoMarkdown)
|
||||
.toThreadTextContent(rootThreadEventId)
|
||||
.toContent())
|
||||
fun createThreadTextEvent(
|
||||
rootThreadEventId: String,
|
||||
roomId: String,
|
||||
text: String,
|
||||
msgType: String,
|
||||
autoMarkdown: Boolean,
|
||||
formattedText: String?): Event {
|
||||
|
||||
val content = formattedText?.let { TextContent(text, it) } ?: createTextContent(text, autoMarkdown)
|
||||
return createEvent(
|
||||
roomId,
|
||||
EventType.MESSAGE,
|
||||
content.toThreadTextContent(rootThreadEventId, msgType)
|
||||
.toContent())
|
||||
}
|
||||
|
||||
private fun dummyOriginServerTs(): Long {
|
||||
return System.currentTimeMillis()
|
||||
|
|
|
@ -18,17 +18,30 @@ package im.vector.app.features.autocomplete.command
|
|||
|
||||
import android.content.Context
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import im.vector.app.BuildConfig
|
||||
import im.vector.app.features.autocomplete.AutocompleteClickListener
|
||||
import im.vector.app.features.autocomplete.RecyclerViewPresenter
|
||||
import im.vector.app.features.command.Command
|
||||
import im.vector.app.features.home.room.detail.AutoCompleter
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class AutocompleteCommandPresenter @Inject constructor(context: Context,
|
||||
private val controller: AutocompleteCommandController,
|
||||
private val vectorPreferences: VectorPreferences) :
|
||||
class AutocompleteCommandPresenter @AssistedInject constructor(
|
||||
@Assisted val isInThreadTimeline: Boolean,
|
||||
context: Context,
|
||||
private val controller: AutocompleteCommandController,
|
||||
private val vectorPreferences: VectorPreferences) :
|
||||
RecyclerViewPresenter<Command>(context), AutocompleteClickListener<Command> {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(isFromThreadTimeline: Boolean): AutocompleteCommandPresenter
|
||||
}
|
||||
|
||||
init {
|
||||
controller.listener = this
|
||||
}
|
||||
|
@ -46,6 +59,12 @@ class AutocompleteCommandPresenter @Inject constructor(context: Context,
|
|||
.filter {
|
||||
!it.isDevCommand || vectorPreferences.developerMode()
|
||||
}
|
||||
.filter {
|
||||
if (BuildConfig.THREADING_ENABLED && isInThreadTimeline) {
|
||||
it.isThreadCommand
|
||||
} else
|
||||
true
|
||||
}
|
||||
.filter {
|
||||
if (query.isNullOrEmpty()) {
|
||||
true
|
||||
|
|
|
@ -24,42 +24,42 @@ import im.vector.app.R
|
|||
* the user can write theses messages to perform some actions
|
||||
* the list will be displayed in this order
|
||||
*/
|
||||
enum class Command(val command: String, val parameters: String, @StringRes val description: Int, val isDevCommand: Boolean) {
|
||||
EMOTE("/me", "<message>", R.string.command_description_emote, false),
|
||||
BAN_USER("/ban", "<user-id> [reason]", R.string.command_description_ban_user, false),
|
||||
UNBAN_USER("/unban", "<user-id> [reason]", R.string.command_description_unban_user, false),
|
||||
IGNORE_USER("/ignore", "<user-id> [reason]", R.string.command_description_ignore_user, false),
|
||||
UNIGNORE_USER("/unignore", "<user-id>", R.string.command_description_unignore_user, false),
|
||||
SET_USER_POWER_LEVEL("/op", "<user-id> [<power-level>]", R.string.command_description_op_user, false),
|
||||
RESET_USER_POWER_LEVEL("/deop", "<user-id>", R.string.command_description_deop_user, false),
|
||||
ROOM_NAME("/roomname", "<name>", R.string.command_description_room_name, false),
|
||||
INVITE("/invite", "<user-id> [reason]", R.string.command_description_invite_user, false),
|
||||
JOIN_ROOM("/join", "<room-alias> [reason]", R.string.command_description_join_room, false),
|
||||
PART("/part", "<room-alias> [reason]", R.string.command_description_part_room, false),
|
||||
TOPIC("/topic", "<topic>", R.string.command_description_topic, false),
|
||||
KICK_USER("/kick", "<user-id> [reason]", R.string.command_description_kick_user, false),
|
||||
CHANGE_DISPLAY_NAME("/nick", "<display-name>", R.string.command_description_nick, false),
|
||||
CHANGE_DISPLAY_NAME_FOR_ROOM("/myroomnick", "<display-name>", R.string.command_description_nick_for_room, false),
|
||||
ROOM_AVATAR("/roomavatar", "<mxc_url>", R.string.command_description_room_avatar, true /* Since user has to know the mxc url */),
|
||||
CHANGE_AVATAR_FOR_ROOM("/myroomavatar", "<mxc_url>", R.string.command_description_avatar_for_room, true /* Since user has to know the mxc url */),
|
||||
MARKDOWN("/markdown", "<on|off>", R.string.command_description_markdown, false),
|
||||
RAINBOW("/rainbow", "<message>", R.string.command_description_rainbow, false),
|
||||
RAINBOW_EMOTE("/rainbowme", "<message>", R.string.command_description_rainbow_emote, false),
|
||||
CLEAR_SCALAR_TOKEN("/clear_scalar_token", "", R.string.command_description_clear_scalar_token, false),
|
||||
SPOILER("/spoiler", "<message>", R.string.command_description_spoiler, false),
|
||||
POLL("/poll", "Question | Option 1 | Option 2 ...", R.string.command_description_poll, false),
|
||||
SHRUG("/shrug", "<message>", R.string.command_description_shrug, false),
|
||||
LENNY("/lenny", "<message>", R.string.command_description_lenny, false),
|
||||
PLAIN("/plain", "<message>", R.string.command_description_plain, false),
|
||||
WHOIS("/whois", "<user-id>", R.string.command_description_whois, false),
|
||||
DISCARD_SESSION("/discardsession", "", R.string.command_description_discard_session, false),
|
||||
CONFETTI("/confetti", "<message>", R.string.command_confetti, false),
|
||||
SNOWFALL("/snowfall", "<message>", R.string.command_snow, false),
|
||||
CREATE_SPACE("/createspace", "<name> <invitee>*", R.string.command_description_create_space, true),
|
||||
ADD_TO_SPACE("/addToSpace", "spaceId", R.string.command_description_add_to_space, true),
|
||||
JOIN_SPACE("/joinSpace", "spaceId", R.string.command_description_join_space, true),
|
||||
LEAVE_ROOM("/leave", "<roomId?>", R.string.command_description_leave_room, true),
|
||||
UPGRADE_ROOM("/upgraderoom", "newVersion", R.string.command_description_upgrade_room, true);
|
||||
enum class Command(val command: String, val parameters: String, @StringRes val description: Int, val isDevCommand: Boolean, val isThreadCommand: Boolean) {
|
||||
EMOTE("/me", "<message>", R.string.command_description_emote, false, true),
|
||||
BAN_USER("/ban", "<user-id> [reason]", R.string.command_description_ban_user, false, false),
|
||||
UNBAN_USER("/unban", "<user-id> [reason]", R.string.command_description_unban_user, false, false),
|
||||
IGNORE_USER("/ignore", "<user-id> [reason]", R.string.command_description_ignore_user, false, true),
|
||||
UNIGNORE_USER("/unignore", "<user-id>", R.string.command_description_unignore_user, false, true),
|
||||
SET_USER_POWER_LEVEL("/op", "<user-id> [<power-level>]", R.string.command_description_op_user, false, false),
|
||||
RESET_USER_POWER_LEVEL("/deop", "<user-id>", R.string.command_description_deop_user, false, false),
|
||||
ROOM_NAME("/roomname", "<name>", R.string.command_description_room_name, false, false),
|
||||
INVITE("/invite", "<user-id> [reason]", R.string.command_description_invite_user, false, false),
|
||||
JOIN_ROOM("/join", "<room-alias> [reason]", R.string.command_description_join_room, false, false),
|
||||
PART("/part", "<room-alias> [reason]", R.string.command_description_part_room, false, false),
|
||||
TOPIC("/topic", "<topic>", R.string.command_description_topic, false, false),
|
||||
KICK_USER("/kick", "<user-id> [reason]", R.string.command_description_kick_user, false, false),
|
||||
CHANGE_DISPLAY_NAME("/nick", "<display-name>", R.string.command_description_nick, false, false),
|
||||
CHANGE_DISPLAY_NAME_FOR_ROOM("/myroomnick", "<display-name>", R.string.command_description_nick_for_room, false, false),
|
||||
ROOM_AVATAR("/roomavatar", "<mxc_url>", R.string.command_description_room_avatar, true /* Since user has to know the mxc url */, false),
|
||||
CHANGE_AVATAR_FOR_ROOM("/myroomavatar", "<mxc_url>", R.string.command_description_avatar_for_room, true /* Since user has to know the mxc url */, false),
|
||||
MARKDOWN("/markdown", "<on|off>", R.string.command_description_markdown, false, false),
|
||||
RAINBOW("/rainbow", "<message>", R.string.command_description_rainbow, false, true),
|
||||
RAINBOW_EMOTE("/rainbowme", "<message>", R.string.command_description_rainbow_emote, false, true),
|
||||
CLEAR_SCALAR_TOKEN("/clear_scalar_token", "", R.string.command_description_clear_scalar_token, false, false),
|
||||
SPOILER("/spoiler", "<message>", R.string.command_description_spoiler, false, true),
|
||||
POLL("/poll", "Question | Option 1 | Option 2 ...", R.string.command_description_poll, false, false),
|
||||
SHRUG("/shrug", "<message>", R.string.command_description_shrug, false, true),
|
||||
LENNY("/lenny", "<message>", R.string.command_description_lenny, false, true),
|
||||
PLAIN("/plain", "<message>", R.string.command_description_plain, false, true),
|
||||
WHOIS("/whois", "<user-id>", R.string.command_description_whois, false, true),
|
||||
DISCARD_SESSION("/discardsession", "", R.string.command_description_discard_session, false, false),
|
||||
CONFETTI("/confetti", "<message>", R.string.command_confetti, false, false),
|
||||
SNOWFALL("/snowfall", "<message>", R.string.command_snow, false, false),
|
||||
CREATE_SPACE("/createspace", "<name> <invitee>*", R.string.command_description_create_space, true, false),
|
||||
ADD_TO_SPACE("/addToSpace", "spaceId", R.string.command_description_add_to_space, true, false),
|
||||
JOIN_SPACE("/joinSpace", "spaceId", R.string.command_description_join_space, true, false),
|
||||
LEAVE_ROOM("/leave", "<roomId?>", R.string.command_description_leave_room, true, false),
|
||||
UPGRADE_ROOM("/upgraderoom", "newVersion", R.string.command_description_upgrade_room, true, false);
|
||||
|
||||
val length
|
||||
get() = command.length + 1
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package im.vector.app.features.command
|
||||
|
||||
import im.vector.app.BuildConfig
|
||||
import im.vector.app.core.extensions.isEmail
|
||||
import im.vector.app.core.extensions.isMsisdn
|
||||
import im.vector.app.features.home.room.detail.ChatEffect
|
||||
|
@ -32,7 +33,7 @@ object CommandParser {
|
|||
* @param textMessage the text message
|
||||
* @return a parsed slash command (ok or error)
|
||||
*/
|
||||
fun parseSplashCommand(textMessage: CharSequence): ParsedCommand {
|
||||
fun parseSplashCommand(textMessage: CharSequence, isInThreadTimeline: Boolean): ParsedCommand {
|
||||
// check if it has the Slash marker
|
||||
if (!textMessage.startsWith("/")) {
|
||||
return ParsedCommand.ErrorNotACommand
|
||||
|
@ -61,6 +62,20 @@ object CommandParser {
|
|||
return ParsedCommand.ErrorEmptySlashCommand
|
||||
}
|
||||
|
||||
// If the command is not supported by threads return error
|
||||
|
||||
if(BuildConfig.THREADING_ENABLED && isInThreadTimeline){
|
||||
val slashCommand = messageParts.first()
|
||||
val notSupportedCommandsInThreads = Command.values().filter {
|
||||
!it.isThreadCommand
|
||||
}.map {
|
||||
it.command
|
||||
}
|
||||
if(notSupportedCommandsInThreads.contains(slashCommand)){
|
||||
return ParsedCommand.ErrorCommandNotSupportedInThreads(slashCommand)
|
||||
}
|
||||
}
|
||||
|
||||
return when (val slashCommand = messageParts.first()) {
|
||||
Command.PLAIN.command -> {
|
||||
val text = textMessage.substring(Command.PLAIN.command.length).trim()
|
||||
|
|
|
@ -28,6 +28,8 @@ sealed class ParsedCommand {
|
|||
|
||||
object ErrorEmptySlashCommand : ParsedCommand()
|
||||
|
||||
class ErrorCommandNotSupportedInThreads(val slashCommand: String) : ParsedCommand()
|
||||
|
||||
// Unknown/Unsupported slash command
|
||||
class ErrorUnknownSlashCommand(val slashCommand: String) : ParsedCommand()
|
||||
|
||||
|
|
|
@ -49,9 +49,10 @@ import org.matrix.android.sdk.api.util.toRoomAliasMatrixItem
|
|||
|
||||
class AutoCompleter @AssistedInject constructor(
|
||||
@Assisted val roomId: String,
|
||||
@Assisted val isInThreadTimeline: Boolean,
|
||||
private val avatarRenderer: AvatarRenderer,
|
||||
private val commandAutocompletePolicy: CommandAutocompletePolicy,
|
||||
private val autocompleteCommandPresenter: AutocompleteCommandPresenter,
|
||||
AutocompleteCommandPresenterFactory: AutocompleteCommandPresenter.Factory,
|
||||
private val autocompleteMemberPresenterFactory: AutocompleteMemberPresenter.Factory,
|
||||
private val autocompleteRoomPresenter: AutocompleteRoomPresenter,
|
||||
private val autocompleteGroupPresenter: AutocompleteGroupPresenter,
|
||||
|
@ -62,7 +63,11 @@ class AutoCompleter @AssistedInject constructor(
|
|||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(roomId: String): AutoCompleter
|
||||
fun create(roomId: String, isInThreadTimeline: Boolean): AutoCompleter
|
||||
}
|
||||
|
||||
private val autocompleteCommandPresenter: AutocompleteCommandPresenter by lazy {
|
||||
AutocompleteCommandPresenterFactory.create(isInThreadTimeline)
|
||||
}
|
||||
|
||||
private var editText: EditText? = null
|
||||
|
|
|
@ -291,8 +291,9 @@ class RoomDetailFragment @Inject constructor(
|
|||
}
|
||||
|
||||
private val autoCompleter: AutoCompleter by lazy {
|
||||
autoCompleterFactory.create(roomDetailArgs.roomId)
|
||||
autoCompleterFactory.create(roomDetailArgs.roomId, isThreadTimeLine())
|
||||
}
|
||||
|
||||
private val roomDetailViewModel: RoomDetailViewModel by fragmentViewModel()
|
||||
private val textComposerViewModel: TextComposerViewModel by fragmentViewModel()
|
||||
private val debouncer = Debouncer(createUIHandler())
|
||||
|
@ -396,10 +397,10 @@ class RoomDetailFragment @Inject constructor(
|
|||
return@onEach
|
||||
}
|
||||
when (mode) {
|
||||
is SendMode.REGULAR -> renderRegularMode(mode.text)
|
||||
is SendMode.EDIT -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_edit, R.string.edit, mode.text)
|
||||
is SendMode.QUOTE -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_quote, R.string.quote, mode.text)
|
||||
is SendMode.REPLY -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_reply, R.string.reply, mode.text)
|
||||
is SendMode.REGULAR -> renderRegularMode(mode.text)
|
||||
is SendMode.EDIT -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_edit, R.string.edit, mode.text)
|
||||
is SendMode.QUOTE -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_quote, R.string.quote, mode.text)
|
||||
is SendMode.REPLY -> renderSpecialMode(mode.timelineEvent, R.drawable.ic_reply, R.string.reply, mode.text)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1466,24 +1467,27 @@ class RoomDetailFragment @Inject constructor(
|
|||
|
||||
private fun renderSendMessageResult(sendMessageResult: TextComposerViewEvents.SendMessageResult) {
|
||||
when (sendMessageResult) {
|
||||
is TextComposerViewEvents.SlashCommandHandled -> {
|
||||
is TextComposerViewEvents.SlashCommandHandled -> {
|
||||
sendMessageResult.messageRes?.let { showSnackWithMessage(getString(it)) }
|
||||
}
|
||||
is TextComposerViewEvents.SlashCommandError -> {
|
||||
is TextComposerViewEvents.SlashCommandError -> {
|
||||
displayCommandError(getString(R.string.command_problem_with_parameters, sendMessageResult.command.command))
|
||||
}
|
||||
is TextComposerViewEvents.SlashCommandUnknown -> {
|
||||
is TextComposerViewEvents.SlashCommandUnknown -> {
|
||||
displayCommandError(getString(R.string.unrecognized_command, sendMessageResult.command))
|
||||
}
|
||||
is TextComposerViewEvents.SlashCommandResultOk -> {
|
||||
is TextComposerViewEvents.SlashCommandResultOk -> {
|
||||
views.composerLayout.setTextIfDifferent("")
|
||||
}
|
||||
is TextComposerViewEvents.SlashCommandResultError -> {
|
||||
is TextComposerViewEvents.SlashCommandResultError -> {
|
||||
displayCommandError(errorFormatter.toHumanReadable(sendMessageResult.throwable))
|
||||
}
|
||||
is TextComposerViewEvents.SlashCommandNotImplemented -> {
|
||||
is TextComposerViewEvents.SlashCommandNotImplemented -> {
|
||||
displayCommandError(getString(R.string.not_implemented))
|
||||
}
|
||||
is TextComposerViewEvents.SlashCommandNotSupportedInThreads -> {
|
||||
displayCommandError(getString(R.string.command_not_supported_in_threads, sendMessageResult.command))
|
||||
}
|
||||
} // .exhaustive
|
||||
|
||||
lockSendButton = false
|
||||
|
@ -2217,6 +2221,6 @@ class RoomDetailFragment @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun isThreadTimeLine(): Boolean = roomDetailArgs.roomThreadDetailArgs != null
|
||||
private fun isThreadTimeLine(): Boolean = roomDetailArgs.roomThreadDetailArgs != null
|
||||
fun getRootThreadEventId(): String? = roomDetailArgs.roomThreadDetailArgs?.eventId
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ sealed class TextComposerViewEvents : VectorViewEvents {
|
|||
data class JoinRoomCommandSuccess(val roomId: String) : SendMessageResult()
|
||||
class SlashCommandError(val command: Command) : SendMessageResult()
|
||||
class SlashCommandUnknown(val command: String) : SendMessageResult()
|
||||
class SlashCommandNotSupportedInThreads(val command: String) : SendMessageResult()
|
||||
data class SlashCommandHandled(@StringRes val messageRes: Int? = null) : SendMessageResult()
|
||||
object SlashCommandResultOk : SendMessageResult()
|
||||
class SlashCommandResultError(val throwable: Throwable) : SendMessageResult()
|
||||
|
|
|
@ -152,160 +152,198 @@ class TextComposerViewModel @AssistedInject constructor(
|
|||
private fun handleSendMessage(action: TextComposerAction.SendMessage) {
|
||||
withState { state ->
|
||||
when (state.sendMode) {
|
||||
is SendMode.REGULAR -> {
|
||||
when (val slashCommandResult = CommandParser.parseSplashCommand(action.text)) {
|
||||
is ParsedCommand.ErrorNotACommand -> {
|
||||
is SendMode.REGULAR -> {
|
||||
when (val slashCommandResult = CommandParser.parseSplashCommand(action.text, state.isInThreadTimeline())) {
|
||||
is ParsedCommand.ErrorNotACommand -> {
|
||||
// Send the text message to the room
|
||||
if (state.rootThreadEventId != null)
|
||||
room.replyInThread(state.rootThreadEventId, action.text.toString(), action.autoMarkdown)
|
||||
room.replyInThread(
|
||||
rootThreadEventId = state.rootThreadEventId,
|
||||
replyInThreadText = action.text.toString(),
|
||||
autoMarkdown = action.autoMarkdown)
|
||||
else
|
||||
room.sendTextMessage(action.text, autoMarkdown = action.autoMarkdown)
|
||||
|
||||
_viewEvents.post(TextComposerViewEvents.MessageSent)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.ErrorSyntax -> {
|
||||
is ParsedCommand.ErrorSyntax -> {
|
||||
_viewEvents.post(TextComposerViewEvents.SlashCommandError(slashCommandResult.command))
|
||||
}
|
||||
is ParsedCommand.ErrorEmptySlashCommand -> {
|
||||
is ParsedCommand.ErrorEmptySlashCommand -> {
|
||||
_viewEvents.post(TextComposerViewEvents.SlashCommandUnknown("/"))
|
||||
}
|
||||
is ParsedCommand.ErrorUnknownSlashCommand -> {
|
||||
is ParsedCommand.ErrorUnknownSlashCommand -> {
|
||||
_viewEvents.post(TextComposerViewEvents.SlashCommandUnknown(slashCommandResult.slashCommand))
|
||||
}
|
||||
is ParsedCommand.SendPlainText -> {
|
||||
is ParsedCommand.ErrorCommandNotSupportedInThreads -> {
|
||||
_viewEvents.post(TextComposerViewEvents.SlashCommandNotSupportedInThreads(slashCommandResult.slashCommand))
|
||||
}
|
||||
is ParsedCommand.SendPlainText -> {
|
||||
// Send the text message to the room, without markdown
|
||||
room.sendTextMessage(slashCommandResult.message, autoMarkdown = false)
|
||||
if (state.rootThreadEventId != null)
|
||||
room.replyInThread(
|
||||
rootThreadEventId = state.rootThreadEventId,
|
||||
replyInThreadText = action.text.toString(),
|
||||
autoMarkdown = false)
|
||||
else
|
||||
room.sendTextMessage(slashCommandResult.message, autoMarkdown = false)
|
||||
_viewEvents.post(TextComposerViewEvents.MessageSent)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.ChangeRoomName -> {
|
||||
is ParsedCommand.ChangeRoomName -> {
|
||||
handleChangeRoomNameSlashCommand(slashCommandResult)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.Invite -> {
|
||||
is ParsedCommand.Invite -> {
|
||||
handleInviteSlashCommand(slashCommandResult)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.Invite3Pid -> {
|
||||
is ParsedCommand.Invite3Pid -> {
|
||||
handleInvite3pidSlashCommand(slashCommandResult)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.SetUserPowerLevel -> {
|
||||
is ParsedCommand.SetUserPowerLevel -> {
|
||||
handleSetUserPowerLevel(slashCommandResult)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.ClearScalarToken -> {
|
||||
is ParsedCommand.ClearScalarToken -> {
|
||||
// TODO
|
||||
_viewEvents.post(TextComposerViewEvents.SlashCommandNotImplemented)
|
||||
}
|
||||
is ParsedCommand.SetMarkdown -> {
|
||||
is ParsedCommand.SetMarkdown -> {
|
||||
vectorPreferences.setMarkdownEnabled(slashCommandResult.enable)
|
||||
_viewEvents.post(TextComposerViewEvents.SlashCommandHandled(
|
||||
if (slashCommandResult.enable) R.string.markdown_has_been_enabled else R.string.markdown_has_been_disabled))
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.BanUser -> {
|
||||
is ParsedCommand.BanUser -> {
|
||||
handleBanSlashCommand(slashCommandResult)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.UnbanUser -> {
|
||||
is ParsedCommand.UnbanUser -> {
|
||||
handleUnbanSlashCommand(slashCommandResult)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.IgnoreUser -> {
|
||||
is ParsedCommand.IgnoreUser -> {
|
||||
handleIgnoreSlashCommand(slashCommandResult)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.UnignoreUser -> {
|
||||
is ParsedCommand.UnignoreUser -> {
|
||||
handleUnignoreSlashCommand(slashCommandResult)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.KickUser -> {
|
||||
is ParsedCommand.KickUser -> {
|
||||
handleKickSlashCommand(slashCommandResult)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.JoinRoom -> {
|
||||
is ParsedCommand.JoinRoom -> {
|
||||
handleJoinToAnotherRoomSlashCommand(slashCommandResult)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.PartRoom -> {
|
||||
is ParsedCommand.PartRoom -> {
|
||||
// TODO
|
||||
_viewEvents.post(TextComposerViewEvents.SlashCommandNotImplemented)
|
||||
}
|
||||
is ParsedCommand.SendEmote -> {
|
||||
room.sendTextMessage(slashCommandResult.message, msgType = MessageType.MSGTYPE_EMOTE, autoMarkdown = action.autoMarkdown)
|
||||
is ParsedCommand.SendEmote -> {
|
||||
state.rootThreadEventId?.let {
|
||||
room.replyInThread(
|
||||
rootThreadEventId = state.rootThreadEventId,
|
||||
replyInThreadText = slashCommandResult.message,
|
||||
msgType = MessageType.MSGTYPE_EMOTE,
|
||||
autoMarkdown = action.autoMarkdown)
|
||||
} ?: room.sendTextMessage(slashCommandResult.message, msgType = MessageType.MSGTYPE_EMOTE, autoMarkdown = action.autoMarkdown)
|
||||
_viewEvents.post(TextComposerViewEvents.SlashCommandHandled())
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.SendRainbow -> {
|
||||
slashCommandResult.message.toString().let {
|
||||
room.sendFormattedTextMessage(it, rainbowGenerator.generate(it))
|
||||
}
|
||||
is ParsedCommand.SendRainbow -> {
|
||||
|
||||
val message = slashCommandResult.message.toString()
|
||||
state.rootThreadEventId?.let {
|
||||
room.replyInThread(
|
||||
rootThreadEventId = state.rootThreadEventId,
|
||||
replyInThreadText = slashCommandResult.message,
|
||||
formattedText = rainbowGenerator.generate(message))
|
||||
} ?: room.sendFormattedTextMessage(message, rainbowGenerator.generate(message))
|
||||
_viewEvents.post(TextComposerViewEvents.SlashCommandHandled())
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.SendRainbowEmote -> {
|
||||
slashCommandResult.message.toString().let {
|
||||
room.sendFormattedTextMessage(it, rainbowGenerator.generate(it), MessageType.MSGTYPE_EMOTE)
|
||||
}
|
||||
is ParsedCommand.SendRainbowEmote -> {
|
||||
val message = slashCommandResult.message.toString()
|
||||
state.rootThreadEventId?.let {
|
||||
room.replyInThread(
|
||||
rootThreadEventId = state.rootThreadEventId,
|
||||
replyInThreadText = slashCommandResult.message,
|
||||
msgType = MessageType.MSGTYPE_EMOTE,
|
||||
formattedText = rainbowGenerator.generate(message))
|
||||
} ?: room.sendFormattedTextMessage(message, rainbowGenerator.generate(message),MessageType.MSGTYPE_EMOTE)
|
||||
|
||||
_viewEvents.post(TextComposerViewEvents.SlashCommandHandled())
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.SendSpoiler -> {
|
||||
room.sendFormattedTextMessage(
|
||||
"[${stringProvider.getString(R.string.spoiler)}](${slashCommandResult.message})",
|
||||
"<span data-mx-spoiler>${slashCommandResult.message}</span>"
|
||||
is ParsedCommand.SendSpoiler -> {
|
||||
|
||||
val text = "[${stringProvider.getString(R.string.spoiler)}](${slashCommandResult.message})"
|
||||
val formattedText = "<span data-mx-spoiler>${slashCommandResult.message}</span>"
|
||||
state.rootThreadEventId?.let {
|
||||
room.replyInThread(
|
||||
rootThreadEventId = state.rootThreadEventId,
|
||||
replyInThreadText = text,
|
||||
formattedText = formattedText)
|
||||
} ?: room.sendFormattedTextMessage(
|
||||
text,
|
||||
formattedText
|
||||
)
|
||||
_viewEvents.post(TextComposerViewEvents.SlashCommandHandled())
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.SendShrug -> {
|
||||
sendPrefixedMessage("¯\\_(ツ)_/¯", slashCommandResult.message)
|
||||
is ParsedCommand.SendShrug -> {
|
||||
|
||||
sendPrefixedMessage("¯\\_(ツ)_/¯", slashCommandResult.message, state.rootThreadEventId)
|
||||
_viewEvents.post(TextComposerViewEvents.SlashCommandHandled())
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.SendLenny -> {
|
||||
sendPrefixedMessage("( ͡° ͜ʖ ͡°)", slashCommandResult.message)
|
||||
is ParsedCommand.SendLenny -> {
|
||||
sendPrefixedMessage("( ͡° ͜ʖ ͡°)", slashCommandResult.message, state.rootThreadEventId)
|
||||
_viewEvents.post(TextComposerViewEvents.SlashCommandHandled())
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.SendChatEffect -> {
|
||||
is ParsedCommand.SendChatEffect -> {
|
||||
sendChatEffect(slashCommandResult)
|
||||
_viewEvents.post(TextComposerViewEvents.SlashCommandHandled())
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.SendPoll -> {
|
||||
is ParsedCommand.SendPoll -> {
|
||||
room.sendPoll(slashCommandResult.question, slashCommandResult.options.mapIndexed { index, s -> OptionItem(s, "$index. $s") })
|
||||
_viewEvents.post(TextComposerViewEvents.SlashCommandHandled())
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.ChangeTopic -> {
|
||||
is ParsedCommand.ChangeTopic -> {
|
||||
handleChangeTopicSlashCommand(slashCommandResult)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.ChangeDisplayName -> {
|
||||
is ParsedCommand.ChangeDisplayName -> {
|
||||
handleChangeDisplayNameSlashCommand(slashCommandResult)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.ChangeDisplayNameForRoom -> {
|
||||
is ParsedCommand.ChangeDisplayNameForRoom -> {
|
||||
handleChangeDisplayNameForRoomSlashCommand(slashCommandResult)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.ChangeRoomAvatar -> {
|
||||
is ParsedCommand.ChangeRoomAvatar -> {
|
||||
handleChangeRoomAvatarSlashCommand(slashCommandResult)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.ChangeAvatarForRoom -> {
|
||||
is ParsedCommand.ChangeAvatarForRoom -> {
|
||||
handleChangeAvatarForRoomSlashCommand(slashCommandResult)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.ShowUser -> {
|
||||
is ParsedCommand.ShowUser -> {
|
||||
_viewEvents.post(TextComposerViewEvents.SlashCommandHandled())
|
||||
handleWhoisSlashCommand(slashCommandResult)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.DiscardSession -> {
|
||||
is ParsedCommand.DiscardSession -> {
|
||||
if (room.isEncrypted()) {
|
||||
session.cryptoService().discardOutboundSession(room.roomId)
|
||||
_viewEvents.post(TextComposerViewEvents.SlashCommandHandled())
|
||||
|
@ -318,7 +356,7 @@ class TextComposerViewModel @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
is ParsedCommand.CreateSpace -> {
|
||||
is ParsedCommand.CreateSpace -> {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val params = CreateSpaceParams().apply {
|
||||
|
@ -340,7 +378,7 @@ class TextComposerViewModel @AssistedInject constructor(
|
|||
_viewEvents.post(TextComposerViewEvents.SlashCommandHandled())
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.AddToSpace -> {
|
||||
is ParsedCommand.AddToSpace -> {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
session.spaceService().getSpace(slashCommandResult.spaceId)
|
||||
|
@ -357,7 +395,7 @@ class TextComposerViewModel @AssistedInject constructor(
|
|||
_viewEvents.post(TextComposerViewEvents.SlashCommandHandled())
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.JoinSpace -> {
|
||||
is ParsedCommand.JoinSpace -> {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
session.spaceService().joinSpace(slashCommandResult.spaceIdOrAlias)
|
||||
|
@ -368,7 +406,7 @@ class TextComposerViewModel @AssistedInject constructor(
|
|||
_viewEvents.post(TextComposerViewEvents.SlashCommandHandled())
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.LeaveRoom -> {
|
||||
is ParsedCommand.LeaveRoom -> {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
session.getRoom(slashCommandResult.roomId)?.leave(null)
|
||||
|
@ -379,7 +417,7 @@ class TextComposerViewModel @AssistedInject constructor(
|
|||
_viewEvents.post(TextComposerViewEvents.SlashCommandHandled())
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.UpgradeRoom -> {
|
||||
is ParsedCommand.UpgradeRoom -> {
|
||||
_viewEvents.post(
|
||||
TextComposerViewEvents.ShowRoomUpgradeDialog(
|
||||
slashCommandResult.newVersion,
|
||||
|
@ -391,7 +429,7 @@ class TextComposerViewModel @AssistedInject constructor(
|
|||
}
|
||||
}.exhaustive
|
||||
}
|
||||
is SendMode.EDIT -> {
|
||||
is SendMode.EDIT -> {
|
||||
// is original event a reply?
|
||||
val inReplyTo = state.sendMode.timelineEvent.getRelationContent()?.inReplyTo?.eventId
|
||||
if (inReplyTo != null) {
|
||||
|
@ -414,7 +452,7 @@ class TextComposerViewModel @AssistedInject constructor(
|
|||
_viewEvents.post(TextComposerViewEvents.MessageSent)
|
||||
popDraft()
|
||||
}
|
||||
is SendMode.QUOTE -> {
|
||||
is SendMode.QUOTE -> {
|
||||
val messageContent = state.sendMode.timelineEvent.getLastMessageContent()
|
||||
val textMsg = messageContent?.body
|
||||
|
||||
|
@ -435,7 +473,7 @@ class TextComposerViewModel @AssistedInject constructor(
|
|||
_viewEvents.post(TextComposerViewEvents.MessageSent)
|
||||
popDraft()
|
||||
}
|
||||
is SendMode.REPLY -> {
|
||||
is SendMode.REPLY -> {
|
||||
state.sendMode.timelineEvent.let {
|
||||
room.replyToMessage(it, action.text.toString(), action.autoMarkdown)
|
||||
_viewEvents.post(TextComposerViewEvents.MessageSent)
|
||||
|
@ -657,7 +695,7 @@ class TextComposerViewModel @AssistedInject constructor(
|
|||
_viewEvents.post(TextComposerViewEvents.OpenRoomMemberProfile(whois.userId))
|
||||
}
|
||||
|
||||
private fun sendPrefixedMessage(prefix: String, message: CharSequence) {
|
||||
private fun sendPrefixedMessage(prefix: String, message: CharSequence, rootThreadEventId: String?) {
|
||||
val sequence = buildString {
|
||||
append(prefix)
|
||||
if (message.isNotEmpty()) {
|
||||
|
@ -665,7 +703,9 @@ class TextComposerViewModel @AssistedInject constructor(
|
|||
append(message)
|
||||
}
|
||||
}
|
||||
room.sendTextMessage(sequence)
|
||||
rootThreadEventId?.let {
|
||||
room.replyInThread(it, sequence)
|
||||
}?: room.sendTextMessage(sequence)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1831,6 +1831,7 @@
|
|||
<string name="command_error">Command error</string>
|
||||
<string name="unrecognized_command">Unrecognized command: %s</string>
|
||||
<string name="command_problem_with_parameters">The command \"%s\" needs more parameters, or some parameters are incorrect.</string>
|
||||
<string name="command_not_supported_in_threads">The command \"%s\" is recognized but not supported in threads.</string>
|
||||
<string name="command_description_emote">Displays action</string>
|
||||
<string name="command_description_ban_user">Bans user with given id</string>
|
||||
<string name="command_description_unban_user">Unbans user with given id</string>
|
||||
|
|
Loading…
Reference in New Issue