From a76a936e21db572737101d9fb5edd927bdc70476 Mon Sep 17 00:00:00 2001 From: David Langley Date: Mon, 26 Jul 2021 22:54:32 +0100 Subject: [PATCH] implement bottom sheet and error handling --- .../home/room/list/RoomListFragment.kt | 12 ++- .../RoomListQuickActionsBottomSheet.kt | 36 ++++++++- .../RoomListQuickActionsEpoxyController.kt | 65 ++++++++++++---- .../RoomListQuickActionsSharedAction.kt | 7 +- .../list/actions/RoomListQuickActionsState.kt | 33 -------- .../actions/RoomListQuickActionsViewModel.kt | 78 ------------------- .../roomprofile/RoomProfileFragment.kt | 1 - .../RoomNotificationSettingsViewModel.kt | 11 ++- .../RoomNotificationSettingsViewState.kt | 2 + 9 files changed, 108 insertions(+), 137 deletions(-) delete mode 100644 vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsState.kt delete mode 100644 vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsViewModel.kt diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt index fdfe171439..b053b5b825 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt @@ -473,9 +473,17 @@ class RoomListFragment @Inject constructor( // refresh footer footerController.setData(it) } - RoomListQuickActionsBottomSheet + val bottomSheet = RoomListQuickActionsBottomSheet .newInstance(room.roomId, RoomListActionsArgs.Mode.FULL) - .show(childFragmentManager, "ROOM_LIST_QUICK_ACTIONS") + bottomSheet.listener = object : RoomListQuickActionsBottomSheet.Listener { + override fun handleFailure(throwable: Throwable) { + showFailure(throwable) + } + } + + bottomSheet.show(childFragmentManager, "ROOM_LIST_QUICK_ACTIONS") + + return true } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt index 94f9aaf496..463e5ad94a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt @@ -22,6 +22,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView +import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.app.core.di.ScreenComponent @@ -30,7 +31,12 @@ import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.databinding.BottomSheetGenericListBinding import im.vector.app.features.navigation.Navigator +import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsAction +import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsViewEvents +import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsViewModel +import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsViewState import kotlinx.parcelize.Parcelize +import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState import javax.inject.Inject @Parcelize @@ -45,6 +51,11 @@ data class RoomListActionsArgs( } } +data class RoomListQuickActionViewState( + val roomListActionsArgs: RoomListActionsArgs, + val notificationSettingsViewState: RoomNotificationSettingsViewState +) + /** * Bottom sheet fragment that shows room information with list of contextual actions */ @@ -52,15 +63,20 @@ class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), RoomListQuickActionsEpoxyController.Listener { + interface Listener { + fun handleFailure(throwable: Throwable) + } private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel @Inject lateinit var sharedViewPool: RecyclerView.RecycledViewPool - @Inject lateinit var roomListActionsViewModelFactory: RoomListQuickActionsViewModel.Factory + @Inject lateinit var roomNotificationSettingsViewModelFactory: RoomNotificationSettingsViewModel.Factory @Inject lateinit var roomListActionsEpoxyController: RoomListQuickActionsEpoxyController @Inject lateinit var navigator: Navigator - private val viewModel: RoomListQuickActionsViewModel by fragmentViewModel(RoomListQuickActionsViewModel::class) + private val roomListActionsArgs: RoomListActionsArgs by args() + private val viewModel: RoomNotificationSettingsViewModel by fragmentViewModel(RoomNotificationSettingsViewModel::class) override val showExpanded = true + var listener: Listener? = null override fun injectWith(injector: ScreenComponent) { injector.inject(this) @@ -80,6 +96,12 @@ class RoomListQuickActionsBottomSheet : disableItemAnimation = true ) roomListActionsEpoxyController.listener = this + + viewModel.observeViewEvents { + when(it){ + is RoomNotificationSettingsViewEvents.Failure -> listener?.handleFailure(it.throwable) + } + } } override fun onDestroyView() { @@ -89,7 +111,11 @@ class RoomListQuickActionsBottomSheet : } override fun invalidate() = withState(viewModel) { - roomListActionsEpoxyController.setData(it) + val roomListViewState = RoomListQuickActionViewState( + roomListActionsArgs, + it + ) + roomListActionsEpoxyController.setData(roomListViewState) super.invalidate() } @@ -103,6 +129,10 @@ class RoomListQuickActionsBottomSheet : } } + override fun didSelectRoomNotificationState(roomNotificationState: RoomNotificationState) { + viewModel.handle(RoomNotificationSettingsAction.SelectNotificationState(roomNotificationState)) + } + companion object { fun newInstance(roomId: String, mode: RoomListActionsArgs.Mode): RoomListQuickActionsBottomSheet { return RoomListQuickActionsBottomSheet().apply { diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt index 4604159338..0bca6639a0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt @@ -15,13 +15,18 @@ */ package im.vector.app.features.home.room.list.actions +import androidx.annotation.StringRes import com.airbnb.epoxy.TypedEpoxyController +import im.vector.app.R import im.vector.app.core.epoxy.bottomSheetDividerItem import im.vector.app.core.epoxy.bottomsheet.bottomSheetActionItem import im.vector.app.core.epoxy.bottomsheet.bottomSheetRoomPreviewItem +import im.vector.app.core.epoxy.profiles.notifications.radioButtonItem import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.roomprofile.notifications.notificationOptions +import im.vector.app.features.roomprofile.notifications.notificationStateMapped import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject @@ -33,16 +38,26 @@ class RoomListQuickActionsEpoxyController @Inject constructor( private val avatarRenderer: AvatarRenderer, private val colorProvider: ColorProvider, private val stringProvider: StringProvider -) : TypedEpoxyController() { +) : TypedEpoxyController() { var listener: Listener? = null - override fun buildModels(state: RoomListQuickActionsState) { - val roomSummary = state.roomSummary() ?: return - val host = this - val showAll = state.mode == RoomListActionsArgs.Mode.FULL + @StringRes + private fun titleForNotificationState(notificationState: RoomNotificationState): Int? = when(notificationState) { + RoomNotificationState.ALL_MESSAGES_NOISY -> R.string.room_settings_all_messages + RoomNotificationState.MENTIONS_ONLY -> R.string.room_settings_mention_and_keyword_only + RoomNotificationState.MUTE -> R.string.room_settings_none + else -> null + } - if (showAll) { + override fun buildModels(state: RoomListQuickActionViewState) { + val notificationViewState = state.notificationSettingsViewState + val roomSummary = notificationViewState.roomSummary() ?: return + val host = this + val showFull = state.roomListActionsArgs.mode == RoomListActionsArgs.Mode.FULL + var isV2 = true + + if (showFull || isV2) { // Preview, favorite, settings bottomSheetRoomPreviewItem { id("room_preview") @@ -63,15 +78,30 @@ class RoomListQuickActionsEpoxyController @Inject constructor( } } - val selectedRoomState = state.roomNotificationState() - RoomListQuickActionsSharedAction.NotificationsAllNoisy(roomSummary.roomId).toBottomSheetItem(0, selectedRoomState) - RoomListQuickActionsSharedAction.NotificationsAll(roomSummary.roomId).toBottomSheetItem(1, selectedRoomState) - RoomListQuickActionsSharedAction.NotificationsMentionsOnly(roomSummary.roomId).toBottomSheetItem(2, selectedRoomState) - RoomListQuickActionsSharedAction.NotificationsMute(roomSummary.roomId).toBottomSheetItem(3, selectedRoomState) - - if (showAll) { - RoomListQuickActionsSharedAction.Leave(roomSummary.roomId).toBottomSheetItem(5) + if (isV2) { + notificationViewState.notificationOptions.forEach { notificationState -> + val title = titleForNotificationState(notificationState) + radioButtonItem { + id(notificationState.name) + titleRes(title) + selected(notificationViewState.notificationStateMapped() == notificationState) + listener { + host.listener?.didSelectRoomNotificationState(notificationState) + } + } + } + } else { + val selectedRoomState = notificationViewState.notificationState() + RoomListQuickActionsSharedAction.NotificationsAllNoisy(roomSummary.roomId).toBottomSheetItem(0, selectedRoomState) + RoomListQuickActionsSharedAction.NotificationsAll(roomSummary.roomId).toBottomSheetItem(1, selectedRoomState) + RoomListQuickActionsSharedAction.NotificationsMentionsOnly(roomSummary.roomId).toBottomSheetItem(2, selectedRoomState) + RoomListQuickActionsSharedAction.NotificationsMute(roomSummary.roomId).toBottomSheetItem(3, selectedRoomState) } + + if (showFull || isV2) { + RoomListQuickActionsSharedAction.Leave(roomSummary.roomId, showIcon = !isV2).toBottomSheetItem(5) + } + } private fun RoomListQuickActionsSharedAction.toBottomSheetItem(index: Int, roomNotificationState: RoomNotificationState? = null) { @@ -86,7 +116,11 @@ class RoomListQuickActionsEpoxyController @Inject constructor( return bottomSheetActionItem { id("action_$index") selected(selected) - iconRes(iconResId) + if(iconResId != null){ + iconRes(iconResId) + } else{ + showIcon(false) + } textRes(titleRes) destructive(this@toBottomSheetItem.destructive) listener { host.listener?.didSelectMenuAction(this@toBottomSheetItem) } @@ -95,5 +129,6 @@ class RoomListQuickActionsEpoxyController @Inject constructor( interface Listener { fun didSelectMenuAction(quickAction: RoomListQuickActionsSharedAction) + fun didSelectRoomNotificationState(roomNotificationState: RoomNotificationState) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsSharedAction.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsSharedAction.kt index 075dca0c52..20c3087f54 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsSharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsSharedAction.kt @@ -21,9 +21,10 @@ import androidx.annotation.StringRes import im.vector.app.R import im.vector.app.core.platform.VectorSharedAction + sealed class RoomListQuickActionsSharedAction( @StringRes val titleRes: Int, - @DrawableRes val iconResId: Int, + @DrawableRes val iconResId: Int?, val destructive: Boolean = false) : VectorSharedAction { @@ -60,9 +61,9 @@ sealed class RoomListQuickActionsSharedAction( R.string.room_list_quick_actions_favorite_add, R.drawable.ic_star_24dp) - data class Leave(val roomId: String) : RoomListQuickActionsSharedAction( + data class Leave(val roomId: String, val showIcon: Boolean=true) : RoomListQuickActionsSharedAction( R.string.room_list_quick_actions_leave, - R.drawable.ic_room_actions_leave, + if (showIcon) R.drawable.ic_room_actions_leave else null, true ) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsState.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsState.kt deleted file mode 100644 index 2731620cec..0000000000 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsState.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.app.features.home.room.list.actions - -import com.airbnb.mvrx.Async -import com.airbnb.mvrx.MvRxState -import com.airbnb.mvrx.Uninitialized -import org.matrix.android.sdk.api.session.room.model.RoomSummary -import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState - -data class RoomListQuickActionsState( - val roomId: String, - val mode: RoomListActionsArgs.Mode, - val roomSummary: Async = Uninitialized, - val roomNotificationState: Async = Uninitialized -) : MvRxState { - - constructor(args: RoomListActionsArgs) : this(roomId = args.roomId, mode = args.mode) -} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsViewModel.kt deleted file mode 100644 index 75e9459d2c..0000000000 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsViewModel.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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.app.features.home.room.list.actions - -import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory -import com.airbnb.mvrx.ViewModelContext -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -import dagger.assisted.AssistedFactory -import im.vector.app.core.platform.EmptyAction -import im.vector.app.core.platform.EmptyViewEvents -import im.vector.app.core.platform.VectorViewModel -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.rx.rx -import org.matrix.android.sdk.rx.unwrap - -class RoomListQuickActionsViewModel @AssistedInject constructor(@Assisted initialState: RoomListQuickActionsState, - session: Session -) : VectorViewModel(initialState) { - - @AssistedFactory - interface Factory { - fun create(initialState: RoomListQuickActionsState): RoomListQuickActionsViewModel - } - - companion object : MvRxViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomListQuickActionsState): RoomListQuickActionsViewModel? { - val fragment: RoomListQuickActionsBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.roomListActionsViewModelFactory.create(state) - } - } - - private val room = session.getRoom(initialState.roomId)!! - - init { - observeRoomSummary() - observeNotificationState() - } - - private fun observeNotificationState() { - room - .rx() - .liveNotificationState() - .execute { - copy(roomNotificationState = it) - } - } - - private fun observeRoomSummary() { - room - .rx() - .liveRoomSummary() - .unwrap() - .execute { - copy(roomSummary = it) - } - } - - override fun handle(action: EmptyAction) { - // No op - } -} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt index 69e05f372d..7030e96c6f 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt @@ -30,7 +30,6 @@ import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder -import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.animations.AppBarStateChangeListener import im.vector.app.core.animations.MatrixItemAppBarStateChangeListener diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt index ad71343c1f..5d23931d88 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt @@ -25,6 +25,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.platform.VectorViewModel +import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.rx.rx @@ -44,8 +45,14 @@ class RoomNotificationSettingsViewModel @AssistedInject constructor( @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomNotificationSettingsViewState): RoomNotificationSettingsViewModel { - val fragment: RoomNotificationSettingsFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.viewModelFactory.create(state) + val fragmentModelContext = (viewModelContext as FragmentViewModelContext) + return if (fragmentModelContext.fragment is RoomNotificationSettingsFragment) { + val fragment: RoomNotificationSettingsFragment = fragmentModelContext.fragment() + fragment.viewModelFactory.create(state) + } else { + val fragment: RoomListQuickActionsBottomSheet = fragmentModelContext.fragment() + fragment.roomNotificationSettingsViewModelFactory.create(state) + } } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewState.kt index bff1a26595..a88dc84078 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewState.kt @@ -20,6 +20,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized +import im.vector.app.features.home.room.list.actions.RoomListActionsArgs import im.vector.app.features.roomprofile.RoomProfileArgs import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState @@ -32,6 +33,7 @@ data class RoomNotificationSettingsViewState( val notificationState: Async = Uninitialized, ) : MvRxState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) + constructor(args: RoomListActionsArgs) : this(roomId = args.roomId) } data class AvatarData (