From 125eacb20b55122f5ff573aa5b0ab2d691553885 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 23 Jul 2019 19:53:47 +0200 Subject: [PATCH] Direct messages: try to handle selecting/deselecting users (WIP) --- .../matrix/android/api/MatrixPatterns.kt | 2 +- .../vector/riotx/core/extensions/LiveData.kt | 5 ++ .../riotx/core/mvrx/NavigationViewModel.kt | 3 +- .../riotx/core/platform/VectorViewModel.kt | 30 +++++-- .../CreateDirectRoomController.kt | 10 +-- .../CreateDirectRoomDirectoryUsersFragment.kt | 2 + .../createdirect/CreateDirectRoomFragment.kt | 85 ++++++++++++++++--- .../createdirect/CreateDirectRoomUserItem.kt | 2 - .../createdirect/CreateDirectRoomViewModel.kt | 48 ++++++++--- .../features/home/group/GroupListViewModel.kt | 3 +- .../home/room/detail/RoomDetailViewModel.kt | 59 ++++++------- .../home/room/list/RoomListViewModel.kt | 5 +- .../roomdirectory/RoomDirectoryViewModel.kt | 3 +- .../layout/fragment_create_direct_room.xml | 11 +-- 14 files changed, 184 insertions(+), 84 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixPatterns.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixPatterns.kt index 5cb7f4ca..e843128e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixPatterns.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixPatterns.kt @@ -28,7 +28,7 @@ object MatrixPatterns { // regex pattern to find matrix user ids in a string. // See https://matrix.org/speculator/spec/HEAD/appendices.html#historical-user-ids private const val MATRIX_USER_IDENTIFIER_REGEX = "@[A-Z0-9\\x21-\\x39\\x3B-\\x7F]+$DOMAIN_REGEX" - private val PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER = MATRIX_USER_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE) + val PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER = MATRIX_USER_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE) // regex pattern to find room ids in a string. private const val MATRIX_ROOM_IDENTIFIER_REGEX = "![A-Z0-9]+$DOMAIN_REGEX" diff --git a/vector/src/main/java/im/vector/riotx/core/extensions/LiveData.kt b/vector/src/main/java/im/vector/riotx/core/extensions/LiveData.kt index a278eab0..97215e1e 100644 --- a/vector/src/main/java/im/vector/riotx/core/extensions/LiveData.kt +++ b/vector/src/main/java/im/vector/riotx/core/extensions/LiveData.kt @@ -18,6 +18,7 @@ package im.vector.riotx.core.extensions import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Observer import im.vector.riotx.core.utils.FirstThrottler import im.vector.riotx.core.utils.EventObserver @@ -44,3 +45,7 @@ inline fun LiveData>.observeEventFirstThrottle(owner: Lifecycle } }) } + +fun MutableLiveData>.postLiveEvent(content: T) { + this.postValue(LiveEvent(content)) +} diff --git a/vector/src/main/java/im/vector/riotx/core/mvrx/NavigationViewModel.kt b/vector/src/main/java/im/vector/riotx/core/mvrx/NavigationViewModel.kt index ab3ce7c8..a6bf07e0 100644 --- a/vector/src/main/java/im/vector/riotx/core/mvrx/NavigationViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/core/mvrx/NavigationViewModel.kt @@ -19,6 +19,7 @@ package im.vector.riotx.core.mvrx import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.utils.LiveEvent abstract class NavigationViewModel : ViewModel() { @@ -29,6 +30,6 @@ abstract class NavigationViewModel : ViewModel() { fun goTo(navigation: NavigationClass) { - _navigateTo.postValue(LiveEvent(navigation)) + _navigateTo.postLiveEvent(navigation) } } \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt index 1570a7f8..1c2f1d53 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt @@ -16,20 +16,36 @@ package im.vector.riotx.core.platform -import com.airbnb.mvrx.BaseMvRxViewModel -import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.* import im.vector.matrix.android.api.util.CancelableBag import im.vector.riotx.BuildConfig +import io.reactivex.Observable +import io.reactivex.Single +import io.reactivex.disposables.Disposable abstract class VectorViewModel(initialState: S) : BaseMvRxViewModel(initialState, false) { - protected val cancelableBag = CancelableBag() - - override fun onCleared() { - super.onCleared() - cancelableBag.cancel() + /** + * This method does the same thing as the execute function, but it doesn't subscribe to the stream + * so you can use this in a switchMap or a flatMap + */ + fun Single.toAsync(stateReducer: S.(Async) -> S): Single> { + setState { stateReducer(Loading()) } + return this.map { Success(it) as Async } + .onErrorReturn { Fail(it) } + .doOnSuccess { setState { stateReducer(it) } } } + /** + * This method does the same thing as the execute function, but it doesn't subscribe to the stream + * so you can use this in a switchMap or a flatMap + */ + fun Observable.toAsync(stateReducer: S.(Async) -> S): Observable> { + setState { stateReducer(Loading()) } + return this.map { Success(it) as Async } + .onErrorReturn { Fail(it) } + .doOnNext { setState { stateReducer(it) } } + } } \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomController.kt b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomController.kt index 8e56813a..4d83477d 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomController.kt @@ -19,13 +19,9 @@ package im.vector.riotx.features.home.createdirect import com.airbnb.epoxy.EpoxyController -import com.airbnb.epoxy.VisibilityState import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Incomplete -import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Success -import com.airbnb.mvrx.Uninitialized -import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.internal.util.firstLetterOfDisplayName import im.vector.riotx.core.epoxy.errorWithRetryItem @@ -60,7 +56,7 @@ class CreateDirectRoomController @Inject constructor(private val avatarRenderer: } when (asyncUsers) { is Incomplete -> renderLoading() - is Success -> renderUsers(asyncUsers(), currentState.selectedUsers) + is Success -> renderUsers(asyncUsers(), currentState.selectedUsers.map { it.userId }) is Fail -> renderFailure(asyncUsers.error) } } @@ -79,10 +75,10 @@ class CreateDirectRoomController @Inject constructor(private val avatarRenderer: } } - private fun renderUsers(users: List, selectedUsers: Set) { + private fun renderUsers(users: List, selectedUsers: List) { var lastFirstLetter: String? = null users.forEach { user -> - val isSelected = selectedUsers.contains(user) + val isSelected = selectedUsers.contains(user.userId) val currentFirstLetter = user.displayName.firstLetterOfDisplayName() val showLetter = currentFirstLetter.isNotEmpty() && lastFirstLetter != currentFirstLetter lastFirstLetter = currentFirstLetter diff --git a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt index 838c7d6e..f19abaa3 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt @@ -25,6 +25,7 @@ import com.jakewharton.rxbinding3.widget.textChanges import im.vector.matrix.android.api.session.user.model.User import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent +import im.vector.riotx.core.extensions.hideKeyboard import im.vector.riotx.core.platform.VectorBaseFragment import kotlinx.android.synthetic.main.fragment_create_direct_room_directory_users.* import javax.inject.Inject @@ -82,6 +83,7 @@ class CreateDirectRoomDirectoryUsersFragment : VectorBaseFragment(), CreateDirec } override fun onItemClick(user: User) { + view?.hideKeyboard() viewModel.handle(CreateDirectRoomActions.SelectUser(user)) navigationViewModel.goTo(CreateDirectRoomActivity.Navigation.Previous) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomFragment.kt index e28346a6..6f0c1727 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomFragment.kt @@ -19,21 +19,24 @@ package im.vector.riotx.features.home.createdirect import android.os.Bundle +import android.text.Spannable import android.view.MenuItem import androidx.lifecycle.ViewModelProviders -import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.Success -import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.activityViewModel -import com.jakewharton.rxbinding3.appcompat.queryTextChanges +import com.airbnb.mvrx.withState +import com.jakewharton.rxbinding3.widget.beforeTextChangeEvents +import com.jakewharton.rxbinding3.widget.textChanges +import im.vector.matrix.android.api.MatrixPatterns import im.vector.matrix.android.api.session.user.model.User import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent +import im.vector.riotx.core.extensions.hideKeyboard +import im.vector.riotx.core.extensions.observeEvent +import im.vector.riotx.core.glide.GlideApp import im.vector.riotx.core.platform.VectorBaseFragment -import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity +import im.vector.riotx.features.home.AvatarRenderer +import im.vector.riotx.features.html.PillImageSpan import kotlinx.android.synthetic.main.fragment_create_direct_room.* -import kotlinx.android.synthetic.main.fragment_public_rooms.* import javax.inject.Inject class CreateDirectRoomFragment : VectorBaseFragment(), CreateDirectRoomController.Callback { @@ -45,6 +48,7 @@ class CreateDirectRoomFragment : VectorBaseFragment(), CreateDirectRoomControlle private val viewModel: CreateDirectRoomViewModel by activityViewModel() @Inject lateinit var directRoomController: CreateDirectRoomController + @Inject lateinit var avatarRenderer: AvatarRenderer private lateinit var navigationViewModel: CreateDirectRoomNavigationViewModel override fun injectWith(injector: ScreenComponent) { @@ -59,6 +63,10 @@ class CreateDirectRoomFragment : VectorBaseFragment(), CreateDirectRoomControlle setupFilterView() setupAddByMatrixIdView() setupCloseView() + viewModel.selectUserEvent.observeEvent(this) { + updateFilterViewWith(it) + + } viewModel.subscribe(this) { renderState(it) } } @@ -90,16 +98,43 @@ class CreateDirectRoomFragment : VectorBaseFragment(), CreateDirectRoomControlle private fun setupFilterView() { createDirectRoomFilter - .queryTextChanges() - .subscribe { - val action = if (it.isNullOrEmpty()) { + .textChanges() + .subscribe { text -> + val userMatches = MatrixPatterns.PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER.findAll(text) + val lastUserMatch = userMatches.lastOrNull() + val filterValue = if (lastUserMatch == null) { + text + } else { + text.substring(startIndex = lastUserMatch.range.endInclusive + 1) + }.trim() + + val action = if (filterValue.isBlank()) { CreateDirectRoomActions.ClearFilterKnownUsers } else { - CreateDirectRoomActions.FilterKnownUsers(it.toString()) + CreateDirectRoomActions.FilterKnownUsers(filterValue.toString()) } viewModel.handle(action) } .disposeOnDestroy() + + createDirectRoomFilter + .beforeTextChangeEvents() + .subscribe { event -> + if (event.after == 0) { + val sub = event.text.substring(0, event.start) + val startIndexOfUser = sub.lastIndexOf(" ") + 1 + val user = sub.substring(startIndexOfUser) + val selectedUser = withState(viewModel) { state -> + state.selectedUsers.find { it.userId == user } + } + if (selectedUser != null) { + viewModel.handle(CreateDirectRoomActions.RemoveSelectedUser(selectedUser)) + } + } + } + .disposeOnDestroy() + + createDirectRoomFilter.requestFocus() } private fun setupCloseView() { @@ -109,11 +144,37 @@ class CreateDirectRoomFragment : VectorBaseFragment(), CreateDirectRoomControlle } private fun renderState(state: CreateDirectRoomViewState) { - directRoomController.setData(state) } + private fun updateFilterViewWith(data: SelectUserAction) = withState(viewModel) { state -> + if (state.selectedUsers.isEmpty()) { + createDirectRoomFilter.text = null + } else { + val editable = createDirectRoomFilter.editableText + val user = data.user + if (data.isAdded) { + val startIndex = editable.lastIndexOf(" ") + 1 + val endIndex = editable.length + editable.replace(startIndex, endIndex, "${user.userId} ") + val span = PillImageSpan(GlideApp.with(this), avatarRenderer, requireContext(), user.userId, user) + span.bind(createDirectRoomFilter) + editable.setSpan(span, startIndex, startIndex + user.userId.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + } else { + val startIndex = editable.indexOf(user.userId) + if (startIndex != -1) { + var endIndex = editable.indexOf(" ", startIndex) + 1 + if (endIndex == 0) { + endIndex = editable.length + } + editable.replace(startIndex, endIndex, "") + } + } + } + } + override fun onItemClick(user: User) { + view?.hideKeyboard() viewModel.handle(CreateDirectRoomActions.SelectUser(user)) } } \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomUserItem.kt b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomUserItem.kt index 33f7c22f..96a5ce0b 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomUserItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomUserItem.kt @@ -28,9 +28,7 @@ import com.amulyakhare.textdrawable.TextDrawable import im.vector.riotx.R import im.vector.riotx.core.epoxy.VectorEpoxyHolder import im.vector.riotx.core.epoxy.VectorEpoxyModel -import im.vector.riotx.core.resources.ColorProvider import im.vector.riotx.features.home.AvatarRenderer -import im.vector.riotx.features.home.getColorFromUserId @EpoxyModelClass(layout = R.layout.item_create_direct_room_user) abstract class CreateDirectRoomUserItem : VectorEpoxyModel() { diff --git a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomViewModel.kt index 481ca922..2d609388 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomViewModel.kt @@ -18,10 +18,10 @@ package im.vector.riotx.features.home.createdirect +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import arrow.core.Option -import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory -import com.airbnb.mvrx.ViewModelContext +import com.airbnb.mvrx.* import com.jakewharton.rxrelay2.BehaviorRelay import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject @@ -29,7 +29,9 @@ import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.rx.rx +import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.platform.VectorViewModel +import im.vector.riotx.core.utils.LiveEvent import io.reactivex.Observable import io.reactivex.functions.BiFunction import java.util.concurrent.TimeUnit @@ -37,6 +39,11 @@ import java.util.concurrent.TimeUnit private typealias KnowUsersFilter = String private typealias DirectoryUsersSearch = String +data class SelectUserAction( + val user: User, + val isAdded: Boolean +) + class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted initialState: CreateDirectRoomViewState, private val session: Session) @@ -50,6 +57,10 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted private val knownUsersFilter = BehaviorRelay.createDefault>(Option.empty()) private val directoryUsersSearch = BehaviorRelay.create() + private val _selectUserEvent = MutableLiveData>() + val selectUserEvent: LiveData> + get() = _selectUserEvent + companion object : MvRxViewModelFactory { @JvmStatic @@ -78,7 +89,7 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted private fun createRoomAndInviteSelectedUsers() = withState { currentState -> val isDirect = currentState.selectedUsers.size == 1 val roomParams = CreateRoomParams().apply { - invitedUserIds = ArrayList(currentState.selectedUsers.map { user -> user.userId }) + invitedUserIds = ArrayList(currentState.selectedUsers.map { it.userId }) if (isDirect) { setDirectMessage() } @@ -92,33 +103,42 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted } private fun handleRemoveSelectedUser(action: CreateDirectRoomActions.RemoveSelectedUser) = withState { - val selectedUsers = it.selectedUsers.minusElement(action.user) + val selectedUsers = it.selectedUsers.minus(action.user) setState { copy(selectedUsers = selectedUsers) } + _selectUserEvent.postLiveEvent(SelectUserAction(action.user, false)) } private fun handleSelectUser(action: CreateDirectRoomActions.SelectUser) = withState { - val selectedUsers = if (it.selectedUsers.contains(action.user)) { - it.selectedUsers.minusElement(action.user) + //Reset the filter asap + knownUsersFilter.accept(Option.empty()) + directoryUsersSearch.accept("") + + val isAddOperation: Boolean + val selectedUsers: Set + if (it.selectedUsers.contains(action.user)) { + selectedUsers = it.selectedUsers.minus(action.user) + isAddOperation = false } else { - it.selectedUsers.plus(action.user) + selectedUsers = it.selectedUsers.plus(action.user) + isAddOperation = true } setState { copy(selectedUsers = selectedUsers) } + _selectUserEvent.postLiveEvent(SelectUserAction(action.user, isAddOperation)) } private fun observeDirectoryUsers() { directoryUsersSearch - .throttleLast(300, TimeUnit.MILLISECONDS) + .throttleLast(500, TimeUnit.MILLISECONDS) .switchMapSingle { search -> session.rx() .searchUsersDirectory(search, 50, emptySet()) .map { users -> users.sortedBy { it.displayName } } + .toAsync { copy(directoryUsers = it) } } - .execute { async -> - copy(directoryUsers = async) - } - + .subscribe() + .disposeOnClear() } private fun observeKnownUsers() { @@ -133,7 +153,7 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted } else { users.filter { it.displayName?.contains(filterValue, ignoreCase = true) ?: false - || it.userId.contains(filterValue, ignoreCase = true) + || it.userId.contains(filterValue, ignoreCase = true) } } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt index 513379bd..7aff4a32 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt @@ -28,6 +28,7 @@ import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.rx.rx import im.vector.riotx.R +import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.utils.LiveEvent @@ -67,7 +68,7 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro private fun observeSelectionState() { selectSubscribe(GroupListViewState::selectedGroup) { if (it != null) { - _openGroupLiveData.postValue(LiveEvent(it)) + _openGroupLiveData.postLiveEvent(it) val optionGroup = Option.fromNullable(it) selectedGroupHolder.post(optionGroup) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index 208b8e64..4b734c95 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -40,6 +40,7 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent import im.vector.matrix.rx.rx +import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.intent.getFilenameFromUri import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.resources.UserPreferencesProvider @@ -168,62 +169,62 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro is ParsedCommand.ErrorNotACommand -> { // Send the text message to the room room.sendTextMessage(action.text, autoMarkdown = action.autoMarkdown) - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent)) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.MessageSent) } is ParsedCommand.ErrorSyntax -> { - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandError(slashCommandResult.command))) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandError(slashCommandResult.command)) } is ParsedCommand.ErrorEmptySlashCommand -> { - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandUnknown("/"))) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandUnknown("/")) } is ParsedCommand.ErrorUnknownSlashCommand -> { - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandUnknown(slashCommandResult.slashCommand))) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandUnknown(slashCommandResult.slashCommand)) } is ParsedCommand.Invite -> { handleInviteSlashCommand(slashCommandResult) } is ParsedCommand.SetUserPowerLevel -> { // TODO - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented) } is ParsedCommand.ClearScalarToken -> { // TODO - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented) } is ParsedCommand.SetMarkdown -> { // TODO - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented) } is ParsedCommand.UnbanUser -> { // TODO - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented) } is ParsedCommand.BanUser -> { // TODO - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented) } is ParsedCommand.KickUser -> { // TODO - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented) } is ParsedCommand.JoinRoom -> { // TODO - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented) } is ParsedCommand.PartRoom -> { // TODO - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented) } is ParsedCommand.SendEmote -> { room.sendTextMessage(slashCommandResult.message, msgType = MessageType.MSGTYPE_EMOTE) - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandHandled)) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandHandled) } is ParsedCommand.ChangeTopic -> { handleChangeTopicSlashCommand(slashCommandResult) } is ParsedCommand.ChangeDisplayName -> { // TODO - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented) } } } @@ -255,7 +256,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro sendMode = SendMode.REGULAR ) } - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent)) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.MessageSent) } is SendMode.QUOTE -> { val messageContent: MessageContent? = @@ -280,7 +281,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro sendMode = SendMode.REGULAR ) } - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent)) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.MessageSent) } is SendMode.REPLY -> { state.sendMode.timelineEvent.let { @@ -290,7 +291,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro sendMode = SendMode.REGULAR ) } - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent)) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.MessageSent) } } @@ -319,29 +320,29 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } private fun handleChangeTopicSlashCommand(changeTopic: ParsedCommand.ChangeTopic) { - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandHandled)) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandHandled) room.updateTopic(changeTopic.topic, object : MatrixCallback { override fun onSuccess(data: Unit) { - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandResultOk)) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandResultOk) } override fun onFailure(failure: Throwable) { - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandResultError(failure))) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandResultError(failure)) } }) } private fun handleInviteSlashCommand(invite: ParsedCommand.Invite) { - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandHandled)) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandHandled) room.invite(invite.userId, object : MatrixCallback { override fun onSuccess(data: Unit) { - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandResultOk)) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandResultOk) } override fun onFailure(failure: Throwable) { - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandResultError(failure))) + _sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandResultError(failure)) } }) } @@ -453,19 +454,19 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro action.messageFileContent.encryptedFileInfo?.toElementToDecrypt(), object : MatrixCallback { override fun onSuccess(data: File) { - _downloadedFileEvent.postValue(LiveEvent(DownloadFileState( + _downloadedFileEvent.postLiveEvent(DownloadFileState( action.messageFileContent.getMimeType(), data, null - ))) + )) } override fun onFailure(failure: Throwable) { - _downloadedFileEvent.postValue(LiveEvent(DownloadFileState( + _downloadedFileEvent.postLiveEvent(DownloadFileState( action.messageFileContent.getMimeType(), null, failure - ))) + )) } }) @@ -494,7 +495,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } } - _navigateToEvent.postValue(LiveEvent(targetEventId)) + _navigateToEvent.postLiveEvent(targetEventId) } else { // change timeline timeline.dispose() @@ -519,7 +520,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } } - _navigateToEvent.postValue(LiveEvent(targetEventId)) + _navigateToEvent.postLiveEvent(targetEventId) } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt index a1ae4fdf..f590a789 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt @@ -28,6 +28,7 @@ import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.tag.RoomTag +import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.utils.LiveEvent import im.vector.riotx.features.home.HomeRoomListObservableStore @@ -142,7 +143,7 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room override fun onFailure(failure: Throwable) { // Notify the user - _invitationAnswerErrorLiveData.postValue(LiveEvent(failure)) + _invitationAnswerErrorLiveData.postLiveEvent(failure) setState { copy( @@ -178,7 +179,7 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room override fun onFailure(failure: Throwable) { // Notify the user - _invitationAnswerErrorLiveData.postValue(LiveEvent(failure)) + _invitationAnswerErrorLiveData.postLiveEvent(failure) setState { copy( diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryViewModel.kt index c47e8bbd..8d0b6284 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryViewModel.kt @@ -31,6 +31,7 @@ import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRooms import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.rx.rx +import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.utils.LiveEvent import timber.log.Timber @@ -207,7 +208,7 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState: override fun onFailure(failure: Throwable) { // Notify the user - _joinRoomErrorLiveData.postValue(LiveEvent(failure)) + _joinRoomErrorLiveData.postLiveEvent(failure) setState { copy( diff --git a/vector/src/main/res/layout/fragment_create_direct_room.xml b/vector/src/main/res/layout/fragment_create_direct_room.xml index 3899d3d2..987f902b 100644 --- a/vector/src/main/res/layout/fragment_create_direct_room.xml +++ b/vector/src/main/res/layout/fragment_create_direct_room.xml @@ -71,16 +71,13 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/createDirectRoomToolbar"> - + android:maxHeight="80dp" + android:importantForAutofill="no" + android:hint="@string/room_directory_search_hint"/>