diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt index 675df69f23..08ba53a024 100644 --- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt @@ -61,6 +61,7 @@ import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment import im.vector.app.features.home.room.detail.TimelineFragment import im.vector.app.features.home.room.detail.search.SearchFragment import im.vector.app.features.home.room.list.RoomListFragment +import im.vector.app.features.home.room.list.home.HomeRoomListFragment import im.vector.app.features.home.room.threads.list.views.ThreadListFragment import im.vector.app.features.location.LocationPreviewFragment import im.vector.app.features.location.LocationSharingFragment @@ -1041,4 +1042,9 @@ interface FragmentModule { @IntoMap @FragmentKey(LocationPreviewFragment::class) fun bindLocationPreviewFragment(fragment: LocationPreviewFragment): Fragment + + @Binds + @IntoMap + @FragmentKey(HomeRoomListFragment::class) + fun binHomeRoomListFragment(fragment: HomeRoomListFragment): Fragment } diff --git a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt index a5fa04f1b0..236622210f 100644 --- a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt @@ -51,6 +51,7 @@ import im.vector.app.features.home.room.detail.timeline.edithistory.ViewEditHist import im.vector.app.features.home.room.detail.timeline.reactions.ViewReactionsViewModel import im.vector.app.features.home.room.detail.upgrade.MigrateRoomViewModel import im.vector.app.features.home.room.list.RoomListViewModel +import im.vector.app.features.home.room.list.home.HomeRoomListViewModel import im.vector.app.features.homeserver.HomeServerCapabilitiesViewModel import im.vector.app.features.invite.InviteUsersToRoomViewModel import im.vector.app.features.location.LocationSharingViewModel @@ -618,4 +619,9 @@ interface MavericksViewModelModule { @IntoMap @MavericksViewModelKey(FontScaleSettingViewModel::class) fun fontScaleSettingViewModelFactory(factory: FontScaleSettingViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(HomeRoomListViewModel::class) + fun homeRoomListViewModel(factory: HomeRoomListViewModel.Factory): MavericksAssistedViewModelFactory<*, *> } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt index 828e4cc26c..f7fd268082 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt @@ -41,6 +41,7 @@ import im.vector.app.core.ui.views.CurrentCallsView import im.vector.app.core.ui.views.CurrentCallsViewPresenter import im.vector.app.core.ui.views.KeysBackupBanner import im.vector.app.databinding.FragmentHomeDetailBinding +import im.vector.app.features.VectorFeatures import im.vector.app.features.call.SharedKnownCallsViewModel import im.vector.app.features.call.VectorCallActivity import im.vector.app.features.call.dialpad.DialPadFragment @@ -48,6 +49,7 @@ import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.home.room.list.RoomListFragment import im.vector.app.features.home.room.list.RoomListParams import im.vector.app.features.home.room.list.UnreadCounterBadgeView +import im.vector.app.features.home.room.list.home.HomeRoomListFragment import im.vector.app.features.popup.PopupAlertManager import im.vector.app.features.popup.VerificationVectorAlert import im.vector.app.features.settings.VectorLocale @@ -66,7 +68,8 @@ class HomeDetailFragment @Inject constructor( private val alertManager: PopupAlertManager, private val callManager: WebRtcCallManager, private val vectorPreferences: VectorPreferences, - private val appStateHandler: AppStateHandler + private val appStateHandler: AppStateHandler, + private val vectorFeatures: VectorFeatures, ) : VectorBaseFragment(), KeysBackupBanner.Delegate, CurrentCallsView.Callback, @@ -352,8 +355,12 @@ class HomeDetailFragment @Inject constructor( if (fragmentToShow == null) { when (tab) { is HomeTab.RoomList -> { - val params = RoomListParams(tab.displayMode) - add(R.id.roomListContainer, RoomListFragment::class.java, params.toMvRxBundle(), fragmentTag) + if (vectorFeatures.isNewAppLayoutEnabled()) { + add(R.id.roomListContainer, HomeRoomListFragment::class.java, null, fragmentTag) + } else { + val params = RoomListParams(tab.displayMode) + add(R.id.roomListContainer, RoomListFragment::class.java, params.toMvRxBundle(), fragmentTag) + } } is HomeTab.DialPad -> { add(R.id.roomListContainer, createDialPadFragment(), fragmentTag) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListAction.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListAction.kt new file mode 100644 index 0000000000..04c8524b50 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListAction.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022 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.home + +import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState + +sealed class HomeRoomListAction : VectorViewModelAction { + data class SelectRoom(val roomSummary: RoomSummary) : HomeRoomListAction() + data class ChangeRoomNotificationState(val roomId: String, val notificationState: RoomNotificationState) : HomeRoomListAction() + data class ToggleTag(val roomId: String, val tag: String) : HomeRoomListAction() + data class LeaveRoom(val roomId: String) : HomeRoomListAction() +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListFragment.kt new file mode 100644 index 0000000000..f0eb027785 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListFragment.kt @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2022 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.home + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.ConcatAdapter +import androidx.recyclerview.widget.LinearLayoutManager +import com.airbnb.epoxy.EpoxyControllerAdapter +import com.airbnb.epoxy.OnModelBuildFinishedListener +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import im.vector.app.R +import im.vector.app.core.epoxy.LayoutManagerStateRestorer +import im.vector.app.core.platform.StateView +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.core.resources.UserPreferencesProvider +import im.vector.app.databinding.FragmentRoomListBinding +import im.vector.app.features.analytics.plan.ViewRoom +import im.vector.app.features.home.RoomListDisplayMode +import im.vector.app.features.home.room.list.RoomListAnimator +import im.vector.app.features.home.room.list.RoomListListener +import im.vector.app.features.home.room.list.RoomSummaryItemFactory +import im.vector.app.features.home.room.list.RoomSummaryPagedController +import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet +import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedAction +import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo +import org.matrix.android.sdk.api.session.room.model.tag.RoomTag +import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState +import javax.inject.Inject + +class HomeRoomListFragment @Inject constructor( + private val roomSummaryItemFactory: RoomSummaryItemFactory, + private val userPreferencesProvider: UserPreferencesProvider +) : VectorBaseFragment(), + RoomListListener { + + private val roomListViewModel: HomeRoomListViewModel by fragmentViewModel() + private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel + private var concatAdapter = ConcatAdapter() + private var modelBuildListener: OnModelBuildFinishedListener? = null + + private lateinit var stateRestorer: LayoutManagerStateRestorer + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomListBinding { + return FragmentRoomListBinding.inflate(inflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java) + sharedActionViewModel + .stream() + .onEach { handleQuickActions(it) } + .launchIn(viewLifecycleOwner.lifecycleScope) + + views.stateView.contentView = views.roomListView + views.stateView.state = StateView.State.Loading + + roomListViewModel.observeViewEvents { + when (it) { + is HomeRoomListViewEvents.Loading -> showLoading(it.message) + is HomeRoomListViewEvents.Failure -> showFailure(it.throwable) + is HomeRoomListViewEvents.SelectRoom -> handleSelectRoom(it, it.isInviteAlreadyAccepted) + is HomeRoomListViewEvents.Done -> Unit + } + } + + setupRecyclerView() + } + + private fun setupRecyclerView() { + val layoutManager = LinearLayoutManager(context) + stateRestorer = LayoutManagerStateRestorer(layoutManager).register() + views.roomListView.layoutManager = layoutManager + views.roomListView.itemAnimator = RoomListAnimator() + layoutManager.recycleChildrenOnDetach = true + + modelBuildListener = OnModelBuildFinishedListener { it.dispatchTo(stateRestorer) } + + roomListViewModel.sections.onEach { sections -> + setUpAdapters(sections) + }.launchIn(lifecycleScope) + + views.roomListView.adapter = concatAdapter + } + + override fun invalidate() = withState(roomListViewModel) { state -> + views.stateView.state = state.state + } + + private fun setUpAdapters(sections: Set) { + sections.forEach { + concatAdapter.addAdapter(getAdapterForData(it)) + } + } + + private fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) { + when (quickAction) { + is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> { + roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES_NOISY)) + } + is RoomListQuickActionsSharedAction.NotificationsAll -> { + roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES)) + } + is RoomListQuickActionsSharedAction.NotificationsMentionsOnly -> { + roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MENTIONS_ONLY)) + } + is RoomListQuickActionsSharedAction.NotificationsMute -> { + roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MUTE)) + } + is RoomListQuickActionsSharedAction.Settings -> { + navigator.openRoomProfile(requireActivity(), quickAction.roomId) + } + is RoomListQuickActionsSharedAction.Favorite -> { + roomListViewModel.handle(HomeRoomListAction.ToggleTag(quickAction.roomId, RoomTag.ROOM_TAG_FAVOURITE)) + } + is RoomListQuickActionsSharedAction.LowPriority -> { + roomListViewModel.handle(HomeRoomListAction.ToggleTag(quickAction.roomId, RoomTag.ROOM_TAG_LOW_PRIORITY)) + } + is RoomListQuickActionsSharedAction.Leave -> { + roomListViewModel.handle(HomeRoomListAction.LeaveRoom(quickAction.roomId)) + promptLeaveRoom(quickAction.roomId) + } + } + } + + private fun promptLeaveRoom(roomId: String) { + val isPublicRoom = roomListViewModel.isPublicRoom(roomId) + val message = buildString { + append(getString(R.string.room_participants_leave_prompt_msg)) + if (!isPublicRoom) { + append("\n\n") + append(getString(R.string.room_participants_leave_private_warning)) + } + } + MaterialAlertDialogBuilder(requireContext(), if (isPublicRoom) 0 else R.style.ThemeOverlay_Vector_MaterialAlertDialog_Destructive) + .setTitle(R.string.room_participants_leave_prompt_title) + .setMessage(message) + .setPositiveButton(R.string.action_leave) { _, _ -> + roomListViewModel.handle(HomeRoomListAction.LeaveRoom(roomId)) + } + .setNegativeButton(R.string.action_cancel, null) + .show() + } + + private fun getAdapterForData(data: HomeRoomSection): EpoxyControllerAdapter { + return when (data) { + is HomeRoomSection.RoomSummaryData -> { + RoomSummaryPagedController( + roomSummaryItemFactory, + RoomListDisplayMode.ROOMS + ).also { controller -> + controller.listener = this + data.list.observe(viewLifecycleOwner) { list -> + controller.submitList(list) + } + }.adapter + } + } + } + + private fun handleSelectRoom(event: HomeRoomListViewEvents.SelectRoom, isInviteAlreadyAccepted: Boolean) { + navigator.openRoom( + context = requireActivity(), + roomId = event.roomSummary.roomId, + isInviteAlreadyAccepted = isInviteAlreadyAccepted, + trigger = ViewRoom.Trigger.RoomList + ) + } + + // region RoomListListener + + override fun onRoomClicked(room: RoomSummary) { + roomListViewModel.handle(HomeRoomListAction.SelectRoom(room)) + } + + override fun onRoomLongClicked(room: RoomSummary): Boolean { + userPreferencesProvider.neverShowLongClickOnRoomHelpAgain() + RoomListQuickActionsBottomSheet + .newInstance(room.roomId) + .show(childFragmentManager, "ROOM_LIST_QUICK_ACTIONS") + return true + } + + override fun onRejectRoomInvitation(room: RoomSummary) { + TODO("Not yet implemented") + } + + override fun onAcceptRoomInvitation(room: RoomSummary) { + TODO("Not yet implemented") + } + + override fun onJoinSuggestedRoom(room: SpaceChildInfo) { + TODO("Not yet implemented") + } + + override fun onSuggestedRoomClicked(room: SpaceChildInfo) { + TODO("Not yet implemented") + } + + // endregion +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewEvents.kt new file mode 100644 index 0000000000..a80ae9fa93 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewEvents.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 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.home + +import im.vector.app.core.platform.VectorViewEvents +import org.matrix.android.sdk.api.session.room.model.RoomSummary + +sealed class HomeRoomListViewEvents : VectorViewEvents { + data class Loading(val message: CharSequence? = null) : HomeRoomListViewEvents() + data class Failure(val throwable: Throwable) : HomeRoomListViewEvents() + object Done : HomeRoomListViewEvents() + data class SelectRoom(val roomSummary: RoomSummary, val isInviteAlreadyAccepted: Boolean = false) : HomeRoomListViewEvents() +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt new file mode 100644 index 0000000000..3226ed24f2 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2022 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.home + +import androidx.paging.PagedList +import arrow.core.toOption +import com.airbnb.mvrx.MavericksViewModelFactory +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import im.vector.app.AppStateHandler +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory +import im.vector.app.core.platform.StateView +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.features.home.room.list.RoomListViewModel +import im.vector.app.features.settings.VectorPreferences +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.query.SpaceFilter +import org.matrix.android.sdk.api.query.toActiveSpaceOrOrphanRooms +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.getRoom +import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams +import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.model.tag.RoomTag +import org.matrix.android.sdk.api.session.room.state.isPublic + +class HomeRoomListViewModel @AssistedInject constructor( + @Assisted initialState: HomeRoomListViewState, + private val session: Session, + private val appStateHandler: AppStateHandler, + private val vectorPreferences: VectorPreferences, +) : VectorViewModel(initialState) { + + @AssistedFactory + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: HomeRoomListViewState): HomeRoomListViewModel + } + + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + + private val pagedListConfig = PagedList.Config.Builder() + .setPageSize(10) + .setInitialLoadSizeHint(20) + .setEnablePlaceholders(true) + .setPrefetchDistance(10) + .build() + + private val _sections = MutableSharedFlow>(replay = 1) + val sections = _sections.asSharedFlow() + + init { + configureSections() + } + + private fun configureSections() { + val newSections = mutableSetOf() + + newSections.add(getAllRoomsSection()) + + viewModelScope.launch { + _sections.emit(newSections) + } + + setState { + copy(state = StateView.State.Content) + } + } + + private fun getAllRoomsSection(): HomeRoomSection.RoomSummaryData { + val builder = RoomSummaryQueryParams.Builder().also { + it.memberships = listOf(Membership.JOIN) + } + + val filteredPagedRoomSummariesLive = session.roomService().getFilteredPagedRoomSummariesLive( + builder.build(), + pagedListConfig + ) + + appStateHandler.selectedSpaceFlow + .distinctUntilChanged() + .onStart { + emit(appStateHandler.getCurrentSpace().toOption()) + } + .onEach { selectedSpaceOption -> + val selectedSpace = selectedSpaceOption.orNull() + val strategy = if (!vectorPreferences.prefSpacesShowAllRoomInHome()) { + RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL + } else { + RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL + } + filteredPagedRoomSummariesLive.queryParams = filteredPagedRoomSummariesLive.queryParams.copy( + spaceFilter = getSpaceFilter(selectedSpaceId = selectedSpace?.roomId, strategy) + ) + }.launchIn(viewModelScope) + + return HomeRoomSection.RoomSummaryData( + list = filteredPagedRoomSummariesLive.livePagedList + ) + } + + private fun getSpaceFilter(selectedSpaceId: String?, strategy: RoomListViewModel.SpaceFilterStrategy): SpaceFilter? { + return when (strategy) { + RoomListViewModel.SpaceFilterStrategy.ORPHANS_IF_SPACE_NULL -> { + selectedSpaceId?.toActiveSpaceOrOrphanRooms() + } + RoomListViewModel.SpaceFilterStrategy.ALL_IF_SPACE_NULL -> { + selectedSpaceId?.let { SpaceFilter.ActiveSpace(it) } + } + RoomListViewModel.SpaceFilterStrategy.NONE -> null + } + } + + override fun handle(action: HomeRoomListAction) { + when (action) { + is HomeRoomListAction.SelectRoom -> handleSelectRoom(action) + is HomeRoomListAction.LeaveRoom -> handleLeaveRoom(action) + is HomeRoomListAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action) + is HomeRoomListAction.ToggleTag -> handleToggleTag(action) + } + } + + fun isPublicRoom(roomId: String): Boolean { + return session.getRoom(roomId)?.stateService()?.isPublic().orFalse() + } + + private fun handleSelectRoom(action: HomeRoomListAction.SelectRoom) = withState { + _viewEvents.post(HomeRoomListViewEvents.SelectRoom(action.roomSummary, false)) + } + + private fun handleLeaveRoom(action: HomeRoomListAction.LeaveRoom) { + _viewEvents.post(HomeRoomListViewEvents.Loading(null)) + viewModelScope.launch { + val value = runCatching { session.roomService().leaveRoom(action.roomId) } + .fold({ HomeRoomListViewEvents.Done }, { HomeRoomListViewEvents.Failure(it) }) + _viewEvents.post(value) + } + } + + private fun handleChangeNotificationMode(action: HomeRoomListAction.ChangeRoomNotificationState) { + val room = session.getRoom(action.roomId) + if (room != null) { + viewModelScope.launch { + try { + room.roomPushRuleService().setRoomNotificationState(action.notificationState) + } catch (failure: Exception) { + _viewEvents.post(HomeRoomListViewEvents.Failure(failure)) + } + } + } + } + + private fun handleToggleTag(action: HomeRoomListAction.ToggleTag) { + session.getRoom(action.roomId)?.let { room -> + viewModelScope.launch(Dispatchers.IO) { + try { + if (room.roomSummary()?.hasTag(action.tag) == false) { + // Favorite and low priority tags are exclusive, so maybe delete the other tag first + action.tag.otherTag() + ?.takeIf { room.roomSummary()?.hasTag(it).orFalse() } + ?.let { tagToRemove -> + room.tagsService().deleteTag(tagToRemove) + } + + // Set the tag. We do not handle the order for the moment + room.tagsService().addTag(action.tag, 0.5) + } else { + room.tagsService().deleteTag(action.tag) + } + } catch (failure: Throwable) { + _viewEvents.post(HomeRoomListViewEvents.Failure(failure)) + } + } + } + } + + private fun String.otherTag(): String? { + return when (this) { + RoomTag.ROOM_TAG_FAVOURITE -> RoomTag.ROOM_TAG_LOW_PRIORITY + RoomTag.ROOM_TAG_LOW_PRIORITY -> RoomTag.ROOM_TAG_FAVOURITE + else -> null + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewState.kt new file mode 100644 index 0000000000..bfcaea22e9 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewState.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 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.home + +import com.airbnb.mvrx.MavericksState +import im.vector.app.core.platform.StateView + +data class HomeRoomListViewState( + val state: StateView.State = StateView.State.Loading +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomSection.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomSection.kt new file mode 100644 index 0000000000..7bfd0a769e --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomSection.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 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.home + +import androidx.lifecycle.LiveData +import androidx.paging.PagedList +import org.matrix.android.sdk.api.session.room.model.RoomSummary + +sealed class HomeRoomSection { + data class RoomSummaryData( + val list: LiveData> + ) : HomeRoomSection() +}