new room list implementation for EditLayout (#6618)
This commit is contained in:
parent
f37034c18e
commit
45bbf74db3
@ -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.TimelineFragment
|
||||||
import im.vector.app.features.home.room.detail.search.SearchFragment
|
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.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.home.room.threads.list.views.ThreadListFragment
|
||||||
import im.vector.app.features.location.LocationPreviewFragment
|
import im.vector.app.features.location.LocationPreviewFragment
|
||||||
import im.vector.app.features.location.LocationSharingFragment
|
import im.vector.app.features.location.LocationSharingFragment
|
||||||
@ -1041,4 +1042,9 @@ interface FragmentModule {
|
|||||||
@IntoMap
|
@IntoMap
|
||||||
@FragmentKey(LocationPreviewFragment::class)
|
@FragmentKey(LocationPreviewFragment::class)
|
||||||
fun bindLocationPreviewFragment(fragment: LocationPreviewFragment): Fragment
|
fun bindLocationPreviewFragment(fragment: LocationPreviewFragment): Fragment
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FragmentKey(HomeRoomListFragment::class)
|
||||||
|
fun binHomeRoomListFragment(fragment: HomeRoomListFragment): Fragment
|
||||||
}
|
}
|
||||||
|
@ -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.timeline.reactions.ViewReactionsViewModel
|
||||||
import im.vector.app.features.home.room.detail.upgrade.MigrateRoomViewModel
|
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.RoomListViewModel
|
||||||
|
import im.vector.app.features.home.room.list.home.HomeRoomListViewModel
|
||||||
import im.vector.app.features.homeserver.HomeServerCapabilitiesViewModel
|
import im.vector.app.features.homeserver.HomeServerCapabilitiesViewModel
|
||||||
import im.vector.app.features.invite.InviteUsersToRoomViewModel
|
import im.vector.app.features.invite.InviteUsersToRoomViewModel
|
||||||
import im.vector.app.features.location.LocationSharingViewModel
|
import im.vector.app.features.location.LocationSharingViewModel
|
||||||
@ -618,4 +619,9 @@ interface MavericksViewModelModule {
|
|||||||
@IntoMap
|
@IntoMap
|
||||||
@MavericksViewModelKey(FontScaleSettingViewModel::class)
|
@MavericksViewModelKey(FontScaleSettingViewModel::class)
|
||||||
fun fontScaleSettingViewModelFactory(factory: FontScaleSettingViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
fun fontScaleSettingViewModelFactory(factory: FontScaleSettingViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@MavericksViewModelKey(HomeRoomListViewModel::class)
|
||||||
|
fun homeRoomListViewModel(factory: HomeRoomListViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||||
}
|
}
|
||||||
|
@ -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.CurrentCallsViewPresenter
|
||||||
import im.vector.app.core.ui.views.KeysBackupBanner
|
import im.vector.app.core.ui.views.KeysBackupBanner
|
||||||
import im.vector.app.databinding.FragmentHomeDetailBinding
|
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.SharedKnownCallsViewModel
|
||||||
import im.vector.app.features.call.VectorCallActivity
|
import im.vector.app.features.call.VectorCallActivity
|
||||||
import im.vector.app.features.call.dialpad.DialPadFragment
|
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.RoomListFragment
|
||||||
import im.vector.app.features.home.room.list.RoomListParams
|
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.UnreadCounterBadgeView
|
||||||
|
import im.vector.app.features.home.room.list.home.HomeRoomListFragment
|
||||||
import im.vector.app.features.popup.PopupAlertManager
|
import im.vector.app.features.popup.PopupAlertManager
|
||||||
import im.vector.app.features.popup.VerificationVectorAlert
|
import im.vector.app.features.popup.VerificationVectorAlert
|
||||||
import im.vector.app.features.settings.VectorLocale
|
import im.vector.app.features.settings.VectorLocale
|
||||||
@ -66,7 +68,8 @@ class HomeDetailFragment @Inject constructor(
|
|||||||
private val alertManager: PopupAlertManager,
|
private val alertManager: PopupAlertManager,
|
||||||
private val callManager: WebRtcCallManager,
|
private val callManager: WebRtcCallManager,
|
||||||
private val vectorPreferences: VectorPreferences,
|
private val vectorPreferences: VectorPreferences,
|
||||||
private val appStateHandler: AppStateHandler
|
private val appStateHandler: AppStateHandler,
|
||||||
|
private val vectorFeatures: VectorFeatures,
|
||||||
) : VectorBaseFragment<FragmentHomeDetailBinding>(),
|
) : VectorBaseFragment<FragmentHomeDetailBinding>(),
|
||||||
KeysBackupBanner.Delegate,
|
KeysBackupBanner.Delegate,
|
||||||
CurrentCallsView.Callback,
|
CurrentCallsView.Callback,
|
||||||
@ -352,8 +355,12 @@ class HomeDetailFragment @Inject constructor(
|
|||||||
if (fragmentToShow == null) {
|
if (fragmentToShow == null) {
|
||||||
when (tab) {
|
when (tab) {
|
||||||
is HomeTab.RoomList -> {
|
is HomeTab.RoomList -> {
|
||||||
val params = RoomListParams(tab.displayMode)
|
if (vectorFeatures.isNewAppLayoutEnabled()) {
|
||||||
add(R.id.roomListContainer, RoomListFragment::class.java, params.toMvRxBundle(), fragmentTag)
|
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 -> {
|
is HomeTab.DialPad -> {
|
||||||
add(R.id.roomListContainer, createDialPadFragment(), fragmentTag)
|
add(R.id.roomListContainer, createDialPadFragment(), fragmentTag)
|
||||||
|
@ -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()
|
||||||
|
}
|
@ -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<FragmentRoomListBinding>(),
|
||||||
|
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<HomeRoomSection>) {
|
||||||
|
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
|
||||||
|
}
|
@ -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()
|
||||||
|
}
|
@ -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<HomeRoomListViewState, HomeRoomListAction, HomeRoomListViewEvents>(initialState) {
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory : MavericksAssistedViewModelFactory<HomeRoomListViewModel, HomeRoomListViewState> {
|
||||||
|
override fun create(initialState: HomeRoomListViewState): HomeRoomListViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : MavericksViewModelFactory<HomeRoomListViewModel, HomeRoomListViewState> by hiltMavericksViewModelFactory()
|
||||||
|
|
||||||
|
private val pagedListConfig = PagedList.Config.Builder()
|
||||||
|
.setPageSize(10)
|
||||||
|
.setInitialLoadSizeHint(20)
|
||||||
|
.setEnablePlaceholders(true)
|
||||||
|
.setPrefetchDistance(10)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private val _sections = MutableSharedFlow<Set<HomeRoomSection>>(replay = 1)
|
||||||
|
val sections = _sections.asSharedFlow()
|
||||||
|
|
||||||
|
init {
|
||||||
|
configureSections()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun configureSections() {
|
||||||
|
val newSections = mutableSetOf<HomeRoomSection>()
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
@ -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<PagedList<RoomSummary>>
|
||||||
|
) : HomeRoomSection()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user