diff --git a/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt b/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt index c5dc67e38d..f8ed0b01c4 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt @@ -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) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActivity.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActivity.kt index e11ba981a6..c4770245e7 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActivity.kt @@ -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 diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index 97f8469357..48b9106f4d 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -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 diff --git a/vector/src/main/java/im/vector/riotx/features/room/RequireActiveMembershipAction.kt b/vector/src/main/java/im/vector/riotx/features/room/RequireActiveMembershipAction.kt new file mode 100644 index 0000000000..b3a1fa6976 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/room/RequireActiveMembershipAction.kt @@ -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() +} diff --git a/vector/src/main/java/im/vector/riotx/features/room/RequireActiveMembershipViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/room/RequireActiveMembershipViewEvents.kt new file mode 100644 index 0000000000..2415c8c46c --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/room/RequireActiveMembershipViewEvents.kt @@ -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() +} diff --git a/vector/src/main/java/im/vector/riotx/features/room/RequireActiveMembershipViewModel.kt b/vector/src/main/java/im/vector/riotx/features/room/RequireActiveMembershipViewModel.kt new file mode 100644 index 0000000000..9555aad92f --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/room/RequireActiveMembershipViewModel.kt @@ -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(initialState) { + + @AssistedInject.Factory + interface Factory { + fun create(initialState: RequireActiveMembershipViewState): RequireActiveMembershipViewModel + } + + companion object : MvRxViewModelFactory { + + @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 + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/room/RequireActiveMembershipViewState.kt b/vector/src/main/java/im/vector/riotx/features/room/RequireActiveMembershipViewState.kt new file mode 100644 index 0000000000..ccf0270efc --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/room/RequireActiveMembershipViewState.kt @@ -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) +} diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileActivity.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileActivity.kt index 25efecf541..e802a3eaa4 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileActivity.kt @@ -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) { diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt index bfc815f1ed..c9b0974f2b 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt @@ -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() {