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.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) {
// Generic handling of any request error
@ -52,4 +56,6 @@ abstract class VectorViewModel<S : MvRxState>(initialState: S)
.onErrorReturn { Fail(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()
if (supportFragmentManager.fragments.isEmpty()) {
replaceFragment(R.id.container, KeysBackupSettingsFragment::class.java)
viewModel.init()
viewModel.handle(KeyBackupSettingsActions.Init)
}
// 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)
.setCancelable(false)
.setPositiveButton(R.string.keys_backup_settings_delete_confirm_title) { _, _ ->
viewModel.deleteCurrentBackup()
viewModel.handle(KeyBackupSettingsActions.DeleteKeyBackup)
}
.setNegativeButton(R.string.cancel, null)
.setCancelable(true)
@ -75,10 +75,10 @@ class KeysBackupSettingsFragment @Inject constructor(private val keysBackupSetti
}
override fun loadTrustData() {
viewModel.getKeysBackupTrust()
viewModel.handle(KeyBackupSettingsActions.GetKeyBackupTrust)
}
override fun loadKeysBackupState() {
viewModel.init()
viewModel.handle(KeyBackupSettingsActions.Init)
}
}

View file

@ -15,13 +15,7 @@
*/
package im.vector.riotx.features.crypto.keysbackup.settings
import com.airbnb.mvrx.ActivityViewModelContext
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.airbnb.mvrx.*
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
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,
session: Session
) : VectorViewModel<KeysBackupSettingViewState>(initialState),
KeysBackupStateListener {
) : VectorViewModel<KeysBackupSettingViewState, KeyBackupSettingsActions>(initialState),
KeysBackupStateListener {
@AssistedInject.Factory
interface Factory {
@ -64,11 +58,19 @@ class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialS
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> {})
}
fun getKeysBackupTrust() = withState { state ->
private fun getKeysBackupTrust() = withState { state ->
val versionResult = keysBackupService.keysBackupVersion
if (state.keysBackupVersionTrust is Uninitialized && versionResult != null) {
@ -116,7 +118,7 @@ class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialS
getKeysBackupTrust()
}
fun deleteCurrentBackup() {
private fun deleteCurrentBackup() {
val keysBackupService = keysBackupService
if (keysBackupService.currentBackupVersion != null) {
@ -153,6 +155,6 @@ class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialS
val currentBackupState = keysBackupService.state
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
else -> RoomListFragment.DisplayMode.HOME
}
viewModel.switchDisplayMode(displayMode)
viewModel.handle(HomeDetailAction.SwitchDisplayMode(displayMode))
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.resources.StringProvider
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 io.reactivex.schedulers.Schedulers
@ -41,7 +40,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
private val selectedGroupStore: SelectedGroupDataSource,
private val homeRoomListStore: HomeRoomListDataSource,
private val stringProvider: StringProvider)
: VectorViewModel<HomeDetailViewState>(initialState) {
: VectorViewModel<HomeDetailViewState, HomeDetailAction>(initialState) {
@AssistedInject.Factory
interface Factory {
@ -70,13 +69,19 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
observeRoomSummaries()
}
fun switchDisplayMode(displayMode: RoomListFragment.DisplayMode) = withState { state ->
if (state.displayMode != displayMode) {
override fun handle(action: HomeDetailAction) {
when (action) {
is HomeDetailAction.SwitchDisplayMode -> handleSwitchDisplayMode(action)
}
}
private fun handleSwitchDisplayMode(action: HomeDetailAction.SwitchDisplayMode) = withState { state ->
if (state.displayMode != action.displayMode) {
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
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()
data class FilterKnownUsers(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
initialState: CreateDirectRoomViewState,
private val session: Session)
: VectorViewModel<CreateDirectRoomViewState>(initialState) {
: VectorViewModel<CreateDirectRoomViewState, CreateDirectRoomActions>(initialState) {
@AssistedInject.Factory
interface Factory {
@ -79,7 +79,7 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
observeDirectoryUsers()
}
fun handle(action: CreateDirectRoomActions) {
override fun handle(action: CreateDirectRoomActions) {
when (action) {
is CreateDirectRoomActions.CreateRoomAndInviteSelectedUsers -> createRoomAndInviteSelectedUsers()
is CreateDirectRoomActions.FilterKnownUsers -> knownUsersFilter.accept(Option.just(action.value))

View file

@ -17,8 +17,8 @@
package im.vector.riotx.features.home.group
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()
}

View file

@ -62,6 +62,6 @@ class GroupListFragment @Inject constructor(
}
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 session: Session,
private val stringProvider: StringProvider
) : VectorViewModel<GroupListViewState>(initialState) {
) : VectorViewModel<GroupListViewState, GroupListActions>(initialState) {
@AssistedInject.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) {
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.timeline.Timeline
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 SendMessage(val text: String, val autoMarkdown: Boolean) : RoomDetailActions()
data class SendMedia(val attachments: List<ContentAttachmentData>) : RoomDetailActions()

View file

@ -278,8 +278,8 @@ class RoomDetailFragment @Inject constructor(
if (savedInstanceState == null) {
when (val sharedData = roomDetailArgs.sharedData) {
is SharedData.Text -> roomDetailViewModel.process(RoomDetailActions.SendMessage(sharedData.text, false))
is SharedData.Attachments -> roomDetailViewModel.process(RoomDetailActions.SendMedia(sharedData.attachmentData))
is SharedData.Text -> roomDetailViewModel.handle(RoomDetailActions.SendMessage(sharedData.text, false))
is SharedData.Attachments -> roomDetailViewModel.handle(RoomDetailActions.SendMedia(sharedData.attachmentData))
null -> Timber.v("No share data to process")
}
}
@ -323,7 +323,7 @@ class RoomDetailFragment @Inject constructor(
private fun setupNotificationView() {
notificationAreaView.delegate = object : NotificationAreaView.Delegate {
override fun onTombstoneEventClicked(tombstoneEvent: Event) {
roomDetailViewModel.process(RoomDetailActions.HandleTombstoneEvent(tombstoneEvent))
roomDetailViewModel.handle(RoomDetailActions.HandleTombstoneEvent(tombstoneEvent))
}
override fun resendUnsentEvents() {
@ -355,11 +355,11 @@ class RoomDetailFragment @Inject constructor(
// This a temporary option during dev as it is not super stable
// Cancel all pending actions in room queue and post a dummy
// Then mark all sending events as undelivered
roomDetailViewModel.process(RoomDetailActions.ClearSendQueue)
roomDetailViewModel.handle(RoomDetailActions.ClearSendQueue)
return true
}
if (item.itemId == R.id.resend_all) {
roomDetailViewModel.process(RoomDetailActions.ResendAll)
roomDetailViewModel.handle(RoomDetailActions.ResendAll)
return true
}
return super.onOptionsItemSelected(item)
@ -427,7 +427,7 @@ class RoomDetailFragment @Inject constructor(
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?) {
@ -440,7 +440,7 @@ class RoomDetailFragment @Inject constructor(
val reaction = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_REACTION_RESULT)
?: return
// 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<*>) {
(model as? AbsMessageItem)?.attributes?.informationData?.let {
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()
if (textMessage.isNotBlank()) {
lockSendButton = true
roomDetailViewModel.process(RoomDetailActions.SendMessage(textMessage, vectorPreferences.isMarkdownEnabled()))
roomDetailViewModel.handle(RoomDetailActions.SendMessage(textMessage, vectorPreferences.isMarkdownEnabled()))
}
}
composerLayout.composerRelatedMessageCloseButton.setOnClickListener {
roomDetailViewModel.process(RoomDetailActions.ExitSpecialMode(composerLayout.composerEditText.text.toString()))
roomDetailViewModel.handle(RoomDetailActions.ExitSpecialMode(composerLayout.composerEditText.text.toString()))
}
composerLayout.callback = object : TextComposerView.Callback {
override fun onRichContentSelected(contentUri: Uri): Boolean {
@ -760,7 +760,7 @@ class RoomDetailFragment @Inject constructor(
.setView(layout)
.setPositiveButton(R.string.report_content_custom_submit) { _, _ ->
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)
.show()
@ -785,7 +785,7 @@ class RoomDetailFragment @Inject constructor(
.setMessage(R.string.content_reported_as_spam_content)
.setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.block_user) { _, _ ->
roomDetailViewModel.process(RoomDetailActions.IgnoreUser(data.senderId))
roomDetailViewModel.handle(RoomDetailActions.IgnoreUser(data.senderId))
}
.show()
.withColoredButton(DialogInterface.BUTTON_NEGATIVE)
@ -796,7 +796,7 @@ class RoomDetailFragment @Inject constructor(
.setMessage(R.string.content_reported_as_inappropriate_content)
.setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.block_user) { _, _ ->
roomDetailViewModel.process(RoomDetailActions.IgnoreUser(data.senderId))
roomDetailViewModel.handle(RoomDetailActions.IgnoreUser(data.senderId))
}
.show()
.withColoredButton(DialogInterface.BUTTON_NEGATIVE)
@ -807,7 +807,7 @@ class RoomDetailFragment @Inject constructor(
.setMessage(R.string.content_reported_content)
.setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.block_user) { _, _ ->
roomDetailViewModel.process(RoomDetailActions.IgnoreUser(data.senderId))
roomDetailViewModel.handle(RoomDetailActions.IgnoreUser(data.senderId))
}
.show()
.withColoredButton(DialogInterface.BUTTON_NEGATIVE)
@ -831,7 +831,7 @@ class RoomDetailFragment @Inject constructor(
showSnackWithMessage(getString(R.string.navigate_to_room_when_already_in_the_room))
} else {
// Highlight and scroll to this event
roomDetailViewModel.process(RoomDetailActions.NavigateToEvent(eventId, true))
roomDetailViewModel.handle(RoomDetailActions.NavigateToEvent(eventId, true))
}
return true
}
@ -859,11 +859,11 @@ class RoomDetailFragment @Inject constructor(
}
override fun onEventVisible(event: TimelineEvent) {
roomDetailViewModel.process(RoomDetailActions.TimelineEventTurnsVisible(event))
roomDetailViewModel.handle(RoomDetailActions.TimelineEventTurnsVisible(event))
}
override fun onEventInvisible(event: TimelineEvent) {
roomDetailViewModel.process(RoomDetailActions.TimelineEventTurnsInvisible(event))
roomDetailViewModel.handle(RoomDetailActions.TimelineEventTurnsInvisible(event))
}
override fun onEncryptedMessageClicked(informationData: MessageInformationData, view: View) {
@ -902,7 +902,7 @@ class RoomDetailFragment @Inject constructor(
val action = RoomDetailActions.DownloadFile(eventId, messageFileContent)
// We need WRITE_EXTERNAL permission
if (checkPermissions(PERMISSIONS_FOR_WRITING_FILES, this, PERMISSION_REQUEST_CODE_DOWNLOAD_FILE)) {
roomDetailViewModel.process(action)
roomDetailViewModel.handle(action)
} else {
roomDetailViewModel.pendingAction = action
}
@ -915,7 +915,7 @@ class RoomDetailFragment @Inject constructor(
val action = roomDetailViewModel.pendingAction
if (action != null) {
roomDetailViewModel.pendingAction = null
roomDetailViewModel.process(action)
roomDetailViewModel.handle(action)
}
}
PERMISSION_REQUEST_CODE_INCOMING_URI -> {
@ -946,7 +946,7 @@ class RoomDetailFragment @Inject constructor(
}
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) {
@ -976,10 +976,10 @@ class RoomDetailFragment @Inject constructor(
override fun onClickOnReactionPill(informationData: MessageInformationData, reaction: String, on: Boolean) {
if (on) {
// 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 {
// 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) {
roomDetailViewModel.process(RoomDetailActions.SetReadMarkerAction(nextReadMarkerId))
roomDetailViewModel.handle(RoomDetailActions.SetReadMarkerAction(nextReadMarkerId))
}
}
// AutocompleteUserPresenter.Callback
override fun onQueryUsers(query: CharSequence?) {
textComposerViewModel.process(TextComposerActions.QueryUsers(query))
textComposerViewModel.handle(TextComposerActions.QueryUsers(query))
}
private fun handleActions(action: EventSharedAction) {
@ -1053,7 +1053,7 @@ class RoomDetailFragment @Inject constructor(
showSnackWithMessage(msg, Snackbar.LENGTH_SHORT)
}
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 -> {
// TODO current data communication is too limited
@ -1110,16 +1110,16 @@ class RoomDetailFragment @Inject constructor(
}
is EventSharedAction.QuickReact -> {
// 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 -> {
roomDetailViewModel.process(RoomDetailActions.EnterEditMode(action.eventId, composerLayout.composerEditText.text.toString()))
roomDetailViewModel.handle(RoomDetailActions.EnterEditMode(action.eventId, composerLayout.composerEditText.text.toString()))
}
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 -> {
roomDetailViewModel.process(RoomDetailActions.EnterReplyMode(action.eventId, composerLayout.composerEditText.text.toString()))
roomDetailViewModel.handle(RoomDetailActions.EnterReplyMode(action.eventId, composerLayout.composerEditText.text.toString()))
}
is EventSharedAction.CopyPermalink -> {
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)
}
is EventSharedAction.Resend -> {
roomDetailViewModel.process(RoomDetailActions.ResendMessage(action.eventId))
roomDetailViewModel.handle(RoomDetailActions.ResendMessage(action.eventId))
}
is EventSharedAction.Remove -> {
roomDetailViewModel.process(RoomDetailActions.RemoveFailedEcho(action.eventId))
roomDetailViewModel.handle(RoomDetailActions.RemoveFailedEcho(action.eventId))
}
is EventSharedAction.ReportContentSpam -> {
roomDetailViewModel.process(RoomDetailActions.ReportContent(
roomDetailViewModel.handle(RoomDetailActions.ReportContent(
action.eventId, action.senderId, "This message is spam", spam = true))
}
is EventSharedAction.ReportContentInappropriate -> {
roomDetailViewModel.process(RoomDetailActions.ReportContent(
roomDetailViewModel.handle(RoomDetailActions.ReportContent(
action.eventId, action.senderId, "This message is inappropriate", inappropriate = true))
}
is EventSharedAction.ReportContentCustom -> {
@ -1210,22 +1210,22 @@ class RoomDetailFragment @Inject constructor(
override fun onAcceptInvite() {
notificationDrawerManager.clearMemberShipNotificationForRoom(roomDetailArgs.roomId)
roomDetailViewModel.process(RoomDetailActions.AcceptInvite)
roomDetailViewModel.handle(RoomDetailActions.AcceptInvite)
}
override fun onRejectInvite() {
notificationDrawerManager.clearMemberShipNotificationForRoom(roomDetailArgs.roomId)
roomDetailViewModel.process(RoomDetailActions.RejectInvite)
roomDetailViewModel.handle(RoomDetailActions.RejectInvite)
}
// JumpToReadMarkerView.Callback
override fun onJumpToReadMarkerClicked(readMarkerId: String) {
roomDetailViewModel.process(RoomDetailActions.NavigateToEvent(readMarkerId, false))
roomDetailViewModel.handle(RoomDetailActions.NavigateToEvent(readMarkerId, false))
}
override fun onClearReadMarkerClicked() {
roomDetailViewModel.process(RoomDetailActions.MarkAllAsRead)
roomDetailViewModel.handle(RoomDetailActions.MarkAllAsRead)
}
// AttachmentTypeSelectorView.Callback
@ -1252,7 +1252,7 @@ class RoomDetailFragment @Inject constructor(
// AttachmentsHelper.Callback
override fun onContentAttachmentsReady(attachments: List<ContentAttachmentData>) {
roomDetailViewModel.process(RoomDetailActions.SendMedia(attachments))
roomDetailViewModel.handle(RoomDetailActions.SendMedia(attachments))
}
override fun onAttachmentsProcessFailed() {
@ -1262,6 +1262,6 @@ class RoomDetailFragment @Inject constructor(
override fun onContactAttachmentReady(contactAttachment: ContactAttachment) {
super.onContactAttachmentReady(contactAttachment)
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 stringProvider: StringProvider,
private val session: Session
) : VectorViewModel<RoomDetailViewState>(initialState) {
) : VectorViewModel<RoomDetailViewState, RoomDetailActions>(initialState) {
private val room = session.getRoom(initialState.roomId)!!
private val eventId = initialState.eventId
@ -129,7 +129,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
setState { copy(timeline = this@RoomDetailViewModel.timeline) }
}
fun process(action: RoomDetailActions) {
override fun handle(action: RoomDetailActions) {
when (action) {
is RoomDetailActions.SaveDraft -> handleSaveDraft(action)
is RoomDetailActions.SendMessage -> handleSendMessage(action)

View file

@ -16,6 +16,8 @@
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()
}

View file

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

View file

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

View file

@ -37,13 +37,13 @@ import im.vector.matrix.rx.unwrap
import im.vector.riotx.R
import im.vector.riotx.core.extensions.canReact
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.features.home.room.detail.timeline.format.NoticeEventFormatter
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
import im.vector.riotx.features.html.EventHtmlRenderer
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.*
/**
* Quick reactions state
@ -77,8 +77,12 @@ data class MessageActionState(
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
initialState: MessageActionState,
@ -86,7 +90,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
private val session: Session,
private val noticeEventFormatter: NoticeEventFormatter,
private val stringProvider: StringProvider
) : VectorViewModel<MessageActionState>(initialState) {
) : VectorViewModel<MessageActionState, MessageActionActions>(initialState) {
private val eventId = initialState.eventId
private val informationData = initialState.informationData
@ -113,7 +117,13 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
observeEventAction()
}
fun toggleReportMenu() = withState {
override fun handle(action: MessageActionActions) {
when (action) {
MessageActionActions.ToggleReportMenu -> toggleReportMenu()
}
}
private fun toggleReportMenu() = withState {
setState {
copy(
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.isReply
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.platform.EmptyAction
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
import timber.log.Timber
import java.util.UUID
import java.util.*
data class ViewEditHistoryViewState(
val eventId: String,
@ -46,7 +47,7 @@ class ViewEditHistoryViewModel @AssistedInject constructor(@Assisted
initialState: ViewEditHistoryViewState,
val session: Session,
val dateFormatter: VectorDateFormatter
) : VectorViewModel<ViewEditHistoryViewState>(initialState) {
) : VectorViewModel<ViewEditHistoryViewState, EmptyAction>(initialState) {
private val roomId = initialState.roomId
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
import com.airbnb.mvrx.Async
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.airbnb.mvrx.*
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.room.model.ReactionAggregatedSummary
import im.vector.matrix.rx.RxRoom
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.platform.EmptyAction
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
import io.reactivex.Observable
import io.reactivex.Single
@ -55,15 +51,15 @@ data class ReactionInfo(
* Used to display the list of members that reacted to a given event
*/
class ViewReactionsViewModel @AssistedInject constructor(@Assisted
initialState: DisplayReactionsViewState,
initialState: DisplayReactionsViewState,
private val session: Session,
private val dateFormatter: VectorDateFormatter
) : VectorViewModel<DisplayReactionsViewState>(initialState) {
) : VectorViewModel<DisplayReactionsViewState, EmptyAction>(initialState) {
private val roomId = initialState.roomId
private val eventId = initialState.eventId
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
interface Factory {
@ -103,7 +99,7 @@ class ViewReactionsViewModel @AssistedInject constructor(@Assisted
.fromIterable(summary.sourceEvents)
.map {
val event = room.getTimeLineEvent(it)
?: throw RuntimeException("Your eventId is not valid")
?: throw RuntimeException("Your eventId is not valid")
ReactionInfo(
event.root.eventId!!,
summary.key,
@ -115,4 +111,8 @@ class ViewReactionsViewModel @AssistedInject constructor(@Assisted
}
}.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.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 ToggleCategory(val category: RoomCategory) : RoomListActions()
data class AcceptInvitation(val roomSummary: RoomSummary) : RoomListActions()

View file

@ -85,7 +85,7 @@ class RoomListFragment @Inject constructor(
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_home_mark_all_as_read -> {
roomListViewModel.accept(RoomListActions.MarkAllRoomsRead)
roomListViewModel.handle(RoomListActions.MarkAllRoomsRead)
return true
}
}
@ -183,7 +183,7 @@ class RoomListFragment @Inject constructor(
// Scroll the list to top
roomListEpoxyRecyclerView.scrollToPosition(0)
roomListViewModel.accept(RoomListActions.FilterWith(filter))
roomListViewModel.handle(RoomListActions.FilterWith(filter))
}
override fun openRoomDirectory(initialFilter: String) {
@ -219,16 +219,16 @@ class RoomListFragment @Inject constructor(
private fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) {
when (quickAction) {
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 -> {
roomListViewModel.accept(RoomListActions.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES))
roomListViewModel.handle(RoomListActions.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES))
}
is RoomListQuickActionsSharedAction.NotificationsMentionsOnly -> {
roomListViewModel.accept(RoomListActions.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MENTIONS_ONLY))
roomListViewModel.handle(RoomListActions.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MENTIONS_ONLY))
}
is RoomListQuickActionsSharedAction.NotificationsMute -> {
roomListViewModel.accept(RoomListActions.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MUTE))
roomListViewModel.handle(RoomListActions.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MUTE))
}
is RoomListQuickActionsSharedAction.Settings -> {
vectorBaseActivity.notImplemented("Opening room settings")
@ -238,7 +238,7 @@ class RoomListFragment @Inject constructor(
.setTitle(R.string.room_participants_leave_prompt_title)
.setMessage(R.string.room_participants_leave_prompt_msg)
.setPositiveButton(R.string.leave) { _, _ ->
roomListViewModel.accept(RoomListActions.LeaveRoom(quickAction.roomId))
roomListViewModel.handle(RoomListActions.LeaveRoom(quickAction.roomId))
}
.setNegativeButton(R.string.cancel, null)
.show()
@ -342,7 +342,7 @@ class RoomListFragment @Inject constructor(
// RoomSummaryController.Callback **************************************************************
override fun onRoomClicked(room: RoomSummary) {
roomListViewModel.accept(RoomListActions.SelectRoom(room))
roomListViewModel.handle(RoomListActions.SelectRoom(room))
}
override fun onRoomLongClicked(room: RoomSummary): Boolean {
@ -354,16 +354,16 @@ class RoomListFragment @Inject constructor(
override fun onAcceptRoomInvitation(room: RoomSummary) {
notificationDrawerManager.clearMemberShipNotificationForRoom(room.roomId)
roomListViewModel.accept(RoomListActions.AcceptInvitation(room))
roomListViewModel.handle(RoomListActions.AcceptInvitation(room))
}
override fun onRejectRoomInvitation(room: RoomSummary) {
notificationDrawerManager.clearMemberShipNotificationForRoom(room.roomId)
roomListViewModel.accept(RoomListActions.RejectInvitation(room))
roomListViewModel.handle(RoomListActions.RejectInvitation(room))
}
override fun onToggleRoomCategory(roomCategory: RoomCategory) {
roomListViewModel.accept(RoomListActions.ToggleCategory(roomCategory))
roomListViewModel.handle(RoomListActions.ToggleCategory(roomCategory))
}
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 alphabeticalRoomComparator: AlphabeticalRoomComparator,
private val chronologicalRoomComparator: ChronologicalRoomComparator)
: VectorViewModel<RoomListViewState>(initialState) {
: VectorViewModel<RoomListViewState, RoomListActions>(initialState) {
interface Factory {
fun create(initialState: RoomListViewState): RoomListViewModel
@ -61,7 +61,7 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
observeRoomSummaries()
}
fun accept(action: RoomListActions) {
override fun handle(action: RoomListActions) {
when (action) {
is RoomListActions.SelectRoom -> handleSelectRoom(action)
is RoomListActions.ToggleCategory -> handleToggleCategory(action)

View file

@ -15,17 +15,20 @@
*/
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.AssistedInject
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.rx.rx
import im.vector.matrix.rx.unwrap
import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.VectorViewModel
class RoomListQuickActionsViewModel @AssistedInject constructor(@Assisted initialState: RoomListQuickActionsState,
session: Session
) : VectorViewModel<RoomListQuickActionsState>(initialState) {
) : VectorViewModel<RoomListQuickActionsState, EmptyAction>(initialState) {
@AssistedInject.Factory
interface Factory {
@ -65,4 +68,8 @@ class RoomListQuickActionsViewModel @AssistedInject constructor(@Assisted initia
copy(roomSummary = it)
}
}
override fun handle(action: EmptyAction) {
// No op
}
}

View file

@ -17,9 +17,9 @@
package im.vector.riotx.features.login
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 Login(val login: String, val password: String) : 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 pushRuleTriggerListener: PushRuleTriggerListener,
private val sessionListener: SessionListener)
: VectorViewModel<LoginViewState>(initialState) {
: VectorViewModel<LoginViewState, LoginActions>(initialState) {
@AssistedInject.Factory
interface Factory {
@ -67,7 +67,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
private var homeServerConnectionConfig: HomeServerConnectionConfig? = null
private var currentTask: Cancelable? = null
fun handle(action: LoginActions) {
override fun handle(action: LoginActions) {
when (action) {
is LoginActions.InitWith -> handleInitWith(action)
is LoginActions.UpdateHomeServer -> handleUpdateHomeserver(action)

View file

@ -63,7 +63,7 @@ class EmojiReactionPickerActivity : VectorBaseActivity(),
@Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider
val searchResultViewModel: EmojiSearchResultViewModel by viewModel()
private val searchResultViewModel: EmojiSearchResultViewModel by viewModel()
private var tabLayoutSelectionListener = object : TabLayout.OnTabSelectedListener {
override fun onTabReselected(tab: TabLayout.Tab) {
@ -201,7 +201,7 @@ class EmojiReactionPickerActivity : VectorBaseActivity(),
tabLayout.isVisible = false
emojiPickerWholeListFragmentContainer.isVisible = false
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
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 {
copy(
query = queryString,
query = action.queryString,
results = dataSource.rawData?.emojis?.toList()
?.map { it.second }
?.filter {
it.name.contains(queryString, true)
|| queryString.split("\\s".toRegex()).fold(true, { prev, q ->
it.name.contains(action.queryString, true)
|| action.queryString.split("\\s".toRegex()).fold(true, { prev, q ->
prev && (it.keywords?.any { it.contains(q, true) } ?: false)
})
} ?: emptyList()

View file

@ -66,7 +66,7 @@ class PublicRoomsFragment @Inject constructor(
publicRoomsFilter.queryTextChanges()
.debounce(500, TimeUnit.MILLISECONDS)
.subscribeBy {
viewModel.filterWith(it.toString())
viewModel.handle(RoomDirectoryActions.FilterWith(it.toString()))
}
.disposeOnDestroy()
@ -130,14 +130,14 @@ class PublicRoomsFragment @Inject constructor(
override fun onPublicRoomJoin(publicRoom: PublicRoom) {
Timber.v("PublicRoomJoinClicked: $publicRoom")
viewModel.joinRoom(publicRoom)
viewModel.handle(RoomDirectoryActions.JoinRoom(publicRoom))
}
override fun loadMore() {
viewModel.loadMore()
viewModel.handle(RoomDirectoryActions.LoadMore)
}
var initialValueSet = false
private var initialValueSet = false
override fun invalidate() = withState(viewModel) { state ->
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.addFragmentToBackstack
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.CreateRoomViewModel
import im.vector.riotx.features.roomdirectory.picker.RoomDirectoryPickerFragment
@ -50,7 +51,7 @@ class RoomDirectoryActivity : VectorBaseActivity() {
actionViewModel = ViewModelProviders.of(this, viewModelFactory).get(RoomDirectorySharedActionViewModel::class.java)
if (isFirstCreation()) {
roomDirectoryViewModel.filterWith(intent?.getStringExtra(INITIAL_FILTER) ?: "")
roomDirectoryViewModel.handle(RoomDirectoryActions.FilterWith(intent?.getStringExtra(INITIAL_FILTER) ?: ""))
}
actionViewModel.observe()
@ -66,7 +67,7 @@ class RoomDirectoryActivity : VectorBaseActivity() {
roomDirectoryViewModel.selectSubscribe(this, PublicRoomsViewState::currentFilter) { currentFilter ->
// 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.session.Session
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.PublicRoomsParams
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
class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState: PublicRoomsViewState,
private val session: Session) : VectorViewModel<PublicRoomsViewState>(initialState) {
private val session: Session)
: VectorViewModel<PublicRoomsViewState, RoomDirectoryActions>(initialState) {
@AssistedInject.Factory
interface Factory {
@ -103,23 +103,32 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
.disposeOnClear()
}
fun setRoomDirectoryData(roomDirectoryData: RoomDirectoryData) {
if (this.roomDirectoryData == roomDirectoryData) {
override fun handle(action: RoomDirectoryActions) {
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
}
this.roomDirectoryData = roomDirectoryData
this.roomDirectoryData = action.roomDirectoryData
reset("")
load("")
}
fun filterWith(filter: String) = withState { state ->
if (state.currentFilter != filter) {
private fun filterWith(action: RoomDirectoryActions.FilterWith) = withState { state ->
if (state.currentFilter != action.filter) {
currentTask?.cancel()
reset(filter)
load(filter)
reset(action.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) {
setState {
copy(
@ -192,8 +201,10 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
})
}
fun joinRoom(publicRoom: PublicRoom) = withState { state ->
if (state.joiningRoomsIds.contains(publicRoom.roomId)) {
private fun joinRoom(action: RoomDirectoryActions.JoinRoom) = withState { state ->
val roomId = action.publicRoom.roomId
if (state.joiningRoomsIds.contains(roomId)) {
// Request already sent, should not happen
Timber.w("Try to join an already joining room. Should not happen")
return@withState
@ -201,11 +212,11 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
setState {
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) {
// 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
@ -217,8 +228,8 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
setState {
copy(
joiningRoomsIds = joiningRoomsIds.toMutableSet().apply { remove(publicRoom.roomId) },
joiningErrorRoomsIds = joiningErrorRoomsIds.toMutableSet().apply { add(publicRoom.roomId) }
joiningRoomsIds = joiningRoomsIds.toMutableSet().apply { remove(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() {
if (isFirstCreation()) {
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 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 val viewModel: CreateRoomViewModel by activityViewModel()
@ -53,7 +53,7 @@ class CreateRoomFragment @Inject constructor(private val createRoomController: C
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_create_room -> {
viewModel.doCreateRoom()
viewModel.handle(CreateRoomActions.Create)
true
}
else ->
@ -71,20 +71,20 @@ class CreateRoomFragment @Inject constructor(private val createRoomController: C
}
override fun onNameChange(newName: String) {
viewModel.setName(newName)
viewModel.handle(CreateRoomActions.SetName(newName))
}
override fun setIsPublic(isPublic: Boolean) {
viewModel.setIsPublic(isPublic)
viewModel.handle(CreateRoomActions.SetIsPublic(isPublic))
}
override fun setIsInRoomDirectory(isInRoomDirectory: Boolean) {
viewModel.setIsInRoomDirectory(isInRoomDirectory)
viewModel.handle(CreateRoomActions.SetIsInRoomDirectory(isInRoomDirectory))
}
override fun retry() {
Timber.v("Retry")
viewModel.doCreateRoom()
viewModel.handle(CreateRoomActions.Create)
}
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,
private val session: Session
) : VectorViewModel<CreateRoomViewState>(initialState) {
) : VectorViewModel<CreateRoomViewState, CreateRoomActions>(initialState) {
@AssistedInject.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) {
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.riotx.R
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.RoomDirectorySharedActionViewModel
import im.vector.riotx.features.roomdirectory.RoomDirectoryViewModel
@ -86,14 +87,14 @@ class RoomDirectoryPickerFragment @Inject constructor(val roomDirectoryPickerVie
override fun onRoomDirectoryClicked(roomDirectoryData: RoomDirectoryData) {
Timber.v("onRoomDirectoryClicked: $roomDirectoryData")
viewModel.setRoomDirectoryData(roomDirectoryData)
viewModel.handle(RoomDirectoryActions.SetRoomDirectoryData(roomDirectoryData))
actionViewModel.post(RoomDirectorySharedAction.Back)
}
override fun retry() {
Timber.v("Retry")
pickerViewModel.load()
pickerViewModel.handle(RoomDirectoryPickerActions.Retry)
}
override fun invalidate() = withState(pickerViewModel) { state ->

View file

@ -16,11 +16,7 @@
package im.vector.riotx.features.roomdirectory.picker
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
import com.airbnb.mvrx.*
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
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
class RoomDirectoryPickerViewModel @AssistedInject constructor(@Assisted initialState: RoomDirectoryPickerViewState,
private val session: Session) : VectorViewModel<RoomDirectoryPickerViewState>(initialState) {
private val session: Session)
: VectorViewModel<RoomDirectoryPickerViewState, RoomDirectoryPickerActions>(initialState) {
@AssistedInject.Factory
interface Factory {
@ -49,7 +46,7 @@ class RoomDirectoryPickerViewModel @AssistedInject constructor(@Assisted initial
load()
}
fun load() {
private fun load() {
session.getThirdPartyProtocol(object : MatrixCallback<Map<String, ThirdPartyProtocol>> {
override fun onSuccess(data: Map<String, ThirdPartyProtocol>) {
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 {
override fun onButtonClicked() {
roomPreviewViewModel.joinRoom()
roomPreviewViewModel.handle(RoomPreviewActions.Join)
}
override fun onRetryClicked() {

View file

@ -30,7 +30,8 @@ import im.vector.riotx.features.roomdirectory.JoinState
import timber.log.Timber
class RoomPreviewViewModel @AssistedInject constructor(@Assisted initialState: RoomPreviewViewState,
private val session: Session) : VectorViewModel<RoomPreviewViewState>(initialState) {
private val session: Session)
: VectorViewModel<RoomPreviewViewState, RoomPreviewActions>(initialState) {
@AssistedInject.Factory
interface Factory {
@ -76,7 +77,13 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted initialState: R
.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) {
// Request already sent, 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.riotx.core.extensions.postLiveEvent
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.platform.VectorViewModelAction
data class IgnoredUsersViewState(
val ignoredUsers: List<User> = emptyList(),
val unIgnoreRequest: Async<Unit> = Uninitialized
) : MvRxState
sealed class IgnoredUsersAction {
sealed class IgnoredUsersAction : VectorViewModelAction {
data class UnIgnore(val userId: String) : IgnoredUsersAction()
}
class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState: IgnoredUsersViewState,
private val session: Session) : VectorViewModel<IgnoredUsersViewState>(initialState) {
private val session: Session)
: VectorViewModel<IgnoredUsersViewState, IgnoredUsersAction>(initialState) {
@AssistedInject.Factory
interface Factory {
@ -66,7 +68,7 @@ class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState:
}
}
fun handle(action: IgnoredUsersAction) {
override fun handle(action: IgnoredUsersAction) {
when (action) {
is IgnoredUsersAction.UnIgnore -> handleUnIgnore(action)
}

View file

@ -16,17 +16,13 @@
package im.vector.riotx.features.settings.push
import com.airbnb.mvrx.Async
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.airbnb.mvrx.*
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.pushers.Pusher
import im.vector.matrix.rx.RxSession
import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.VectorViewModel
data class PushGatewayViewState(
@ -34,7 +30,8 @@ data class PushGatewayViewState(
) : MvRxState
class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState: PushGatewayViewState,
private val session: Session) : VectorViewModel<PushGatewayViewState>(initialState) {
private val session: Session)
: VectorViewModel<PushGatewayViewState, EmptyAction>(initialState) {
@AssistedInject.Factory
interface Factory {
@ -60,4 +57,8 @@ class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState:
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 im.vector.matrix.android.api.pushrules.rest.PushRule
import im.vector.riotx.core.di.HasScreenInjector
import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.VectorViewModel
data class PushRulesViewState(
val rules: List<PushRule> = emptyList()
) : MvRxState
class PushRulesViewModel(initialState: PushRulesViewState) : VectorViewModel<PushRulesViewState>(initialState) {
class PushRulesViewModel(initialState: PushRulesViewState)
: VectorViewModel<PushRulesViewState, EmptyAction>(initialState) {
companion object : MvRxViewModelFactory<PushRulesViewModel, PushRulesViewState> {
@ -36,4 +38,8 @@ class PushRulesViewModel(initialState: PushRulesViewState) : VectorViewModel<Pus
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 im.vector.matrix.rx.rx
import im.vector.riotx.ActiveSessionDataSource
import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.VectorViewModel
import io.reactivex.Observable
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,
private val sessionObservableStore: ActiveSessionDataSource,
private val shareRoomListObservableStore: ShareRoomListDataSource)
: VectorViewModel<IncomingShareState>(initialState) {
: VectorViewModel<IncomingShareState, EmptyAction>(initialState) {
@AssistedInject.Factory
interface Factory {
@ -70,4 +71,8 @@ class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState:
}
.disposeOnClear()
}
override fun handle(action: EmptyAction) {
// No op
}
}