Ensure ViewModel follow the same pattern to handle actions

This commit is contained in:
Benoit Marty 2019-11-08 15:05:11 +01:00
parent 238d1d87c6
commit 70bce9e7dd
49 changed files with 449 additions and 187 deletions

View file

@ -23,7 +23,11 @@ import im.vector.riotx.core.utils.LiveEvent
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
abstract class VectorViewModel<S : MvRxState>(initialState: S) interface VectorViewModelAction
object EmptyAction : VectorViewModelAction
abstract class VectorViewModel<S : MvRxState, A : VectorViewModelAction>(initialState: S)
: BaseMvRxViewModel<S>(initialState, false) { : BaseMvRxViewModel<S>(initialState, false) {
// Generic handling of any request error // Generic handling of any request error
@ -52,4 +56,6 @@ abstract class VectorViewModel<S : MvRxState>(initialState: S)
.onErrorReturn { Fail(it) } .onErrorReturn { Fail(it) }
.doOnNext { setState { stateReducer(it) } } .doOnNext { setState { stateReducer(it) } }
} }
abstract fun handle(action: A)
} }

View file

@ -0,0 +1,25 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.features.crypto.keysbackup.settings
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class KeyBackupSettingsActions : VectorViewModelAction {
object Init : KeyBackupSettingsActions()
object GetKeyBackupTrust : KeyBackupSettingsActions()
object DeleteKeyBackup : KeyBackupSettingsActions()
}

View file

@ -51,7 +51,7 @@ class KeysBackupManageActivity : SimpleFragmentActivity() {
super.initUiAndData() super.initUiAndData()
if (supportFragmentManager.fragments.isEmpty()) { if (supportFragmentManager.fragments.isEmpty()) {
replaceFragment(R.id.container, KeysBackupSettingsFragment::class.java) replaceFragment(R.id.container, KeysBackupSettingsFragment::class.java)
viewModel.init() viewModel.handle(KeyBackupSettingsActions.Init)
} }
// Observe the deletion of keys backup // Observe the deletion of keys backup

View file

@ -66,7 +66,7 @@ class KeysBackupSettingsFragment @Inject constructor(private val keysBackupSetti
.setMessage(R.string.keys_backup_settings_delete_confirm_message) .setMessage(R.string.keys_backup_settings_delete_confirm_message)
.setCancelable(false) .setCancelable(false)
.setPositiveButton(R.string.keys_backup_settings_delete_confirm_title) { _, _ -> .setPositiveButton(R.string.keys_backup_settings_delete_confirm_title) { _, _ ->
viewModel.deleteCurrentBackup() viewModel.handle(KeyBackupSettingsActions.DeleteKeyBackup)
} }
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.setCancelable(true) .setCancelable(true)
@ -75,10 +75,10 @@ class KeysBackupSettingsFragment @Inject constructor(private val keysBackupSetti
} }
override fun loadTrustData() { override fun loadTrustData() {
viewModel.getKeysBackupTrust() viewModel.handle(KeyBackupSettingsActions.GetKeyBackupTrust)
} }
override fun loadKeysBackupState() { override fun loadKeysBackupState() {
viewModel.init() viewModel.handle(KeyBackupSettingsActions.Init)
} }
} }

View file

@ -15,13 +15,7 @@
*/ */
package im.vector.riotx.features.crypto.keysbackup.settings package im.vector.riotx.features.crypto.keysbackup.settings
import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.*
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
@ -34,8 +28,8 @@ import im.vector.riotx.core.platform.VectorViewModel
class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialState: KeysBackupSettingViewState, class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialState: KeysBackupSettingViewState,
session: Session session: Session
) : VectorViewModel<KeysBackupSettingViewState>(initialState), ) : VectorViewModel<KeysBackupSettingViewState, KeyBackupSettingsActions>(initialState),
KeysBackupStateListener { KeysBackupStateListener {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -64,11 +58,19 @@ class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialS
getKeysBackupTrust() getKeysBackupTrust()
} }
fun init() { override fun handle(action: KeyBackupSettingsActions) {
when (action) {
KeyBackupSettingsActions.Init -> init()
KeyBackupSettingsActions.GetKeyBackupTrust -> getKeysBackupTrust()
KeyBackupSettingsActions.DeleteKeyBackup -> deleteCurrentBackup()
}
}
private fun init() {
keysBackupService.forceUsingLastVersion(object : MatrixCallback<Boolean> {}) keysBackupService.forceUsingLastVersion(object : MatrixCallback<Boolean> {})
} }
fun getKeysBackupTrust() = withState { state -> private fun getKeysBackupTrust() = withState { state ->
val versionResult = keysBackupService.keysBackupVersion val versionResult = keysBackupService.keysBackupVersion
if (state.keysBackupVersionTrust is Uninitialized && versionResult != null) { if (state.keysBackupVersionTrust is Uninitialized && versionResult != null) {
@ -116,7 +118,7 @@ class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialS
getKeysBackupTrust() getKeysBackupTrust()
} }
fun deleteCurrentBackup() { private fun deleteCurrentBackup() {
val keysBackupService = keysBackupService val keysBackupService = keysBackupService
if (keysBackupService.currentBackupVersion != null) { if (keysBackupService.currentBackupVersion != null) {
@ -153,6 +155,6 @@ class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialS
val currentBackupState = keysBackupService.state val currentBackupState = keysBackupService.state
return currentBackupState == KeysBackupState.Unknown return currentBackupState == KeysBackupState.Unknown
|| currentBackupState == KeysBackupState.CheckingBackUpOnHomeserver || currentBackupState == KeysBackupState.CheckingBackUpOnHomeserver
} }
} }

View file

@ -0,0 +1,24 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.features.home
import im.vector.riotx.core.platform.VectorViewModelAction
import im.vector.riotx.features.home.room.list.RoomListFragment
sealed class HomeDetailAction : VectorViewModelAction {
data class SwitchDisplayMode(val displayMode: RoomListFragment.DisplayMode) : HomeDetailAction()
}

View file

@ -141,7 +141,7 @@ class HomeDetailFragment @Inject constructor(
R.id.bottom_action_rooms -> RoomListFragment.DisplayMode.ROOMS R.id.bottom_action_rooms -> RoomListFragment.DisplayMode.ROOMS
else -> RoomListFragment.DisplayMode.HOME else -> RoomListFragment.DisplayMode.HOME
} }
viewModel.switchDisplayMode(displayMode) viewModel.handle(HomeDetailAction.SwitchDisplayMode(displayMode))
true true
} }

View file

@ -27,7 +27,6 @@ import im.vector.riotx.core.di.HasScreenInjector
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.features.home.group.SelectedGroupDataSource import im.vector.riotx.features.home.group.SelectedGroupDataSource
import im.vector.riotx.features.home.room.list.RoomListFragment
import im.vector.riotx.features.ui.UiStateRepository import im.vector.riotx.features.ui.UiStateRepository
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
@ -41,7 +40,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
private val selectedGroupStore: SelectedGroupDataSource, private val selectedGroupStore: SelectedGroupDataSource,
private val homeRoomListStore: HomeRoomListDataSource, private val homeRoomListStore: HomeRoomListDataSource,
private val stringProvider: StringProvider) private val stringProvider: StringProvider)
: VectorViewModel<HomeDetailViewState>(initialState) { : VectorViewModel<HomeDetailViewState, HomeDetailAction>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -70,13 +69,19 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
observeRoomSummaries() observeRoomSummaries()
} }
fun switchDisplayMode(displayMode: RoomListFragment.DisplayMode) = withState { state -> override fun handle(action: HomeDetailAction) {
if (state.displayMode != displayMode) { when (action) {
is HomeDetailAction.SwitchDisplayMode -> handleSwitchDisplayMode(action)
}
}
private fun handleSwitchDisplayMode(action: HomeDetailAction.SwitchDisplayMode) = withState { state ->
if (state.displayMode != action.displayMode) {
setState { setState {
copy(displayMode = displayMode) copy(displayMode = action.displayMode)
} }
uiStateRepository.storeDisplayMode(displayMode) uiStateRepository.storeDisplayMode(action.displayMode)
} }
} }

View file

@ -17,8 +17,9 @@
package im.vector.riotx.features.home.createdirect package im.vector.riotx.features.home.createdirect
import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.session.user.model.User
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class CreateDirectRoomActions { sealed class CreateDirectRoomActions : VectorViewModelAction {
object CreateRoomAndInviteSelectedUsers : CreateDirectRoomActions() object CreateRoomAndInviteSelectedUsers : CreateDirectRoomActions()
data class FilterKnownUsers(val value: String) : CreateDirectRoomActions() data class FilterKnownUsers(val value: String) : CreateDirectRoomActions()
data class SearchDirectoryUsers(val value: String) : CreateDirectRoomActions() data class SearchDirectoryUsers(val value: String) : CreateDirectRoomActions()

View file

@ -51,7 +51,7 @@ data class SelectUserAction(
class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
initialState: CreateDirectRoomViewState, initialState: CreateDirectRoomViewState,
private val session: Session) private val session: Session)
: VectorViewModel<CreateDirectRoomViewState>(initialState) { : VectorViewModel<CreateDirectRoomViewState, CreateDirectRoomActions>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -79,7 +79,7 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
observeDirectoryUsers() observeDirectoryUsers()
} }
fun handle(action: CreateDirectRoomActions) { override fun handle(action: CreateDirectRoomActions) {
when (action) { when (action) {
is CreateDirectRoomActions.CreateRoomAndInviteSelectedUsers -> createRoomAndInviteSelectedUsers() is CreateDirectRoomActions.CreateRoomAndInviteSelectedUsers -> createRoomAndInviteSelectedUsers()
is CreateDirectRoomActions.FilterKnownUsers -> knownUsersFilter.accept(Option.just(action.value)) is CreateDirectRoomActions.FilterKnownUsers -> knownUsersFilter.accept(Option.just(action.value))

View file

@ -17,8 +17,8 @@
package im.vector.riotx.features.home.group package im.vector.riotx.features.home.group
import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class GroupListActions { sealed class GroupListActions : VectorViewModelAction {
data class SelectGroup(val groupSummary: GroupSummary) : GroupListActions() data class SelectGroup(val groupSummary: GroupSummary) : GroupListActions()
} }

View file

@ -62,6 +62,6 @@ class GroupListFragment @Inject constructor(
} }
override fun onGroupSelected(groupSummary: GroupSummary) { override fun onGroupSelected(groupSummary: GroupSummary) {
viewModel.accept(GroupListActions.SelectGroup(groupSummary)) viewModel.handle(GroupListActions.SelectGroup(groupSummary))
} }
} }

View file

@ -42,7 +42,7 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro
private val selectedGroupStore: SelectedGroupDataSource, private val selectedGroupStore: SelectedGroupDataSource,
private val session: Session, private val session: Session,
private val stringProvider: StringProvider private val stringProvider: StringProvider
) : VectorViewModel<GroupListViewState>(initialState) { ) : VectorViewModel<GroupListViewState, GroupListActions>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -81,7 +81,7 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro
} }
} }
fun accept(action: GroupListActions) { override fun handle(action: GroupListActions) {
when (action) { when (action) {
is GroupListActions.SelectGroup -> handleSelectGroup(action) is GroupListActions.SelectGroup -> handleSelectGroup(action)
} }

View file

@ -21,9 +21,9 @@ import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.model.message.MessageFileContent import im.vector.matrix.android.api.session.room.model.message.MessageFileContent
import im.vector.matrix.android.api.session.room.timeline.Timeline import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class RoomDetailActions { sealed class RoomDetailActions : VectorViewModelAction {
data class SaveDraft(val draft: String) : RoomDetailActions() data class SaveDraft(val draft: String) : RoomDetailActions()
data class SendMessage(val text: String, val autoMarkdown: Boolean) : RoomDetailActions() data class SendMessage(val text: String, val autoMarkdown: Boolean) : RoomDetailActions()
data class SendMedia(val attachments: List<ContentAttachmentData>) : RoomDetailActions() data class SendMedia(val attachments: List<ContentAttachmentData>) : RoomDetailActions()

View file

@ -278,8 +278,8 @@ class RoomDetailFragment @Inject constructor(
if (savedInstanceState == null) { if (savedInstanceState == null) {
when (val sharedData = roomDetailArgs.sharedData) { when (val sharedData = roomDetailArgs.sharedData) {
is SharedData.Text -> roomDetailViewModel.process(RoomDetailActions.SendMessage(sharedData.text, false)) is SharedData.Text -> roomDetailViewModel.handle(RoomDetailActions.SendMessage(sharedData.text, false))
is SharedData.Attachments -> roomDetailViewModel.process(RoomDetailActions.SendMedia(sharedData.attachmentData)) is SharedData.Attachments -> roomDetailViewModel.handle(RoomDetailActions.SendMedia(sharedData.attachmentData))
null -> Timber.v("No share data to process") null -> Timber.v("No share data to process")
} }
} }
@ -323,7 +323,7 @@ class RoomDetailFragment @Inject constructor(
private fun setupNotificationView() { private fun setupNotificationView() {
notificationAreaView.delegate = object : NotificationAreaView.Delegate { notificationAreaView.delegate = object : NotificationAreaView.Delegate {
override fun onTombstoneEventClicked(tombstoneEvent: Event) { override fun onTombstoneEventClicked(tombstoneEvent: Event) {
roomDetailViewModel.process(RoomDetailActions.HandleTombstoneEvent(tombstoneEvent)) roomDetailViewModel.handle(RoomDetailActions.HandleTombstoneEvent(tombstoneEvent))
} }
override fun resendUnsentEvents() { override fun resendUnsentEvents() {
@ -355,11 +355,11 @@ class RoomDetailFragment @Inject constructor(
// This a temporary option during dev as it is not super stable // This a temporary option during dev as it is not super stable
// Cancel all pending actions in room queue and post a dummy // Cancel all pending actions in room queue and post a dummy
// Then mark all sending events as undelivered // Then mark all sending events as undelivered
roomDetailViewModel.process(RoomDetailActions.ClearSendQueue) roomDetailViewModel.handle(RoomDetailActions.ClearSendQueue)
return true return true
} }
if (item.itemId == R.id.resend_all) { if (item.itemId == R.id.resend_all) {
roomDetailViewModel.process(RoomDetailActions.ResendAll) roomDetailViewModel.handle(RoomDetailActions.ResendAll)
return true return true
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
@ -427,7 +427,7 @@ class RoomDetailFragment @Inject constructor(
notificationDrawerManager.setCurrentRoom(null) notificationDrawerManager.setCurrentRoom(null)
roomDetailViewModel.process(RoomDetailActions.SaveDraft(composerLayout.composerEditText.text.toString())) roomDetailViewModel.handle(RoomDetailActions.SaveDraft(composerLayout.composerEditText.text.toString()))
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
@ -440,7 +440,7 @@ class RoomDetailFragment @Inject constructor(
val reaction = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_REACTION_RESULT) val reaction = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_REACTION_RESULT)
?: return ?: return
// TODO check if already reacted with that? // TODO check if already reacted with that?
roomDetailViewModel.process(RoomDetailActions.SendReaction(eventId, reaction)) roomDetailViewModel.handle(RoomDetailActions.SendReaction(eventId, reaction))
} }
} }
} }
@ -498,7 +498,7 @@ class RoomDetailFragment @Inject constructor(
override fun performQuickReplyOnHolder(model: EpoxyModel<*>) { override fun performQuickReplyOnHolder(model: EpoxyModel<*>) {
(model as? AbsMessageItem)?.attributes?.informationData?.let { (model as? AbsMessageItem)?.attributes?.informationData?.let {
val eventId = it.eventId val eventId = it.eventId
roomDetailViewModel.process(RoomDetailActions.EnterReplyMode(eventId, composerLayout.composerEditText.text.toString())) roomDetailViewModel.handle(RoomDetailActions.EnterReplyMode(eventId, composerLayout.composerEditText.text.toString()))
} }
} }
@ -601,11 +601,11 @@ class RoomDetailFragment @Inject constructor(
val textMessage = composerLayout.composerEditText.text.toString() val textMessage = composerLayout.composerEditText.text.toString()
if (textMessage.isNotBlank()) { if (textMessage.isNotBlank()) {
lockSendButton = true lockSendButton = true
roomDetailViewModel.process(RoomDetailActions.SendMessage(textMessage, vectorPreferences.isMarkdownEnabled())) roomDetailViewModel.handle(RoomDetailActions.SendMessage(textMessage, vectorPreferences.isMarkdownEnabled()))
} }
} }
composerLayout.composerRelatedMessageCloseButton.setOnClickListener { composerLayout.composerRelatedMessageCloseButton.setOnClickListener {
roomDetailViewModel.process(RoomDetailActions.ExitSpecialMode(composerLayout.composerEditText.text.toString())) roomDetailViewModel.handle(RoomDetailActions.ExitSpecialMode(composerLayout.composerEditText.text.toString()))
} }
composerLayout.callback = object : TextComposerView.Callback { composerLayout.callback = object : TextComposerView.Callback {
override fun onRichContentSelected(contentUri: Uri): Boolean { override fun onRichContentSelected(contentUri: Uri): Boolean {
@ -760,7 +760,7 @@ class RoomDetailFragment @Inject constructor(
.setView(layout) .setView(layout)
.setPositiveButton(R.string.report_content_custom_submit) { _, _ -> .setPositiveButton(R.string.report_content_custom_submit) { _, _ ->
val reason = input.text.toString() val reason = input.text.toString()
roomDetailViewModel.process(RoomDetailActions.ReportContent(action.eventId, action.senderId, reason)) roomDetailViewModel.handle(RoomDetailActions.ReportContent(action.eventId, action.senderId, reason))
} }
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.show() .show()
@ -785,7 +785,7 @@ class RoomDetailFragment @Inject constructor(
.setMessage(R.string.content_reported_as_spam_content) .setMessage(R.string.content_reported_as_spam_content)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.block_user) { _, _ -> .setNegativeButton(R.string.block_user) { _, _ ->
roomDetailViewModel.process(RoomDetailActions.IgnoreUser(data.senderId)) roomDetailViewModel.handle(RoomDetailActions.IgnoreUser(data.senderId))
} }
.show() .show()
.withColoredButton(DialogInterface.BUTTON_NEGATIVE) .withColoredButton(DialogInterface.BUTTON_NEGATIVE)
@ -796,7 +796,7 @@ class RoomDetailFragment @Inject constructor(
.setMessage(R.string.content_reported_as_inappropriate_content) .setMessage(R.string.content_reported_as_inappropriate_content)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.block_user) { _, _ -> .setNegativeButton(R.string.block_user) { _, _ ->
roomDetailViewModel.process(RoomDetailActions.IgnoreUser(data.senderId)) roomDetailViewModel.handle(RoomDetailActions.IgnoreUser(data.senderId))
} }
.show() .show()
.withColoredButton(DialogInterface.BUTTON_NEGATIVE) .withColoredButton(DialogInterface.BUTTON_NEGATIVE)
@ -807,7 +807,7 @@ class RoomDetailFragment @Inject constructor(
.setMessage(R.string.content_reported_content) .setMessage(R.string.content_reported_content)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.block_user) { _, _ -> .setNegativeButton(R.string.block_user) { _, _ ->
roomDetailViewModel.process(RoomDetailActions.IgnoreUser(data.senderId)) roomDetailViewModel.handle(RoomDetailActions.IgnoreUser(data.senderId))
} }
.show() .show()
.withColoredButton(DialogInterface.BUTTON_NEGATIVE) .withColoredButton(DialogInterface.BUTTON_NEGATIVE)
@ -831,7 +831,7 @@ class RoomDetailFragment @Inject constructor(
showSnackWithMessage(getString(R.string.navigate_to_room_when_already_in_the_room)) showSnackWithMessage(getString(R.string.navigate_to_room_when_already_in_the_room))
} else { } else {
// Highlight and scroll to this event // Highlight and scroll to this event
roomDetailViewModel.process(RoomDetailActions.NavigateToEvent(eventId, true)) roomDetailViewModel.handle(RoomDetailActions.NavigateToEvent(eventId, true))
} }
return true return true
} }
@ -859,11 +859,11 @@ class RoomDetailFragment @Inject constructor(
} }
override fun onEventVisible(event: TimelineEvent) { override fun onEventVisible(event: TimelineEvent) {
roomDetailViewModel.process(RoomDetailActions.TimelineEventTurnsVisible(event)) roomDetailViewModel.handle(RoomDetailActions.TimelineEventTurnsVisible(event))
} }
override fun onEventInvisible(event: TimelineEvent) { override fun onEventInvisible(event: TimelineEvent) {
roomDetailViewModel.process(RoomDetailActions.TimelineEventTurnsInvisible(event)) roomDetailViewModel.handle(RoomDetailActions.TimelineEventTurnsInvisible(event))
} }
override fun onEncryptedMessageClicked(informationData: MessageInformationData, view: View) { override fun onEncryptedMessageClicked(informationData: MessageInformationData, view: View) {
@ -902,7 +902,7 @@ class RoomDetailFragment @Inject constructor(
val action = RoomDetailActions.DownloadFile(eventId, messageFileContent) val action = RoomDetailActions.DownloadFile(eventId, messageFileContent)
// We need WRITE_EXTERNAL permission // We need WRITE_EXTERNAL permission
if (checkPermissions(PERMISSIONS_FOR_WRITING_FILES, this, PERMISSION_REQUEST_CODE_DOWNLOAD_FILE)) { if (checkPermissions(PERMISSIONS_FOR_WRITING_FILES, this, PERMISSION_REQUEST_CODE_DOWNLOAD_FILE)) {
roomDetailViewModel.process(action) roomDetailViewModel.handle(action)
} else { } else {
roomDetailViewModel.pendingAction = action roomDetailViewModel.pendingAction = action
} }
@ -915,7 +915,7 @@ class RoomDetailFragment @Inject constructor(
val action = roomDetailViewModel.pendingAction val action = roomDetailViewModel.pendingAction
if (action != null) { if (action != null) {
roomDetailViewModel.pendingAction = null roomDetailViewModel.pendingAction = null
roomDetailViewModel.process(action) roomDetailViewModel.handle(action)
} }
} }
PERMISSION_REQUEST_CODE_INCOMING_URI -> { PERMISSION_REQUEST_CODE_INCOMING_URI -> {
@ -946,7 +946,7 @@ class RoomDetailFragment @Inject constructor(
} }
override fun onLoadMore(direction: Timeline.Direction) { override fun onLoadMore(direction: Timeline.Direction) {
roomDetailViewModel.process(RoomDetailActions.LoadMoreTimelineEvents(direction)) roomDetailViewModel.handle(RoomDetailActions.LoadMoreTimelineEvents(direction))
} }
override fun onEventCellClicked(informationData: MessageInformationData, messageContent: MessageContent?, view: View) { override fun onEventCellClicked(informationData: MessageInformationData, messageContent: MessageContent?, view: View) {
@ -976,10 +976,10 @@ class RoomDetailFragment @Inject constructor(
override fun onClickOnReactionPill(informationData: MessageInformationData, reaction: String, on: Boolean) { override fun onClickOnReactionPill(informationData: MessageInformationData, reaction: String, on: Boolean) {
if (on) { if (on) {
// we should test the current real state of reaction on this event // we should test the current real state of reaction on this event
roomDetailViewModel.process(RoomDetailActions.SendReaction(informationData.eventId, reaction)) roomDetailViewModel.handle(RoomDetailActions.SendReaction(informationData.eventId, reaction))
} else { } else {
// I need to redact a reaction // I need to redact a reaction
roomDetailViewModel.process(RoomDetailActions.UndoReaction(informationData.eventId, reaction)) roomDetailViewModel.handle(RoomDetailActions.UndoReaction(informationData.eventId, reaction))
} }
} }
@ -1027,14 +1027,14 @@ class RoomDetailFragment @Inject constructor(
} }
} }
if (nextReadMarkerId != null) { if (nextReadMarkerId != null) {
roomDetailViewModel.process(RoomDetailActions.SetReadMarkerAction(nextReadMarkerId)) roomDetailViewModel.handle(RoomDetailActions.SetReadMarkerAction(nextReadMarkerId))
} }
} }
// AutocompleteUserPresenter.Callback // AutocompleteUserPresenter.Callback
override fun onQueryUsers(query: CharSequence?) { override fun onQueryUsers(query: CharSequence?) {
textComposerViewModel.process(TextComposerActions.QueryUsers(query)) textComposerViewModel.handle(TextComposerActions.QueryUsers(query))
} }
private fun handleActions(action: EventSharedAction) { private fun handleActions(action: EventSharedAction) {
@ -1053,7 +1053,7 @@ class RoomDetailFragment @Inject constructor(
showSnackWithMessage(msg, Snackbar.LENGTH_SHORT) showSnackWithMessage(msg, Snackbar.LENGTH_SHORT)
} }
is EventSharedAction.Delete -> { is EventSharedAction.Delete -> {
roomDetailViewModel.process(RoomDetailActions.RedactAction(action.eventId, context?.getString(R.string.event_redacted_by_user_reason))) roomDetailViewModel.handle(RoomDetailActions.RedactAction(action.eventId, context?.getString(R.string.event_redacted_by_user_reason)))
} }
is EventSharedAction.Share -> { is EventSharedAction.Share -> {
// TODO current data communication is too limited // TODO current data communication is too limited
@ -1110,16 +1110,16 @@ class RoomDetailFragment @Inject constructor(
} }
is EventSharedAction.QuickReact -> { is EventSharedAction.QuickReact -> {
// eventId,ClickedOn,Add // eventId,ClickedOn,Add
roomDetailViewModel.process(RoomDetailActions.UpdateQuickReactAction(action.eventId, action.clickedOn, action.add)) roomDetailViewModel.handle(RoomDetailActions.UpdateQuickReactAction(action.eventId, action.clickedOn, action.add))
} }
is EventSharedAction.Edit -> { is EventSharedAction.Edit -> {
roomDetailViewModel.process(RoomDetailActions.EnterEditMode(action.eventId, composerLayout.composerEditText.text.toString())) roomDetailViewModel.handle(RoomDetailActions.EnterEditMode(action.eventId, composerLayout.composerEditText.text.toString()))
} }
is EventSharedAction.Quote -> { is EventSharedAction.Quote -> {
roomDetailViewModel.process(RoomDetailActions.EnterQuoteMode(action.eventId, composerLayout.composerEditText.text.toString())) roomDetailViewModel.handle(RoomDetailActions.EnterQuoteMode(action.eventId, composerLayout.composerEditText.text.toString()))
} }
is EventSharedAction.Reply -> { is EventSharedAction.Reply -> {
roomDetailViewModel.process(RoomDetailActions.EnterReplyMode(action.eventId, composerLayout.composerEditText.text.toString())) roomDetailViewModel.handle(RoomDetailActions.EnterReplyMode(action.eventId, composerLayout.composerEditText.text.toString()))
} }
is EventSharedAction.CopyPermalink -> { is EventSharedAction.CopyPermalink -> {
val permalink = PermalinkFactory.createPermalink(roomDetailArgs.roomId, action.eventId) val permalink = PermalinkFactory.createPermalink(roomDetailArgs.roomId, action.eventId)
@ -1127,17 +1127,17 @@ class RoomDetailFragment @Inject constructor(
showSnackWithMessage(requireContext().getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT) showSnackWithMessage(requireContext().getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT)
} }
is EventSharedAction.Resend -> { is EventSharedAction.Resend -> {
roomDetailViewModel.process(RoomDetailActions.ResendMessage(action.eventId)) roomDetailViewModel.handle(RoomDetailActions.ResendMessage(action.eventId))
} }
is EventSharedAction.Remove -> { is EventSharedAction.Remove -> {
roomDetailViewModel.process(RoomDetailActions.RemoveFailedEcho(action.eventId)) roomDetailViewModel.handle(RoomDetailActions.RemoveFailedEcho(action.eventId))
} }
is EventSharedAction.ReportContentSpam -> { is EventSharedAction.ReportContentSpam -> {
roomDetailViewModel.process(RoomDetailActions.ReportContent( roomDetailViewModel.handle(RoomDetailActions.ReportContent(
action.eventId, action.senderId, "This message is spam", spam = true)) action.eventId, action.senderId, "This message is spam", spam = true))
} }
is EventSharedAction.ReportContentInappropriate -> { is EventSharedAction.ReportContentInappropriate -> {
roomDetailViewModel.process(RoomDetailActions.ReportContent( roomDetailViewModel.handle(RoomDetailActions.ReportContent(
action.eventId, action.senderId, "This message is inappropriate", inappropriate = true)) action.eventId, action.senderId, "This message is inappropriate", inappropriate = true))
} }
is EventSharedAction.ReportContentCustom -> { is EventSharedAction.ReportContentCustom -> {
@ -1210,22 +1210,22 @@ class RoomDetailFragment @Inject constructor(
override fun onAcceptInvite() { override fun onAcceptInvite() {
notificationDrawerManager.clearMemberShipNotificationForRoom(roomDetailArgs.roomId) notificationDrawerManager.clearMemberShipNotificationForRoom(roomDetailArgs.roomId)
roomDetailViewModel.process(RoomDetailActions.AcceptInvite) roomDetailViewModel.handle(RoomDetailActions.AcceptInvite)
} }
override fun onRejectInvite() { override fun onRejectInvite() {
notificationDrawerManager.clearMemberShipNotificationForRoom(roomDetailArgs.roomId) notificationDrawerManager.clearMemberShipNotificationForRoom(roomDetailArgs.roomId)
roomDetailViewModel.process(RoomDetailActions.RejectInvite) roomDetailViewModel.handle(RoomDetailActions.RejectInvite)
} }
// JumpToReadMarkerView.Callback // JumpToReadMarkerView.Callback
override fun onJumpToReadMarkerClicked(readMarkerId: String) { override fun onJumpToReadMarkerClicked(readMarkerId: String) {
roomDetailViewModel.process(RoomDetailActions.NavigateToEvent(readMarkerId, false)) roomDetailViewModel.handle(RoomDetailActions.NavigateToEvent(readMarkerId, false))
} }
override fun onClearReadMarkerClicked() { override fun onClearReadMarkerClicked() {
roomDetailViewModel.process(RoomDetailActions.MarkAllAsRead) roomDetailViewModel.handle(RoomDetailActions.MarkAllAsRead)
} }
// AttachmentTypeSelectorView.Callback // AttachmentTypeSelectorView.Callback
@ -1252,7 +1252,7 @@ class RoomDetailFragment @Inject constructor(
// AttachmentsHelper.Callback // AttachmentsHelper.Callback
override fun onContentAttachmentsReady(attachments: List<ContentAttachmentData>) { override fun onContentAttachmentsReady(attachments: List<ContentAttachmentData>) {
roomDetailViewModel.process(RoomDetailActions.SendMedia(attachments)) roomDetailViewModel.handle(RoomDetailActions.SendMedia(attachments))
} }
override fun onAttachmentsProcessFailed() { override fun onAttachmentsProcessFailed() {
@ -1262,6 +1262,6 @@ class RoomDetailFragment @Inject constructor(
override fun onContactAttachmentReady(contactAttachment: ContactAttachment) { override fun onContactAttachmentReady(contactAttachment: ContactAttachment) {
super.onContactAttachmentReady(contactAttachment) super.onContactAttachmentReady(contactAttachment)
val formattedContact = contactAttachment.toHumanReadable() val formattedContact = contactAttachment.toHumanReadable()
roomDetailViewModel.process(RoomDetailActions.SendMessage(formattedContact, false)) roomDetailViewModel.handle(RoomDetailActions.SendMessage(formattedContact, false))
} }
} }

View file

@ -69,7 +69,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val session: Session private val session: Session
) : VectorViewModel<RoomDetailViewState>(initialState) { ) : VectorViewModel<RoomDetailViewState, RoomDetailActions>(initialState) {
private val room = session.getRoom(initialState.roomId)!! private val room = session.getRoom(initialState.roomId)!!
private val eventId = initialState.eventId private val eventId = initialState.eventId
@ -129,7 +129,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
setState { copy(timeline = this@RoomDetailViewModel.timeline) } setState { copy(timeline = this@RoomDetailViewModel.timeline) }
} }
fun process(action: RoomDetailActions) { override fun handle(action: RoomDetailActions) {
when (action) { when (action) {
is RoomDetailActions.SaveDraft -> handleSaveDraft(action) is RoomDetailActions.SaveDraft -> handleSaveDraft(action)
is RoomDetailActions.SendMessage -> handleSendMessage(action) is RoomDetailActions.SendMessage -> handleSendMessage(action)

View file

@ -16,6 +16,8 @@
package im.vector.riotx.features.home.room.detail.composer package im.vector.riotx.features.home.room.detail.composer
sealed class TextComposerActions { import im.vector.riotx.core.platform.VectorViewModelAction
sealed class TextComposerActions : VectorViewModelAction {
data class QueryUsers(val query: CharSequence?) : TextComposerActions() data class QueryUsers(val query: CharSequence?) : TextComposerActions()
} }

View file

@ -36,7 +36,7 @@ typealias AutocompleteUserQuery = CharSequence
class TextComposerViewModel @AssistedInject constructor(@Assisted initialState: TextComposerViewState, class TextComposerViewModel @AssistedInject constructor(@Assisted initialState: TextComposerViewState,
private val session: Session private val session: Session
) : VectorViewModel<TextComposerViewState>(initialState) { ) : VectorViewModel<TextComposerViewState, TextComposerActions>(initialState) {
private val room = session.getRoom(initialState.roomId)!! private val room = session.getRoom(initialState.roomId)!!
private val roomId = initialState.roomId private val roomId = initialState.roomId
@ -52,7 +52,7 @@ class TextComposerViewModel @AssistedInject constructor(@Assisted initialState:
@JvmStatic @JvmStatic
override fun create(viewModelContext: ViewModelContext, state: TextComposerViewState): TextComposerViewModel? { override fun create(viewModelContext: ViewModelContext, state: TextComposerViewState): TextComposerViewModel? {
val fragment : RoomDetailFragment = (viewModelContext as FragmentViewModelContext).fragment() val fragment: RoomDetailFragment = (viewModelContext as FragmentViewModelContext).fragment()
return fragment.textComposerViewModelFactory.create(state) return fragment.textComposerViewModelFactory.create(state)
} }
} }
@ -61,7 +61,7 @@ class TextComposerViewModel @AssistedInject constructor(@Assisted initialState:
observeUsersQuery() observeUsersQuery()
} }
fun process(action: TextComposerActions) { override fun handle(action: TextComposerActions) {
when (action) { when (action) {
is TextComposerActions.QueryUsers -> handleQueryUsers(action) is TextComposerActions.QueryUsers -> handleQueryUsers(action)
} }
@ -84,8 +84,7 @@ class TextComposerViewModel @AssistedInject constructor(@Assisted initialState:
users users
} else { } else {
users.filter { users.filter {
it.displayName?.startsWith(prefix = filter, ignoreCase = true) it.displayName?.startsWith(prefix = filter, ignoreCase = true) ?: false
?: false
} }
} }
} }

View file

@ -72,7 +72,7 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message
override fun didSelectMenuAction(eventAction: EventSharedAction) { override fun didSelectMenuAction(eventAction: EventSharedAction) {
if (eventAction is EventSharedAction.ReportContent) { if (eventAction is EventSharedAction.ReportContent) {
// Toggle report menu // Toggle report menu
viewModel.toggleReportMenu() viewModel.handle(MessageActionActions.ToggleReportMenu)
} else { } else {
messageActionsStore.post(eventAction) messageActionsStore.post(eventAction)
dismiss() dismiss()

View file

@ -37,13 +37,13 @@ import im.vector.matrix.rx.unwrap
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.extensions.canReact import im.vector.riotx.core.extensions.canReact
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.platform.VectorViewModelAction
import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.features.home.room.detail.timeline.format.NoticeEventFormatter import im.vector.riotx.features.home.room.detail.timeline.format.NoticeEventFormatter
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
import im.vector.riotx.features.html.EventHtmlRenderer import im.vector.riotx.features.html.EventHtmlRenderer
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.*
import java.util.Locale
/** /**
* Quick reactions state * Quick reactions state
@ -77,8 +77,12 @@ data class MessageActionState(
fun canReact() = timelineEvent()?.canReact() == true fun canReact() = timelineEvent()?.canReact() == true
} }
sealed class MessageActionActions : VectorViewModelAction {
object ToggleReportMenu : MessageActionActions()
}
/** /**
* Information related to an event and used to display preview in contextual bottomsheet. * Information related to an event and used to display preview in contextual bottom sheet.
*/ */
class MessageActionsViewModel @AssistedInject constructor(@Assisted class MessageActionsViewModel @AssistedInject constructor(@Assisted
initialState: MessageActionState, initialState: MessageActionState,
@ -86,7 +90,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
private val session: Session, private val session: Session,
private val noticeEventFormatter: NoticeEventFormatter, private val noticeEventFormatter: NoticeEventFormatter,
private val stringProvider: StringProvider private val stringProvider: StringProvider
) : VectorViewModel<MessageActionState>(initialState) { ) : VectorViewModel<MessageActionState, MessageActionActions>(initialState) {
private val eventId = initialState.eventId private val eventId = initialState.eventId
private val informationData = initialState.informationData private val informationData = initialState.informationData
@ -113,7 +117,13 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
observeEventAction() observeEventAction()
} }
fun toggleReportMenu() = withState { override fun handle(action: MessageActionActions) {
when (action) {
MessageActionActions.ToggleReportMenu -> toggleReportMenu()
}
}
private fun toggleReportMenu() = withState {
setState { setState {
copy( copy(
expendedReportContentMenu = it.expendedReportContentMenu.not() expendedReportContentMenu = it.expendedReportContentMenu.not()

View file

@ -26,11 +26,12 @@ import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.message.MessageContent import im.vector.matrix.android.api.session.room.model.message.MessageContent
import im.vector.matrix.android.api.session.room.model.message.isReply import im.vector.matrix.android.api.session.room.model.message.isReply
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.date.VectorDateFormatter import im.vector.riotx.core.date.VectorDateFormatter
import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
import timber.log.Timber import timber.log.Timber
import java.util.UUID import java.util.*
data class ViewEditHistoryViewState( data class ViewEditHistoryViewState(
val eventId: String, val eventId: String,
@ -46,7 +47,7 @@ class ViewEditHistoryViewModel @AssistedInject constructor(@Assisted
initialState: ViewEditHistoryViewState, initialState: ViewEditHistoryViewState,
val session: Session, val session: Session,
val dateFormatter: VectorDateFormatter val dateFormatter: VectorDateFormatter
) : VectorViewModel<ViewEditHistoryViewState>(initialState) { ) : VectorViewModel<ViewEditHistoryViewState, EmptyAction>(initialState) {
private val roomId = initialState.roomId private val roomId = initialState.roomId
private val eventId = initialState.eventId private val eventId = initialState.eventId
@ -115,4 +116,8 @@ class ViewEditHistoryViewModel @AssistedInject constructor(@Assisted
} }
}) })
} }
override fun handle(action: EmptyAction) {
// No op
}
} }

View file

@ -16,20 +16,16 @@
package im.vector.riotx.features.home.room.detail.timeline.reactions package im.vector.riotx.features.home.room.detail.timeline.reactions
import com.airbnb.mvrx.Async import com.airbnb.mvrx.*
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.room.model.ReactionAggregatedSummary import im.vector.matrix.android.api.session.room.model.ReactionAggregatedSummary
import im.vector.matrix.rx.RxRoom import im.vector.matrix.rx.RxRoom
import im.vector.matrix.rx.unwrap import im.vector.matrix.rx.unwrap
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.date.VectorDateFormatter import im.vector.riotx.core.date.VectorDateFormatter
import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
@ -55,15 +51,15 @@ data class ReactionInfo(
* Used to display the list of members that reacted to a given event * Used to display the list of members that reacted to a given event
*/ */
class ViewReactionsViewModel @AssistedInject constructor(@Assisted class ViewReactionsViewModel @AssistedInject constructor(@Assisted
initialState: DisplayReactionsViewState, initialState: DisplayReactionsViewState,
private val session: Session, private val session: Session,
private val dateFormatter: VectorDateFormatter private val dateFormatter: VectorDateFormatter
) : VectorViewModel<DisplayReactionsViewState>(initialState) { ) : VectorViewModel<DisplayReactionsViewState, EmptyAction>(initialState) {
private val roomId = initialState.roomId private val roomId = initialState.roomId
private val eventId = initialState.eventId private val eventId = initialState.eventId
private val room = session.getRoom(roomId) private val room = session.getRoom(roomId)
?: throw IllegalStateException("Shouldn't use this ViewModel without a room") ?: throw IllegalStateException("Shouldn't use this ViewModel without a room")
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -103,7 +99,7 @@ class ViewReactionsViewModel @AssistedInject constructor(@Assisted
.fromIterable(summary.sourceEvents) .fromIterable(summary.sourceEvents)
.map { .map {
val event = room.getTimeLineEvent(it) val event = room.getTimeLineEvent(it)
?: throw RuntimeException("Your eventId is not valid") ?: throw RuntimeException("Your eventId is not valid")
ReactionInfo( ReactionInfo(
event.root.eventId!!, event.root.eventId!!,
summary.key, summary.key,
@ -115,4 +111,8 @@ class ViewReactionsViewModel @AssistedInject constructor(@Assisted
} }
}.toList() }.toList()
} }
override fun handle(action: EmptyAction) {
// No op
}
} }

View file

@ -18,8 +18,9 @@ package im.vector.riotx.features.home.room.list
import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.notification.RoomNotificationState import im.vector.matrix.android.api.session.room.notification.RoomNotificationState
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class RoomListActions { sealed class RoomListActions : VectorViewModelAction {
data class SelectRoom(val roomSummary: RoomSummary) : RoomListActions() data class SelectRoom(val roomSummary: RoomSummary) : RoomListActions()
data class ToggleCategory(val category: RoomCategory) : RoomListActions() data class ToggleCategory(val category: RoomCategory) : RoomListActions()
data class AcceptInvitation(val roomSummary: RoomSummary) : RoomListActions() data class AcceptInvitation(val roomSummary: RoomSummary) : RoomListActions()

View file

@ -85,7 +85,7 @@ class RoomListFragment @Inject constructor(
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.menu_home_mark_all_as_read -> { R.id.menu_home_mark_all_as_read -> {
roomListViewModel.accept(RoomListActions.MarkAllRoomsRead) roomListViewModel.handle(RoomListActions.MarkAllRoomsRead)
return true return true
} }
} }
@ -183,7 +183,7 @@ class RoomListFragment @Inject constructor(
// Scroll the list to top // Scroll the list to top
roomListEpoxyRecyclerView.scrollToPosition(0) roomListEpoxyRecyclerView.scrollToPosition(0)
roomListViewModel.accept(RoomListActions.FilterWith(filter)) roomListViewModel.handle(RoomListActions.FilterWith(filter))
} }
override fun openRoomDirectory(initialFilter: String) { override fun openRoomDirectory(initialFilter: String) {
@ -219,16 +219,16 @@ class RoomListFragment @Inject constructor(
private fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) { private fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) {
when (quickAction) { when (quickAction) {
is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> { is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> {
roomListViewModel.accept(RoomListActions.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES_NOISY)) roomListViewModel.handle(RoomListActions.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES_NOISY))
} }
is RoomListQuickActionsSharedAction.NotificationsAll -> { is RoomListQuickActionsSharedAction.NotificationsAll -> {
roomListViewModel.accept(RoomListActions.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES)) roomListViewModel.handle(RoomListActions.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES))
} }
is RoomListQuickActionsSharedAction.NotificationsMentionsOnly -> { is RoomListQuickActionsSharedAction.NotificationsMentionsOnly -> {
roomListViewModel.accept(RoomListActions.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MENTIONS_ONLY)) roomListViewModel.handle(RoomListActions.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MENTIONS_ONLY))
} }
is RoomListQuickActionsSharedAction.NotificationsMute -> { is RoomListQuickActionsSharedAction.NotificationsMute -> {
roomListViewModel.accept(RoomListActions.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MUTE)) roomListViewModel.handle(RoomListActions.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MUTE))
} }
is RoomListQuickActionsSharedAction.Settings -> { is RoomListQuickActionsSharedAction.Settings -> {
vectorBaseActivity.notImplemented("Opening room settings") vectorBaseActivity.notImplemented("Opening room settings")
@ -238,7 +238,7 @@ class RoomListFragment @Inject constructor(
.setTitle(R.string.room_participants_leave_prompt_title) .setTitle(R.string.room_participants_leave_prompt_title)
.setMessage(R.string.room_participants_leave_prompt_msg) .setMessage(R.string.room_participants_leave_prompt_msg)
.setPositiveButton(R.string.leave) { _, _ -> .setPositiveButton(R.string.leave) { _, _ ->
roomListViewModel.accept(RoomListActions.LeaveRoom(quickAction.roomId)) roomListViewModel.handle(RoomListActions.LeaveRoom(quickAction.roomId))
} }
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.show() .show()
@ -342,7 +342,7 @@ class RoomListFragment @Inject constructor(
// RoomSummaryController.Callback ************************************************************** // RoomSummaryController.Callback **************************************************************
override fun onRoomClicked(room: RoomSummary) { override fun onRoomClicked(room: RoomSummary) {
roomListViewModel.accept(RoomListActions.SelectRoom(room)) roomListViewModel.handle(RoomListActions.SelectRoom(room))
} }
override fun onRoomLongClicked(room: RoomSummary): Boolean { override fun onRoomLongClicked(room: RoomSummary): Boolean {
@ -354,16 +354,16 @@ class RoomListFragment @Inject constructor(
override fun onAcceptRoomInvitation(room: RoomSummary) { override fun onAcceptRoomInvitation(room: RoomSummary) {
notificationDrawerManager.clearMemberShipNotificationForRoom(room.roomId) notificationDrawerManager.clearMemberShipNotificationForRoom(room.roomId)
roomListViewModel.accept(RoomListActions.AcceptInvitation(room)) roomListViewModel.handle(RoomListActions.AcceptInvitation(room))
} }
override fun onRejectRoomInvitation(room: RoomSummary) { override fun onRejectRoomInvitation(room: RoomSummary) {
notificationDrawerManager.clearMemberShipNotificationForRoom(room.roomId) notificationDrawerManager.clearMemberShipNotificationForRoom(room.roomId)
roomListViewModel.accept(RoomListActions.RejectInvitation(room)) roomListViewModel.handle(RoomListActions.RejectInvitation(room))
} }
override fun onToggleRoomCategory(roomCategory: RoomCategory) { override fun onToggleRoomCategory(roomCategory: RoomCategory) {
roomListViewModel.accept(RoomListActions.ToggleCategory(roomCategory)) roomListViewModel.handle(RoomListActions.ToggleCategory(roomCategory))
} }
override fun createRoom(initialName: String) { override fun createRoom(initialName: String) {

View file

@ -36,7 +36,7 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
private val roomSummariesSource: DataSource<List<RoomSummary>>, private val roomSummariesSource: DataSource<List<RoomSummary>>,
private val alphabeticalRoomComparator: AlphabeticalRoomComparator, private val alphabeticalRoomComparator: AlphabeticalRoomComparator,
private val chronologicalRoomComparator: ChronologicalRoomComparator) private val chronologicalRoomComparator: ChronologicalRoomComparator)
: VectorViewModel<RoomListViewState>(initialState) { : VectorViewModel<RoomListViewState, RoomListActions>(initialState) {
interface Factory { interface Factory {
fun create(initialState: RoomListViewState): RoomListViewModel fun create(initialState: RoomListViewState): RoomListViewModel
@ -61,7 +61,7 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
observeRoomSummaries() observeRoomSummaries()
} }
fun accept(action: RoomListActions) { override fun handle(action: RoomListActions) {
when (action) { when (action) {
is RoomListActions.SelectRoom -> handleSelectRoom(action) is RoomListActions.SelectRoom -> handleSelectRoom(action)
is RoomListActions.ToggleCategory -> handleToggleCategory(action) is RoomListActions.ToggleCategory -> handleToggleCategory(action)

View file

@ -15,17 +15,20 @@
*/ */
package im.vector.riotx.features.home.room.list.actions package im.vector.riotx.features.home.room.list.actions
import com.airbnb.mvrx.* import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.matrix.rx.unwrap import im.vector.matrix.rx.unwrap
import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
class RoomListQuickActionsViewModel @AssistedInject constructor(@Assisted initialState: RoomListQuickActionsState, class RoomListQuickActionsViewModel @AssistedInject constructor(@Assisted initialState: RoomListQuickActionsState,
session: Session session: Session
) : VectorViewModel<RoomListQuickActionsState>(initialState) { ) : VectorViewModel<RoomListQuickActionsState, EmptyAction>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -65,4 +68,8 @@ class RoomListQuickActionsViewModel @AssistedInject constructor(@Assisted initia
copy(roomSummary = it) copy(roomSummary = it)
} }
} }
override fun handle(action: EmptyAction) {
// No op
}
} }

View file

@ -17,9 +17,9 @@
package im.vector.riotx.features.login package im.vector.riotx.features.login
import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class LoginActions { sealed class LoginActions : VectorViewModelAction {
data class UpdateHomeServer(val homeServerUrl: String) : LoginActions() data class UpdateHomeServer(val homeServerUrl: String) : LoginActions()
data class Login(val login: String, val password: String) : LoginActions() data class Login(val login: String, val password: String) : LoginActions()
data class SsoLoginSuccess(val credentials: Credentials) : LoginActions() data class SsoLoginSuccess(val credentials: Credentials) : LoginActions()

View file

@ -42,7 +42,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
private val activeSessionHolder: ActiveSessionHolder, private val activeSessionHolder: ActiveSessionHolder,
private val pushRuleTriggerListener: PushRuleTriggerListener, private val pushRuleTriggerListener: PushRuleTriggerListener,
private val sessionListener: SessionListener) private val sessionListener: SessionListener)
: VectorViewModel<LoginViewState>(initialState) { : VectorViewModel<LoginViewState, LoginActions>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -67,7 +67,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
private var homeServerConnectionConfig: HomeServerConnectionConfig? = null private var homeServerConnectionConfig: HomeServerConnectionConfig? = null
private var currentTask: Cancelable? = null private var currentTask: Cancelable? = null
fun handle(action: LoginActions) { override fun handle(action: LoginActions) {
when (action) { when (action) {
is LoginActions.InitWith -> handleInitWith(action) is LoginActions.InitWith -> handleInitWith(action)
is LoginActions.UpdateHomeServer -> handleUpdateHomeserver(action) is LoginActions.UpdateHomeServer -> handleUpdateHomeserver(action)

View file

@ -63,7 +63,7 @@ class EmojiReactionPickerActivity : VectorBaseActivity(),
@Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider @Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider
val searchResultViewModel: EmojiSearchResultViewModel by viewModel() private val searchResultViewModel: EmojiSearchResultViewModel by viewModel()
private var tabLayoutSelectionListener = object : TabLayout.OnTabSelectedListener { private var tabLayoutSelectionListener = object : TabLayout.OnTabSelectedListener {
override fun onTabReselected(tab: TabLayout.Tab) { override fun onTabReselected(tab: TabLayout.Tab) {
@ -201,7 +201,7 @@ class EmojiReactionPickerActivity : VectorBaseActivity(),
tabLayout.isVisible = false tabLayout.isVisible = false
emojiPickerWholeListFragmentContainer.isVisible = false emojiPickerWholeListFragmentContainer.isVisible = false
emojiPickerFilteredListFragmentContainer.isVisible = true emojiPickerFilteredListFragmentContainer.isVisible = true
searchResultViewModel.updateQuery(query) searchResultViewModel.handle(EmojiSearchActions.UpdateQuery(query))
} }
} }

View file

@ -0,0 +1,23 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.features.reactions
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class EmojiSearchActions : VectorViewModelAction {
data class UpdateQuery(val queryString: String) : EmojiSearchActions()
}

View file

@ -26,17 +26,23 @@ data class EmojiSearchResultViewState(
) : MvRxState ) : MvRxState
class EmojiSearchResultViewModel(val dataSource: EmojiDataSource, initialState: EmojiSearchResultViewState) class EmojiSearchResultViewModel(val dataSource: EmojiDataSource, initialState: EmojiSearchResultViewState)
: VectorViewModel<EmojiSearchResultViewState>(initialState) { : VectorViewModel<EmojiSearchResultViewState, EmojiSearchActions>(initialState) {
fun updateQuery(queryString: String) { override fun handle(action: EmojiSearchActions) {
when (action) {
is EmojiSearchActions.UpdateQuery -> updateQuery(action)
}
}
private fun updateQuery(action: EmojiSearchActions.UpdateQuery) {
setState { setState {
copy( copy(
query = queryString, query = action.queryString,
results = dataSource.rawData?.emojis?.toList() results = dataSource.rawData?.emojis?.toList()
?.map { it.second } ?.map { it.second }
?.filter { ?.filter {
it.name.contains(queryString, true) it.name.contains(action.queryString, true)
|| queryString.split("\\s".toRegex()).fold(true, { prev, q -> || action.queryString.split("\\s".toRegex()).fold(true, { prev, q ->
prev && (it.keywords?.any { it.contains(q, true) } ?: false) prev && (it.keywords?.any { it.contains(q, true) } ?: false)
}) })
} ?: emptyList() } ?: emptyList()

View file

@ -66,7 +66,7 @@ class PublicRoomsFragment @Inject constructor(
publicRoomsFilter.queryTextChanges() publicRoomsFilter.queryTextChanges()
.debounce(500, TimeUnit.MILLISECONDS) .debounce(500, TimeUnit.MILLISECONDS)
.subscribeBy { .subscribeBy {
viewModel.filterWith(it.toString()) viewModel.handle(RoomDirectoryActions.FilterWith(it.toString()))
} }
.disposeOnDestroy() .disposeOnDestroy()
@ -130,14 +130,14 @@ class PublicRoomsFragment @Inject constructor(
override fun onPublicRoomJoin(publicRoom: PublicRoom) { override fun onPublicRoomJoin(publicRoom: PublicRoom) {
Timber.v("PublicRoomJoinClicked: $publicRoom") Timber.v("PublicRoomJoinClicked: $publicRoom")
viewModel.joinRoom(publicRoom) viewModel.handle(RoomDirectoryActions.JoinRoom(publicRoom))
} }
override fun loadMore() { override fun loadMore() {
viewModel.loadMore() viewModel.handle(RoomDirectoryActions.LoadMore)
} }
var initialValueSet = false private var initialValueSet = false
override fun invalidate() = withState(viewModel) { state -> override fun invalidate() = withState(viewModel) { state ->
if (!initialValueSet) { if (!initialValueSet) {

View file

@ -0,0 +1,28 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.features.roomdirectory
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class RoomDirectoryActions : VectorViewModelAction {
data class SetRoomDirectoryData(val roomDirectoryData: RoomDirectoryData) : RoomDirectoryActions()
data class FilterWith(val filter: String) : RoomDirectoryActions()
object LoadMore : RoomDirectoryActions()
data class JoinRoom(val publicRoom: PublicRoom) : RoomDirectoryActions()
}

View file

@ -26,6 +26,7 @@ import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.extensions.addFragment import im.vector.riotx.core.extensions.addFragment
import im.vector.riotx.core.extensions.addFragmentToBackstack import im.vector.riotx.core.extensions.addFragmentToBackstack
import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomActions
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomViewModel import im.vector.riotx.features.roomdirectory.createroom.CreateRoomViewModel
import im.vector.riotx.features.roomdirectory.picker.RoomDirectoryPickerFragment import im.vector.riotx.features.roomdirectory.picker.RoomDirectoryPickerFragment
@ -50,7 +51,7 @@ class RoomDirectoryActivity : VectorBaseActivity() {
actionViewModel = ViewModelProviders.of(this, viewModelFactory).get(RoomDirectorySharedActionViewModel::class.java) actionViewModel = ViewModelProviders.of(this, viewModelFactory).get(RoomDirectorySharedActionViewModel::class.java)
if (isFirstCreation()) { if (isFirstCreation()) {
roomDirectoryViewModel.filterWith(intent?.getStringExtra(INITIAL_FILTER) ?: "") roomDirectoryViewModel.handle(RoomDirectoryActions.FilterWith(intent?.getStringExtra(INITIAL_FILTER) ?: ""))
} }
actionViewModel.observe() actionViewModel.observe()
@ -66,7 +67,7 @@ class RoomDirectoryActivity : VectorBaseActivity() {
roomDirectoryViewModel.selectSubscribe(this, PublicRoomsViewState::currentFilter) { currentFilter -> roomDirectoryViewModel.selectSubscribe(this, PublicRoomsViewState::currentFilter) { currentFilter ->
// Transmit the filter to the createRoomViewModel // Transmit the filter to the createRoomViewModel
createRoomViewModel.setName(currentFilter) createRoomViewModel.handle(CreateRoomActions.SetName(currentFilter))
} }
} }

View file

@ -25,7 +25,6 @@ import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.session.Session 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.Membership
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsFilter import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsFilter
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse
@ -40,7 +39,8 @@ import timber.log.Timber
private const val PUBLIC_ROOMS_LIMIT = 20 private const val PUBLIC_ROOMS_LIMIT = 20
class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState: PublicRoomsViewState, class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState: PublicRoomsViewState,
private val session: Session) : VectorViewModel<PublicRoomsViewState>(initialState) { private val session: Session)
: VectorViewModel<PublicRoomsViewState, RoomDirectoryActions>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -103,23 +103,32 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
.disposeOnClear() .disposeOnClear()
} }
fun setRoomDirectoryData(roomDirectoryData: RoomDirectoryData) { override fun handle(action: RoomDirectoryActions) {
if (this.roomDirectoryData == roomDirectoryData) { when (action) {
is RoomDirectoryActions.SetRoomDirectoryData -> setRoomDirectoryData(action)
is RoomDirectoryActions.FilterWith -> filterWith(action)
RoomDirectoryActions.LoadMore -> loadMore()
is RoomDirectoryActions.JoinRoom -> joinRoom(action)
}
}
private fun setRoomDirectoryData(action: RoomDirectoryActions.SetRoomDirectoryData) {
if (this.roomDirectoryData == action.roomDirectoryData) {
return return
} }
this.roomDirectoryData = roomDirectoryData this.roomDirectoryData = action.roomDirectoryData
reset("") reset("")
load("") load("")
} }
fun filterWith(filter: String) = withState { state -> private fun filterWith(action: RoomDirectoryActions.FilterWith) = withState { state ->
if (state.currentFilter != filter) { if (state.currentFilter != action.filter) {
currentTask?.cancel() currentTask?.cancel()
reset(filter) reset(action.filter)
load(filter) load(action.filter)
} }
} }
@ -138,7 +147,7 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
} }
} }
fun loadMore() = withState { state -> private fun loadMore() = withState { state ->
if (currentTask == null) { if (currentTask == null) {
setState { setState {
copy( copy(
@ -192,8 +201,10 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
}) })
} }
fun joinRoom(publicRoom: PublicRoom) = withState { state -> private fun joinRoom(action: RoomDirectoryActions.JoinRoom) = withState { state ->
if (state.joiningRoomsIds.contains(publicRoom.roomId)) { val roomId = action.publicRoom.roomId
if (state.joiningRoomsIds.contains(roomId)) {
// Request already sent, should not happen // Request already sent, should not happen
Timber.w("Try to join an already joining room. Should not happen") Timber.w("Try to join an already joining room. Should not happen")
return@withState return@withState
@ -201,11 +212,11 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
setState { setState {
copy( copy(
joiningRoomsIds = joiningRoomsIds.toMutableSet().apply { add(publicRoom.roomId) } joiningRoomsIds = joiningRoomsIds.toMutableSet().apply { add(roomId) }
) )
} }
session.joinRoom(publicRoom.roomId, emptyList(), object : MatrixCallback<Unit> { session.joinRoom(roomId, emptyList(), object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) { override fun onSuccess(data: Unit) {
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data. // We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
// Instead, we wait for the room to be joined // Instead, we wait for the room to be joined
@ -217,8 +228,8 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
setState { setState {
copy( copy(
joiningRoomsIds = joiningRoomsIds.toMutableSet().apply { remove(publicRoom.roomId) }, joiningRoomsIds = joiningRoomsIds.toMutableSet().apply { remove(roomId) },
joiningErrorRoomsIds = joiningErrorRoomsIds.toMutableSet().apply { add(publicRoom.roomId) } joiningErrorRoomsIds = joiningErrorRoomsIds.toMutableSet().apply { add(roomId) }
) )
} }
} }

View file

@ -0,0 +1,26 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.features.roomdirectory.createroom
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class CreateRoomActions : VectorViewModelAction {
data class SetName(val name: String) : CreateRoomActions()
data class SetIsPublic(val isPublic: Boolean) : CreateRoomActions()
data class SetIsInRoomDirectory(val isInRoomDirectory: Boolean) : CreateRoomActions()
object Create : CreateRoomActions()
}

View file

@ -49,7 +49,7 @@ class CreateRoomActivity : VectorBaseActivity(), ToolbarConfigurable {
override fun initUiAndData() { override fun initUiAndData() {
if (isFirstCreation()) { if (isFirstCreation()) {
addFragment(R.id.simpleFragmentContainer, CreateRoomFragment::class.java) addFragment(R.id.simpleFragmentContainer, CreateRoomFragment::class.java)
createRoomViewModel.setName(intent?.getStringExtra(INITIAL_NAME) ?: "") createRoomViewModel.handle(CreateRoomActions.SetName(intent?.getStringExtra(INITIAL_NAME) ?: ""))
} }
} }

View file

@ -31,7 +31,7 @@ import kotlinx.android.synthetic.main.fragment_create_room.*
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class CreateRoomFragment @Inject constructor(private val createRoomController: CreateRoomController): VectorBaseFragment(), CreateRoomController.Listener { class CreateRoomFragment @Inject constructor(private val createRoomController: CreateRoomController) : VectorBaseFragment(), CreateRoomController.Listener {
private lateinit var actionViewModel: RoomDirectorySharedActionViewModel private lateinit var actionViewModel: RoomDirectorySharedActionViewModel
private val viewModel: CreateRoomViewModel by activityViewModel() private val viewModel: CreateRoomViewModel by activityViewModel()
@ -53,7 +53,7 @@ class CreateRoomFragment @Inject constructor(private val createRoomController: C
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) { return when (item.itemId) {
R.id.action_create_room -> { R.id.action_create_room -> {
viewModel.doCreateRoom() viewModel.handle(CreateRoomActions.Create)
true true
} }
else -> else ->
@ -71,20 +71,20 @@ class CreateRoomFragment @Inject constructor(private val createRoomController: C
} }
override fun onNameChange(newName: String) { override fun onNameChange(newName: String) {
viewModel.setName(newName) viewModel.handle(CreateRoomActions.SetName(newName))
} }
override fun setIsPublic(isPublic: Boolean) { override fun setIsPublic(isPublic: Boolean) {
viewModel.setIsPublic(isPublic) viewModel.handle(CreateRoomActions.SetIsPublic(isPublic))
} }
override fun setIsInRoomDirectory(isInRoomDirectory: Boolean) { override fun setIsInRoomDirectory(isInRoomDirectory: Boolean) {
viewModel.setIsInRoomDirectory(isInRoomDirectory) viewModel.handle(CreateRoomActions.SetIsInRoomDirectory(isInRoomDirectory))
} }
override fun retry() { override fun retry() {
Timber.v("Retry") Timber.v("Retry")
viewModel.doCreateRoom() viewModel.handle(CreateRoomActions.Create)
} }
override fun invalidate() = withState(viewModel) { state -> override fun invalidate() = withState(viewModel) { state ->

View file

@ -30,7 +30,7 @@ import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: CreateRoomViewState, class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: CreateRoomViewState,
private val session: Session private val session: Session
) : VectorViewModel<CreateRoomViewState>(initialState) { ) : VectorViewModel<CreateRoomViewState, CreateRoomActions>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -51,13 +51,22 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: Cr
} }
} }
fun setName(newName: String) = setState { copy(roomName = newName) } override fun handle(action: CreateRoomActions) {
when (action) {
is CreateRoomActions.SetName -> setName(action)
is CreateRoomActions.SetIsPublic -> setIsPublic(action)
is CreateRoomActions.SetIsInRoomDirectory -> setIsInRoomDirectory(action)
is CreateRoomActions.Create -> doCreateRoom()
}
}
fun setIsPublic(isPublic: Boolean) = setState { copy(isPublic = isPublic) } private fun setName(action: CreateRoomActions.SetName) = setState { copy(roomName = action.name) }
fun setIsInRoomDirectory(isInRoomDirectory: Boolean) = setState { copy(isInRoomDirectory = isInRoomDirectory) } private fun setIsPublic(action: CreateRoomActions.SetIsPublic) = setState { copy(isPublic = action.isPublic) }
fun doCreateRoom() = withState { state -> private fun setIsInRoomDirectory(action: CreateRoomActions.SetIsInRoomDirectory) = setState { copy(isInRoomDirectory = action.isInRoomDirectory) }
private fun doCreateRoom() = withState { state ->
if (state.asyncCreateRoomRequest is Loading || state.asyncCreateRoomRequest is Success) { if (state.asyncCreateRoomRequest is Loading || state.asyncCreateRoomRequest is Success) {
return@withState return@withState
} }

View file

@ -0,0 +1,23 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.features.roomdirectory.picker
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class RoomDirectoryPickerActions : VectorViewModelAction {
object Retry : RoomDirectoryPickerActions()
}

View file

@ -27,6 +27,7 @@ import com.airbnb.mvrx.withState
import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.features.roomdirectory.RoomDirectoryActions
import im.vector.riotx.features.roomdirectory.RoomDirectorySharedAction import im.vector.riotx.features.roomdirectory.RoomDirectorySharedAction
import im.vector.riotx.features.roomdirectory.RoomDirectorySharedActionViewModel import im.vector.riotx.features.roomdirectory.RoomDirectorySharedActionViewModel
import im.vector.riotx.features.roomdirectory.RoomDirectoryViewModel import im.vector.riotx.features.roomdirectory.RoomDirectoryViewModel
@ -86,14 +87,14 @@ class RoomDirectoryPickerFragment @Inject constructor(val roomDirectoryPickerVie
override fun onRoomDirectoryClicked(roomDirectoryData: RoomDirectoryData) { override fun onRoomDirectoryClicked(roomDirectoryData: RoomDirectoryData) {
Timber.v("onRoomDirectoryClicked: $roomDirectoryData") Timber.v("onRoomDirectoryClicked: $roomDirectoryData")
viewModel.setRoomDirectoryData(roomDirectoryData) viewModel.handle(RoomDirectoryActions.SetRoomDirectoryData(roomDirectoryData))
actionViewModel.post(RoomDirectorySharedAction.Back) actionViewModel.post(RoomDirectorySharedAction.Back)
} }
override fun retry() { override fun retry() {
Timber.v("Retry") Timber.v("Retry")
pickerViewModel.load() pickerViewModel.handle(RoomDirectoryPickerActions.Retry)
} }
override fun invalidate() = withState(pickerViewModel) { state -> override fun invalidate() = withState(pickerViewModel) { state ->

View file

@ -16,11 +16,7 @@
package im.vector.riotx.features.roomdirectory.picker package im.vector.riotx.features.roomdirectory.picker
import com.airbnb.mvrx.Fail import com.airbnb.mvrx.*
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
@ -29,7 +25,8 @@ import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProt
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
class RoomDirectoryPickerViewModel @AssistedInject constructor(@Assisted initialState: RoomDirectoryPickerViewState, class RoomDirectoryPickerViewModel @AssistedInject constructor(@Assisted initialState: RoomDirectoryPickerViewState,
private val session: Session) : VectorViewModel<RoomDirectoryPickerViewState>(initialState) { private val session: Session)
: VectorViewModel<RoomDirectoryPickerViewState, RoomDirectoryPickerActions>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -49,7 +46,7 @@ class RoomDirectoryPickerViewModel @AssistedInject constructor(@Assisted initial
load() load()
} }
fun load() { private fun load() {
session.getThirdPartyProtocol(object : MatrixCallback<Map<String, ThirdPartyProtocol>> { session.getThirdPartyProtocol(object : MatrixCallback<Map<String, ThirdPartyProtocol>> {
override fun onSuccess(data: Map<String, ThirdPartyProtocol>) { override fun onSuccess(data: Map<String, ThirdPartyProtocol>) {
setState { setState {
@ -64,4 +61,10 @@ class RoomDirectoryPickerViewModel @AssistedInject constructor(@Assisted initial
} }
}) })
} }
override fun handle(action: RoomDirectoryPickerActions) {
when (action) {
RoomDirectoryPickerActions.Retry -> load()
}
}
} }

View file

@ -0,0 +1,23 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.features.roomdirectory.roompreview
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class RoomPreviewActions : VectorViewModelAction {
object Join : RoomPreviewActions()
}

View file

@ -72,7 +72,7 @@ class RoomPreviewNoPreviewFragment @Inject constructor(
roomPreviewNoPreviewJoin.callback = object : ButtonStateView.Callback { roomPreviewNoPreviewJoin.callback = object : ButtonStateView.Callback {
override fun onButtonClicked() { override fun onButtonClicked() {
roomPreviewViewModel.joinRoom() roomPreviewViewModel.handle(RoomPreviewActions.Join)
} }
override fun onRetryClicked() { override fun onRetryClicked() {

View file

@ -30,7 +30,8 @@ import im.vector.riotx.features.roomdirectory.JoinState
import timber.log.Timber import timber.log.Timber
class RoomPreviewViewModel @AssistedInject constructor(@Assisted initialState: RoomPreviewViewState, class RoomPreviewViewModel @AssistedInject constructor(@Assisted initialState: RoomPreviewViewState,
private val session: Session) : VectorViewModel<RoomPreviewViewState>(initialState) { private val session: Session)
: VectorViewModel<RoomPreviewViewState, RoomPreviewActions>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -76,7 +77,13 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted initialState: R
.disposeOnClear() .disposeOnClear()
} }
fun joinRoom() = withState { state -> override fun handle(action: RoomPreviewActions) {
when (action) {
RoomPreviewActions.Join -> joinRoom()
}
}
private fun joinRoom() = withState { state ->
if (state.roomJoinState == JoinState.JOINING) { if (state.roomJoinState == JoinState.JOINING) {
// Request already sent, should not happen // Request already sent, should not happen
Timber.w("Try to join an already joining room. Should not happen") Timber.w("Try to join an already joining room. Should not happen")

View file

@ -25,18 +25,20 @@ import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.extensions.postLiveEvent
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.platform.VectorViewModelAction
data class IgnoredUsersViewState( data class IgnoredUsersViewState(
val ignoredUsers: List<User> = emptyList(), val ignoredUsers: List<User> = emptyList(),
val unIgnoreRequest: Async<Unit> = Uninitialized val unIgnoreRequest: Async<Unit> = Uninitialized
) : MvRxState ) : MvRxState
sealed class IgnoredUsersAction { sealed class IgnoredUsersAction : VectorViewModelAction {
data class UnIgnore(val userId: String) : IgnoredUsersAction() data class UnIgnore(val userId: String) : IgnoredUsersAction()
} }
class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState: IgnoredUsersViewState, class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState: IgnoredUsersViewState,
private val session: Session) : VectorViewModel<IgnoredUsersViewState>(initialState) { private val session: Session)
: VectorViewModel<IgnoredUsersViewState, IgnoredUsersAction>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -66,7 +68,7 @@ class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState:
} }
} }
fun handle(action: IgnoredUsersAction) { override fun handle(action: IgnoredUsersAction) {
when (action) { when (action) {
is IgnoredUsersAction.UnIgnore -> handleUnIgnore(action) is IgnoredUsersAction.UnIgnore -> handleUnIgnore(action)
} }

View file

@ -16,17 +16,13 @@
package im.vector.riotx.features.settings.push package im.vector.riotx.features.settings.push
import com.airbnb.mvrx.Async import com.airbnb.mvrx.*
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.pushers.Pusher import im.vector.matrix.android.api.session.pushers.Pusher
import im.vector.matrix.rx.RxSession import im.vector.matrix.rx.RxSession
import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
data class PushGatewayViewState( data class PushGatewayViewState(
@ -34,7 +30,8 @@ data class PushGatewayViewState(
) : MvRxState ) : MvRxState
class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState: PushGatewayViewState, class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState: PushGatewayViewState,
private val session: Session) : VectorViewModel<PushGatewayViewState>(initialState) { private val session: Session)
: VectorViewModel<PushGatewayViewState, EmptyAction>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -60,4 +57,8 @@ class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState:
copy(pushGateways = it) copy(pushGateways = it)
} }
} }
override fun handle(action: EmptyAction) {
// No op
}
} }

View file

@ -20,13 +20,15 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.ViewModelContext
import im.vector.matrix.android.api.pushrules.rest.PushRule import im.vector.matrix.android.api.pushrules.rest.PushRule
import im.vector.riotx.core.di.HasScreenInjector import im.vector.riotx.core.di.HasScreenInjector
import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
data class PushRulesViewState( data class PushRulesViewState(
val rules: List<PushRule> = emptyList() val rules: List<PushRule> = emptyList()
) : MvRxState ) : MvRxState
class PushRulesViewModel(initialState: PushRulesViewState) : VectorViewModel<PushRulesViewState>(initialState) { class PushRulesViewModel(initialState: PushRulesViewState)
: VectorViewModel<PushRulesViewState, EmptyAction>(initialState) {
companion object : MvRxViewModelFactory<PushRulesViewModel, PushRulesViewState> { companion object : MvRxViewModelFactory<PushRulesViewModel, PushRulesViewState> {
@ -36,4 +38,8 @@ class PushRulesViewModel(initialState: PushRulesViewState) : VectorViewModel<Pus
return PushRulesViewState(rules) return PushRulesViewState(rules)
} }
} }
override fun handle(action: EmptyAction) {
// No op
}
} }

View file

@ -24,6 +24,7 @@ import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.ActiveSessionDataSource import im.vector.riotx.ActiveSessionDataSource
import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
@ -37,7 +38,7 @@ data class IncomingShareState(private val dummy: Boolean = false) : MvRxState
class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState: IncomingShareState, class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState: IncomingShareState,
private val sessionObservableStore: ActiveSessionDataSource, private val sessionObservableStore: ActiveSessionDataSource,
private val shareRoomListObservableStore: ShareRoomListDataSource) private val shareRoomListObservableStore: ShareRoomListDataSource)
: VectorViewModel<IncomingShareState>(initialState) { : VectorViewModel<IncomingShareState, EmptyAction>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -70,4 +71,8 @@ class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState:
} }
.disposeOnClear() .disposeOnClear()
} }
override fun handle(action: EmptyAction) {
// No op
}
} }