Close Activity when room is left.

Create a dedicated ViewModel to handle that in Activity rather than multiple times in Fragments
This commit is contained in:
Benoit Marty 2020-06-11 21:40:22 +02:00
parent 530ce0952c
commit 4f5b1d9646
9 changed files with 267 additions and 21 deletions

View File

@ -32,6 +32,7 @@ import im.vector.riotx.features.crypto.verification.VerificationBottomSheet
import im.vector.riotx.features.debug.DebugMenuActivity
import im.vector.riotx.features.home.HomeActivity
import im.vector.riotx.features.home.HomeModule
import im.vector.riotx.features.home.room.detail.RoomDetailActivity
import im.vector.riotx.features.home.room.detail.readreceipts.DisplayReadReceiptsBottomSheet
import im.vector.riotx.features.home.room.detail.timeline.action.MessageActionsBottomSheet
import im.vector.riotx.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet
@ -57,7 +58,9 @@ import im.vector.riotx.features.reactions.EmojiReactionPickerActivity
import im.vector.riotx.features.reactions.widget.ReactionButton
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomActivity
import im.vector.riotx.features.roommemberprofile.RoomMemberProfileActivity
import im.vector.riotx.features.roommemberprofile.devices.DeviceListBottomSheet
import im.vector.riotx.features.roomprofile.RoomProfileActivity
import im.vector.riotx.features.settings.VectorSettingsActivity
import im.vector.riotx.features.settings.devices.DeviceVerificationInfoBottomSheet
import im.vector.riotx.features.share.IncomingShareActivity
@ -101,6 +104,9 @@ interface ScreenComponent {
* ========================================================================================== */
fun inject(activity: HomeActivity)
fun inject(activity: RoomDetailActivity)
fun inject(activity: RoomProfileActivity)
fun inject(activity: RoomMemberProfileActivity)
fun inject(activity: VectorSettingsActivity)
fun inject(activity: KeysBackupManageActivity)
fun inject(activity: EmojiReactionPickerActivity)

View File

@ -22,20 +22,43 @@ import android.os.Bundle
import androidx.appcompat.widget.Toolbar
import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout
import com.airbnb.mvrx.viewModel
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.extensions.hideKeyboard
import im.vector.riotx.core.extensions.replaceFragment
import im.vector.riotx.core.platform.ToolbarConfigurable
import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.features.home.room.breadcrumbs.BreadcrumbsFragment
import im.vector.riotx.features.room.RequireActiveMembershipAction
import im.vector.riotx.features.room.RequireActiveMembershipViewModel
import im.vector.riotx.features.room.RequireActiveMembershipViewState
import kotlinx.android.synthetic.main.activity_room_detail.*
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
import javax.inject.Inject
class RoomDetailActivity : VectorBaseActivity(), ToolbarConfigurable {
class RoomDetailActivity :
VectorBaseActivity(),
ToolbarConfigurable,
RequireActiveMembershipViewModel.Factory {
override fun getLayoutRes() = R.layout.activity_room_detail
private lateinit var sharedActionViewModel: RoomDetailSharedActionViewModel
private val requireActiveMembershipViewModel: RequireActiveMembershipViewModel by viewModel()
@Inject
lateinit var requireActiveMembershipViewModelFactory: RequireActiveMembershipViewModel.Factory
override fun create(initialState: RequireActiveMembershipViewState): RequireActiveMembershipViewModel {
// Due to shortcut, we cannot use MvRx args. Pass the first roomId here
return requireActiveMembershipViewModelFactory.create(initialState.copy(roomId = currentRoomId ?: ""))
}
override fun injectWith(injector: ScreenComponent) {
super.injectWith(injector)
injector.inject(this)
}
// Simple filter
private var currentRoomId: String? = null
@ -68,6 +91,8 @@ class RoomDetailActivity : VectorBaseActivity(), ToolbarConfigurable {
}
.disposeOnDestroy()
requireActiveMembershipViewModel.observeViewEvents { finish() }
drawerLayout.addDrawerListener(drawerListener)
}
@ -76,6 +101,7 @@ class RoomDetailActivity : VectorBaseActivity(), ToolbarConfigurable {
// Do not replace the Fragment if it's the same roomId
if (currentRoomId != switchToRoom.roomId) {
currentRoomId = switchToRoom.roomId
requireActiveMembershipViewModel.handle(RequireActiveMembershipAction.ChangeRoom(switchToRoom.roomId))
replaceFragment(R.id.roomDetailContainer, RoomDetailFragment::class.java, RoomDetailArgs(switchToRoom.roomId))
}
}
@ -125,6 +151,7 @@ class RoomDetailActivity : VectorBaseActivity(), ToolbarConfigurable {
}
}
// Shortcuts can't have intents with parcelables
fun shortcutIntent(context: Context, roomId: String): Intent {
return Intent(context, RoomDetailActivity::class.java).apply {
action = ACTION_ROOM_DETAILS_FROM_SHORTCUT

View File

@ -753,15 +753,10 @@ class RoomDetailFragment @Inject constructor(
private fun renderRoomSummary(state: RoomDetailViewState) {
state.asyncRoomSummary()?.let { roomSummary ->
if (roomSummary.membership.isLeft()) {
Timber.w("The room has been left")
activity?.finish()
} else {
roomToolbarTitleView.text = roomSummary.displayName
avatarRenderer.render(roomSummary.toMatrixItem(), roomToolbarAvatarImageView)
roomToolbarTitleView.text = roomSummary.displayName
avatarRenderer.render(roomSummary.toMatrixItem(), roomToolbarAvatarImageView)
renderSubTitle(state.typingMessage, roomSummary.topic)
}
renderSubTitle(state.typingMessage, roomSummary.topic)
jumpToBottomView.count = roomSummary.notificationCount
jumpToBottomView.drawBadge = roomSummary.hasUnreadMessages

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2020 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.room
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class RequireActiveMembershipAction : VectorViewModelAction {
data class ChangeRoom(val roomId: String) : RequireActiveMembershipAction()
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2020 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.room
import im.vector.riotx.core.platform.VectorViewEvents
sealed class RequireActiveMembershipViewEvents : VectorViewEvents {
object RoomLeft : RequireActiveMembershipViewEvents()
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2020 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.room
import com.airbnb.mvrx.ActivityViewModelContext
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.extensions.exhaustive
import im.vector.riotx.core.platform.VectorViewModel
import io.reactivex.disposables.Disposable
import timber.log.Timber
/**
* This ViewModel observe a room summary and notify when the room is left
*/
class RequireActiveMembershipViewModel @AssistedInject constructor(
@Assisted initialState: RequireActiveMembershipViewState,
private val session: Session)
: VectorViewModel<RequireActiveMembershipViewState, RequireActiveMembershipAction, RequireActiveMembershipViewEvents>(initialState) {
@AssistedInject.Factory
interface Factory {
fun create(initialState: RequireActiveMembershipViewState): RequireActiveMembershipViewModel
}
companion object : MvRxViewModelFactory<RequireActiveMembershipViewModel, RequireActiveMembershipViewState> {
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: RequireActiveMembershipViewState): RequireActiveMembershipViewModel? {
val factory = when (viewModelContext) {
is FragmentViewModelContext -> viewModelContext.fragment as? Factory
is ActivityViewModelContext -> viewModelContext.activity as? Factory
}
return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface")
}
}
private var currentDisposable: Disposable? = null
init {
observeRoomSummary(initialState.roomId)
}
private fun observeRoomSummary(roomId: String?) {
currentDisposable?.dispose()
currentDisposable = roomId
?.let { session.getRoom(it) }
?.let { room ->
room.rx().liveRoomSummary()
.unwrap()
.subscribe {
if (it.membership.isLeft()) {
Timber.w("The room has been left")
_viewEvents.post(RequireActiveMembershipViewEvents.RoomLeft)
}
}
}
}
override fun onCleared() {
super.onCleared()
currentDisposable?.dispose()
}
override fun handle(action: RequireActiveMembershipAction) {
when (action) {
is RequireActiveMembershipAction.ChangeRoom -> {
setState {
copy(roomId = action.roomId)
}
observeRoomSummary(action.roomId)
}
}.exhaustive
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2020 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.room
import com.airbnb.mvrx.MvRxState
import im.vector.riotx.features.roommemberprofile.RoomMemberProfileArgs
import im.vector.riotx.features.roomprofile.RoomProfileArgs
data class RequireActiveMembershipViewState(
val roomId: String? = null
) : MvRxState {
// No constructor for RoomDetailArgs because of intent for Shortcut
constructor(args: RoomProfileArgs) : this(roomId = args.roomId)
constructor(args: RoomMemberProfileArgs) : this(roomId = args.roomId)
}

View File

@ -20,32 +20,53 @@ package im.vector.riotx.features.roommemberprofile
import android.content.Context
import android.content.Intent
import androidx.appcompat.widget.Toolbar
import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.viewModel
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.extensions.addFragment
import im.vector.riotx.core.platform.ToolbarConfigurable
import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.features.room.RequireActiveMembershipViewModel
import im.vector.riotx.features.room.RequireActiveMembershipViewState
import javax.inject.Inject
class RoomMemberProfileActivity : VectorBaseActivity(), ToolbarConfigurable {
class RoomMemberProfileActivity :
VectorBaseActivity(),
ToolbarConfigurable,
RequireActiveMembershipViewModel.Factory {
companion object {
private const val EXTRA_FRAGMENT_ARGS = "EXTRA_FRAGMENT_ARGS"
fun newIntent(context: Context, args: RoomMemberProfileArgs): Intent {
return Intent(context, RoomMemberProfileActivity::class.java).apply {
putExtra(EXTRA_FRAGMENT_ARGS, args)
putExtra(MvRx.KEY_ARG, args)
}
}
}
private val requireActiveMembershipViewModel: RequireActiveMembershipViewModel by viewModel()
@Inject
lateinit var requireActiveMembershipViewModelFactory: RequireActiveMembershipViewModel.Factory
override fun create(initialState: RequireActiveMembershipViewState): RequireActiveMembershipViewModel { // Due to shortcut, we cannot use MvRx args. Pass the roomId here
return requireActiveMembershipViewModelFactory.create(initialState)
}
override fun injectWith(injector: ScreenComponent) {
super.injectWith(injector)
injector.inject(this)
}
override fun getLayoutRes() = R.layout.activity_simple
override fun initUiAndData() {
if (isFirstCreation()) {
val fragmentArgs: RoomMemberProfileArgs = intent?.extras?.getParcelable(EXTRA_FRAGMENT_ARGS)
?: return
val fragmentArgs: RoomMemberProfileArgs = intent?.extras?.getParcelable(MvRx.KEY_ARG) ?: return
addFragment(R.id.simpleFragmentContainer, RoomMemberProfileFragment::class.java, fragmentArgs)
}
requireActiveMembershipViewModel.observeViewEvents { finish() }
}
override fun configure(toolbar: Toolbar) {

View File

@ -20,25 +20,32 @@ package im.vector.riotx.features.roomprofile
import android.content.Context
import android.content.Intent
import androidx.appcompat.widget.Toolbar
import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.viewModel
import im.vector.riotx.R
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.ToolbarConfigurable
import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.features.room.RequireActiveMembershipViewModel
import im.vector.riotx.features.room.RequireActiveMembershipViewState
import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment
import im.vector.riotx.features.roomprofile.settings.RoomSettingsFragment
import im.vector.riotx.features.roomprofile.uploads.RoomUploadsFragment
import javax.inject.Inject
class RoomProfileActivity : VectorBaseActivity(), ToolbarConfigurable {
class RoomProfileActivity :
VectorBaseActivity(),
ToolbarConfigurable,
RequireActiveMembershipViewModel.Factory {
companion object {
private const val EXTRA_ROOM_PROFILE_ARGS = "EXTRA_ROOM_PROFILE_ARGS"
fun newIntent(context: Context, roomId: String): Intent {
val roomProfileArgs = RoomProfileArgs(roomId)
return Intent(context, RoomProfileActivity::class.java).apply {
putExtra(EXTRA_ROOM_PROFILE_ARGS, roomProfileArgs)
putExtra(MvRx.KEY_ARG, roomProfileArgs)
}
}
}
@ -46,11 +53,25 @@ class RoomProfileActivity : VectorBaseActivity(), ToolbarConfigurable {
private lateinit var sharedActionViewModel: RoomProfileSharedActionViewModel
private lateinit var roomProfileArgs: RoomProfileArgs
private val requireActiveMembershipViewModel: RequireActiveMembershipViewModel by viewModel()
@Inject
lateinit var requireActiveMembershipViewModelFactory: RequireActiveMembershipViewModel.Factory
override fun create(initialState: RequireActiveMembershipViewState): RequireActiveMembershipViewModel {
return requireActiveMembershipViewModelFactory.create(initialState)
}
override fun injectWith(injector: ScreenComponent) {
super.injectWith(injector)
injector.inject(this)
}
override fun getLayoutRes() = R.layout.activity_simple
override fun initUiAndData() {
sharedActionViewModel = viewModelProvider.get(RoomProfileSharedActionViewModel::class.java)
roomProfileArgs = intent?.extras?.getParcelable(EXTRA_ROOM_PROFILE_ARGS) ?: return
roomProfileArgs = intent?.extras?.getParcelable(MvRx.KEY_ARG) ?: return
if (isFirstCreation()) {
addFragment(R.id.simpleFragmentContainer, RoomProfileFragment::class.java, roomProfileArgs)
}
@ -64,6 +85,8 @@ class RoomProfileActivity : VectorBaseActivity(), ToolbarConfigurable {
}
}
.disposeOnDestroy()
requireActiveMembershipViewModel.observeViewEvents { finish() }
}
private fun openRoomUploads() {