mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 05:31:21 +03:00
new app layout's room list overhaul (#7101)
This commit is contained in:
parent
e37344a059
commit
da83a85f74
14 changed files with 371 additions and 409 deletions
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.app.features.home.room.list.home.filter
|
package im.vector.app.features.home.room.list.home
|
||||||
|
|
||||||
import com.airbnb.epoxy.EpoxyModel
|
import com.airbnb.epoxy.EpoxyModel
|
||||||
import com.airbnb.epoxy.paging.PagedListEpoxyController
|
import com.airbnb.epoxy.paging.PagedListEpoxyController
|
||||||
|
@ -24,11 +24,11 @@ import im.vector.app.features.home.RoomListDisplayMode
|
||||||
import im.vector.app.features.home.room.list.RoomListListener
|
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.RoomSummaryItemFactory
|
||||||
import im.vector.app.features.home.room.list.RoomSummaryItemPlaceHolder_
|
import im.vector.app.features.home.room.list.RoomSummaryItemPlaceHolder_
|
||||||
import im.vector.app.features.home.room.list.home.roomListEmptyItem
|
|
||||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
class HomeFilteredRoomsController(
|
class HomeFilteredRoomsController @Inject constructor(
|
||||||
private val roomSummaryItemFactory: RoomSummaryItemFactory,
|
private val roomSummaryItemFactory: RoomSummaryItemFactory,
|
||||||
) : PagedListEpoxyController<RoomSummary>(
|
) : PagedListEpoxyController<RoomSummary>(
|
||||||
// Important it must match the PageList builder notify Looper
|
// Important it must match the PageList builder notify Looper
|
||||||
|
@ -43,22 +43,11 @@ class HomeFilteredRoomsController(
|
||||||
}
|
}
|
||||||
|
|
||||||
var listener: RoomListListener? = null
|
var listener: RoomListListener? = null
|
||||||
var onFilterChanged: ((HomeRoomFilter) -> Unit)? = null
|
|
||||||
|
|
||||||
private var filtersData: List<HomeRoomFilter>? = null
|
|
||||||
private var emptyStateData: StateView.State.Empty? = null
|
private var emptyStateData: StateView.State.Empty? = null
|
||||||
private var currentState: StateView.State = StateView.State.Content
|
private var currentState: StateView.State = StateView.State.Content
|
||||||
|
|
||||||
override fun addModels(models: List<EpoxyModel<*>>) {
|
override fun addModels(models: List<EpoxyModel<*>>) {
|
||||||
val host = this
|
|
||||||
if (host.filtersData != null) {
|
|
||||||
roomFilterHeaderItem {
|
|
||||||
id("filter_header")
|
|
||||||
filtersData(host.filtersData)
|
|
||||||
onFilterChangedListener(host.onFilterChanged)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (models.isEmpty() && emptyStateData != null) {
|
if (models.isEmpty() && emptyStateData != null) {
|
||||||
emptyStateData?.let { emptyState ->
|
emptyStateData?.let { emptyState ->
|
||||||
roomListEmptyItem {
|
roomListEmptyItem {
|
||||||
|
@ -77,10 +66,6 @@ class HomeFilteredRoomsController(
|
||||||
this.emptyStateData = state
|
this.emptyStateData = state
|
||||||
}
|
}
|
||||||
|
|
||||||
fun submitFiltersData(data: List<HomeRoomFilter>?) {
|
|
||||||
this.filtersData = data
|
|
||||||
requestForcedModelBuild()
|
|
||||||
}
|
|
||||||
override fun buildItemModel(currentPosition: Int, item: RoomSummary?): EpoxyModel<*> {
|
override fun buildItemModel(currentPosition: Int, item: RoomSummary?): EpoxyModel<*> {
|
||||||
item ?: return RoomSummaryItemPlaceHolder_().apply { id(currentPosition) }
|
item ?: return RoomSummaryItemPlaceHolder_().apply { id(currentPosition) }
|
||||||
return roomSummaryItemFactory.create(item, roomChangeMembershipStates.orEmpty(), emptySet(), RoomListDisplayMode.ROOMS, listener)
|
return roomSummaryItemFactory.create(item, roomChangeMembershipStates.orEmpty(), emptySet(), RoomListDisplayMode.ROOMS, listener)
|
|
@ -17,7 +17,7 @@
|
||||||
package im.vector.app.features.home.room.list.home
|
package im.vector.app.features.home.room.list.home
|
||||||
|
|
||||||
import im.vector.app.core.platform.VectorViewModelAction
|
import im.vector.app.core.platform.VectorViewModelAction
|
||||||
import im.vector.app.features.home.room.list.home.filter.HomeRoomFilter
|
import im.vector.app.features.home.room.list.home.header.HomeRoomFilter
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.ConcatAdapter
|
import androidx.recyclerview.widget.ConcatAdapter
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.airbnb.epoxy.EpoxyControllerAdapter
|
|
||||||
import com.airbnb.epoxy.OnModelBuildFinishedListener
|
import com.airbnb.epoxy.OnModelBuildFinishedListener
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
|
@ -41,15 +40,12 @@ import im.vector.app.databinding.FragmentRoomListBinding
|
||||||
import im.vector.app.features.analytics.plan.ViewRoom
|
import im.vector.app.features.analytics.plan.ViewRoom
|
||||||
import im.vector.app.features.home.room.list.RoomListAnimator
|
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.RoomListListener
|
||||||
import im.vector.app.features.home.room.list.RoomSummaryItemFactory
|
|
||||||
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
|
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.RoomListQuickActionsSharedAction
|
||||||
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
|
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
|
||||||
import im.vector.app.features.home.room.list.home.filter.HomeFilteredRoomsController
|
import im.vector.app.features.home.room.list.home.header.HomeRoomFilter
|
||||||
import im.vector.app.features.home.room.list.home.filter.HomeRoomFilter
|
import im.vector.app.features.home.room.list.home.header.HomeRoomsHeadersController
|
||||||
import im.vector.app.features.home.room.list.home.invites.InvitesActivity
|
import im.vector.app.features.home.room.list.home.invites.InvitesActivity
|
||||||
import im.vector.app.features.home.room.list.home.invites.InvitesCounterController
|
|
||||||
import im.vector.app.features.home.room.list.home.recent.RecentRoomCarouselController
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
@ -63,10 +59,9 @@ class HomeRoomListFragment :
|
||||||
VectorBaseFragment<FragmentRoomListBinding>(),
|
VectorBaseFragment<FragmentRoomListBinding>(),
|
||||||
RoomListListener {
|
RoomListListener {
|
||||||
|
|
||||||
@Inject lateinit var roomSummaryItemFactory: RoomSummaryItemFactory
|
|
||||||
@Inject lateinit var userPreferencesProvider: UserPreferencesProvider
|
@Inject lateinit var userPreferencesProvider: UserPreferencesProvider
|
||||||
@Inject lateinit var recentRoomCarouselController: RecentRoomCarouselController
|
@Inject lateinit var headersController: HomeRoomsHeadersController
|
||||||
@Inject lateinit var invitesCounterController: InvitesCounterController
|
@Inject lateinit var roomsController: HomeFilteredRoomsController
|
||||||
|
|
||||||
private val roomListViewModel: HomeRoomListViewModel by fragmentViewModel()
|
private val roomListViewModel: HomeRoomListViewModel by fragmentViewModel()
|
||||||
private lateinit var sharedQuickActionsViewModel: RoomListQuickActionsSharedActionViewModel
|
private lateinit var sharedQuickActionsViewModel: RoomListQuickActionsSharedActionViewModel
|
||||||
|
@ -143,10 +138,25 @@ class HomeRoomListFragment :
|
||||||
|
|
||||||
modelBuildListener = OnModelBuildFinishedListener { it.dispatchTo(stateRestorer) }
|
modelBuildListener = OnModelBuildFinishedListener { it.dispatchTo(stateRestorer) }
|
||||||
|
|
||||||
roomListViewModel.sections.onEach { sections ->
|
roomListViewModel.onEach(HomeRoomListViewState::headersData) {
|
||||||
setUpAdapters(sections)
|
headersController.submitData(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
roomListViewModel.onEach(HomeRoomListViewState::roomsLivePagedList) { roomsListLive ->
|
||||||
|
roomsListLive?.observe(viewLifecycleOwner) { roomsList ->
|
||||||
|
roomsController.submitList(roomsList)
|
||||||
|
if (roomsList.isEmpty()) {
|
||||||
|
roomsController.requestForcedModelBuild()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
roomListViewModel.emptyStateFlow.onEach { emptyStateOptional ->
|
||||||
|
roomsController.submitEmptyStateData(emptyStateOptional.getOrNull())
|
||||||
}.launchIn(lifecycleScope)
|
}.launchIn(lifecycleScope)
|
||||||
|
|
||||||
|
setUpAdapters()
|
||||||
|
|
||||||
views.roomListView.adapter = concatAdapter
|
views.roomListView.adapter = concatAdapter
|
||||||
|
|
||||||
// we need to force scroll when recents/filter tabs are added to make them visible
|
// we need to force scroll when recents/filter tabs are added to make them visible
|
||||||
|
@ -163,13 +173,20 @@ class HomeRoomListFragment :
|
||||||
views.stateView.state = state.state
|
views.stateView.state = state.state
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpAdapters(sections: Set<HomeRoomSection>) {
|
private fun setUpAdapters() {
|
||||||
concatAdapter.adapters.forEach {
|
val headersAdapter = headersController.also { controller ->
|
||||||
concatAdapter.removeAdapter(it)
|
controller.invitesClickListener = ::onInvitesCounterClicked
|
||||||
}
|
controller.onFilterChangedListener = ::onRoomFilterChanged
|
||||||
sections.forEach {
|
controller.recentsRoomListener = this
|
||||||
concatAdapter.addAdapter(getAdapterForData(it))
|
}.adapter
|
||||||
}
|
|
||||||
|
val roomsAdapter = roomsController
|
||||||
|
.also { controller ->
|
||||||
|
controller.listener = this
|
||||||
|
}.adapter
|
||||||
|
|
||||||
|
concatAdapter.addAdapter(headersAdapter)
|
||||||
|
concatAdapter.addAdapter(roomsAdapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun promptLeaveRoom(roomId: String) {
|
private fun promptLeaveRoom(roomId: String) {
|
||||||
|
@ -191,43 +208,6 @@ class HomeRoomListFragment :
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAdapterForData(section: HomeRoomSection): EpoxyControllerAdapter {
|
|
||||||
return when (section) {
|
|
||||||
is HomeRoomSection.RoomSummaryData -> {
|
|
||||||
HomeFilteredRoomsController(
|
|
||||||
roomSummaryItemFactory,
|
|
||||||
).also { controller ->
|
|
||||||
controller.listener = this
|
|
||||||
controller.onFilterChanged = ::onRoomFilterChanged
|
|
||||||
roomListViewModel.emptyStateFlow.onEach { emptyStateOptional ->
|
|
||||||
controller.submitEmptyStateData(emptyStateOptional.getOrNull())
|
|
||||||
}.launchIn(lifecycleScope)
|
|
||||||
section.filtersData.onEach {
|
|
||||||
controller.submitFiltersData(it.getOrNull())
|
|
||||||
}.launchIn(lifecycleScope)
|
|
||||||
section.list.observe(viewLifecycleOwner) { list ->
|
|
||||||
controller.submitList(list)
|
|
||||||
if (list.isEmpty()) {
|
|
||||||
controller.requestForcedModelBuild()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.adapter
|
|
||||||
}
|
|
||||||
is HomeRoomSection.RecentRoomsData -> recentRoomCarouselController.also { controller ->
|
|
||||||
controller.listener = this
|
|
||||||
section.list.observe(viewLifecycleOwner) { list ->
|
|
||||||
controller.submitList(list)
|
|
||||||
}
|
|
||||||
}.adapter
|
|
||||||
is HomeRoomSection.InvitesCountData -> invitesCounterController.also { controller ->
|
|
||||||
controller.clickListener = ::onInvitesCounterClicked
|
|
||||||
section.count.observe(viewLifecycleOwner) { count ->
|
|
||||||
controller.submitData(count)
|
|
||||||
}
|
|
||||||
}.adapter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onInvitesCounterClicked() {
|
private fun onInvitesCounterClicked() {
|
||||||
startActivity(Intent(activity, InvitesActivity::class.java))
|
startActivity(Intent(activity, InvitesActivity::class.java))
|
||||||
}
|
}
|
||||||
|
@ -247,8 +227,13 @@ class HomeRoomListFragment :
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
views.roomListView.cleanup()
|
views.roomListView.cleanup()
|
||||||
recentRoomCarouselController.listener = null
|
|
||||||
invitesCounterController.clickListener = null
|
headersController.recentsRoomListener = null
|
||||||
|
headersController.invitesClickListener = null
|
||||||
|
headersController.onFilterChangedListener = null
|
||||||
|
|
||||||
|
roomsController.listener = null
|
||||||
|
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,21 +251,13 @@ class HomeRoomListFragment :
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRejectRoomInvitation(room: RoomSummary) {
|
override fun onRejectRoomInvitation(room: RoomSummary) = Unit
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAcceptRoomInvitation(room: RoomSummary) {
|
override fun onAcceptRoomInvitation(room: RoomSummary) = Unit
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onJoinSuggestedRoom(room: SpaceChildInfo) {
|
override fun onJoinSuggestedRoom(room: SpaceChildInfo) = Unit
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSuggestedRoomClicked(room: SpaceChildInfo) {
|
override fun onSuggestedRoomClicked(room: SpaceChildInfo) = Unit
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
package im.vector.app.features.home.room.list.home
|
package im.vector.app.features.home.room.list.home
|
||||||
|
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import androidx.lifecycle.map
|
|
||||||
import androidx.paging.PagedList
|
import androidx.paging.PagedList
|
||||||
|
import arrow.core.Option
|
||||||
import arrow.core.toOption
|
import arrow.core.toOption
|
||||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
|
@ -32,22 +32,22 @@ import im.vector.app.core.platform.StateView
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
import im.vector.app.core.resources.DrawableProvider
|
import im.vector.app.core.resources.DrawableProvider
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.features.home.room.list.home.filter.HomeRoomFilter
|
import im.vector.app.features.home.room.list.home.header.HomeRoomFilter
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.onStart
|
import kotlinx.coroutines.flow.onStart
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
|
||||||
import org.matrix.android.sdk.api.query.RoomCategoryFilter
|
import org.matrix.android.sdk.api.query.RoomCategoryFilter
|
||||||
import org.matrix.android.sdk.api.query.RoomTagQueryFilter
|
import org.matrix.android.sdk.api.query.RoomTagQueryFilter
|
||||||
import org.matrix.android.sdk.api.query.toActiveSpaceOrNoFilter
|
import org.matrix.android.sdk.api.query.toActiveSpaceOrNoFilter
|
||||||
|
@ -80,6 +80,7 @@ class HomeRoomListViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
companion object : MavericksViewModelFactory<HomeRoomListViewModel, HomeRoomListViewState> by hiltMavericksViewModelFactory()
|
companion object : MavericksViewModelFactory<HomeRoomListViewModel, HomeRoomListViewState> by hiltMavericksViewModelFactory()
|
||||||
|
|
||||||
|
private var roomsFlow: Flow<Option<RoomSummary>>? = null
|
||||||
private val pagedListConfig = PagedList.Config.Builder()
|
private val pagedListConfig = PagedList.Config.Builder()
|
||||||
.setPageSize(10)
|
.setPageSize(10)
|
||||||
.setInitialLoadSizeHint(20)
|
.setInitialLoadSizeHint(20)
|
||||||
|
@ -87,9 +88,6 @@ class HomeRoomListViewModel @AssistedInject constructor(
|
||||||
.setPrefetchDistance(10)
|
.setPrefetchDistance(10)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
private val _sections = MutableSharedFlow<Set<HomeRoomSection>>(replay = 1)
|
|
||||||
val sections = _sections.asSharedFlow()
|
|
||||||
|
|
||||||
private var currentFilter: HomeRoomFilter = HomeRoomFilter.ALL
|
private var currentFilter: HomeRoomFilter = HomeRoomFilter.ALL
|
||||||
private val _emptyStateFlow = MutableSharedFlow<Optional<StateView.State.Empty>>(replay = 1)
|
private val _emptyStateFlow = MutableSharedFlow<Optional<StateView.State.Empty>>(replay = 1)
|
||||||
val emptyStateFlow = _emptyStateFlow.asSharedFlow()
|
val emptyStateFlow = _emptyStateFlow.asSharedFlow()
|
||||||
|
@ -97,118 +95,77 @@ class HomeRoomListViewModel @AssistedInject constructor(
|
||||||
private var filteredPagedRoomSummariesLive: UpdatableLivePageResult? = null
|
private var filteredPagedRoomSummariesLive: UpdatableLivePageResult? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
configureSections()
|
observeOrderPreferences()
|
||||||
observePreferences()
|
observeInvites()
|
||||||
|
observeRecents()
|
||||||
|
observeFilterTabs()
|
||||||
|
observeRooms()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observePreferences() {
|
private fun observeInvites() {
|
||||||
preferencesStore.areRecentsEnabledFlow.onEach {
|
session.flow()
|
||||||
configureSections()
|
.liveRoomSummaries(
|
||||||
}.launchIn(viewModelScope)
|
roomSummaryQueryParams {
|
||||||
|
memberships = listOf(Membership.INVITE)
|
||||||
preferencesStore.isAZOrderingEnabledFlow.onEach {
|
},
|
||||||
configureSections()
|
RoomSortOrder.ACTIVITY
|
||||||
|
).onEach { list ->
|
||||||
|
setState { copy(headersData = headersData.copy(invitesCount = list.size)) }
|
||||||
}.launchIn(viewModelScope)
|
}.launchIn(viewModelScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun configureSections() = viewModelScope.launch {
|
private fun observeRecents() {
|
||||||
val newSections = mutableSetOf<HomeRoomSection>()
|
preferencesStore.areRecentsEnabledFlow
|
||||||
newSections.add(getInvitesCountSection())
|
.distinctUntilChanged()
|
||||||
|
.flatMapLatest { areEnabled ->
|
||||||
val areSettingsEnabled = preferencesStore.areRecentsEnabledFlow.first()
|
if (areEnabled) {
|
||||||
if (areSettingsEnabled) {
|
session.flow()
|
||||||
newSections.add(getRecentRoomsSection())
|
.liveBreadcrumbs(roomSummaryQueryParams {
|
||||||
}
|
|
||||||
newSections.add(getFilteredRoomsSection())
|
|
||||||
|
|
||||||
emitEmptyState()
|
|
||||||
_sections.emit(newSections)
|
|
||||||
|
|
||||||
setState {
|
|
||||||
copy(state = StateView.State.Content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getRecentRoomsSection(): HomeRoomSection {
|
|
||||||
val liveList = session.roomService()
|
|
||||||
.getBreadcrumbsLive(roomSummaryQueryParams {
|
|
||||||
displayName = QueryStringValue.NoCondition
|
|
||||||
memberships = listOf(Membership.JOIN)
|
memberships = listOf(Membership.JOIN)
|
||||||
})
|
})
|
||||||
|
.map { Optional.from(it) }
|
||||||
return HomeRoomSection.RecentRoomsData(
|
|
||||||
list = liveList
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getInvitesCountSection(): HomeRoomSection.InvitesCountData {
|
|
||||||
val builder = RoomSummaryQueryParams.Builder().also {
|
|
||||||
it.memberships = listOf(Membership.INVITE)
|
|
||||||
}
|
|
||||||
|
|
||||||
val liveCount = session.roomService().getRoomSummariesLive(
|
|
||||||
builder.build(),
|
|
||||||
RoomSortOrder.ACTIVITY
|
|
||||||
).map { it.count() }
|
|
||||||
|
|
||||||
return HomeRoomSection.InvitesCountData(liveCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun getFilteredRoomsSection(): HomeRoomSection.RoomSummaryData {
|
|
||||||
val builder = RoomSummaryQueryParams.Builder().also {
|
|
||||||
it.memberships = listOf(Membership.JOIN)
|
|
||||||
}
|
|
||||||
|
|
||||||
val params = getFilteredQueryParams(HomeRoomFilter.ALL, builder.build())
|
|
||||||
val sortOrder = if (preferencesStore.isAZOrderingEnabledFlow.first()) {
|
|
||||||
RoomSortOrder.NAME
|
|
||||||
} else {
|
} else {
|
||||||
RoomSortOrder.ACTIVITY
|
flow { emit(Optional.empty()) }
|
||||||
|
}.onEach { listOptional ->
|
||||||
|
setState { copy(headersData = headersData.copy(recents = listOptional.getOrNull())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val liveResults = session.roomService().getFilteredPagedRoomSummariesLive(
|
|
||||||
params,
|
|
||||||
pagedListConfig,
|
|
||||||
sortOrder
|
|
||||||
).also {
|
|
||||||
this.filteredPagedRoomSummariesLive = it
|
|
||||||
}
|
|
||||||
|
|
||||||
spaceStateHandler.getSelectedSpaceFlow()
|
|
||||||
.distinctUntilChanged()
|
|
||||||
.onStart {
|
|
||||||
emit(spaceStateHandler.getCurrentSpace().toOption())
|
|
||||||
}
|
|
||||||
.onEach { selectedSpaceOption ->
|
|
||||||
val selectedSpace = selectedSpaceOption.orNull()
|
|
||||||
liveResults.queryParams = liveResults.queryParams.copy(
|
|
||||||
spaceFilter = selectedSpace?.roomId.toActiveSpaceOrNoFilter()
|
|
||||||
)
|
|
||||||
emitEmptyState()
|
|
||||||
}.launchIn(viewModelScope)
|
}.launchIn(viewModelScope)
|
||||||
|
}
|
||||||
|
|
||||||
return HomeRoomSection.RoomSummaryData(
|
private fun observeFilterTabs() {
|
||||||
list = liveResults.livePagedList,
|
preferencesStore.areFiltersEnabledFlow
|
||||||
filtersData = getFiltersDataFlow()
|
.distinctUntilChanged()
|
||||||
|
.flatMapLatest { areEnabled ->
|
||||||
|
if (areEnabled) {
|
||||||
|
getFilterTabsFlow()
|
||||||
|
} else {
|
||||||
|
flow { emit(Optional.empty()) }
|
||||||
|
}.onEach { filtersOptional ->
|
||||||
|
setState {
|
||||||
|
validateCurrentFilter(filtersOptional.getOrNull())
|
||||||
|
copy(
|
||||||
|
headersData = headersData.copy(
|
||||||
|
filtersList = filtersOptional.getOrNull(),
|
||||||
|
currentFilter = currentFilter
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}.launchIn(viewModelScope)
|
||||||
|
}
|
||||||
|
|
||||||
private fun emitEmptyState() {
|
private fun validateCurrentFilter(filtersList: List<HomeRoomFilter>?) {
|
||||||
viewModelScope.launch {
|
if (filtersList?.contains(currentFilter) != true) {
|
||||||
val emptyState = getEmptyStateData(currentFilter, spaceStateHandler.getCurrentSpace())
|
handleChangeRoomFilter(HomeRoomFilter.ALL)
|
||||||
_emptyStateFlow.emit(Optional.from(emptyState))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFiltersDataFlow(): SharedFlow<Optional<List<HomeRoomFilter>>> {
|
private fun getFilterTabsFlow(): Flow<Optional<MutableList<HomeRoomFilter>>> {
|
||||||
val flow = MutableSharedFlow<Optional<List<HomeRoomFilter>>>(replay = 1)
|
|
||||||
|
|
||||||
val spaceFLow = spaceStateHandler.getSelectedSpaceFlow()
|
val spaceFLow = spaceStateHandler.getSelectedSpaceFlow()
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.onStart {
|
.onStart {
|
||||||
emit(spaceStateHandler.getCurrentSpace().toOption())
|
emit(spaceStateHandler.getCurrentSpace().toOption())
|
||||||
}
|
}
|
||||||
|
|
||||||
val favouritesFlow =
|
val favouritesFlow =
|
||||||
spaceFLow.flatMapLatest { selectedSpace ->
|
spaceFLow.flatMapLatest { selectedSpace ->
|
||||||
session.flow()
|
session.flow()
|
||||||
|
@ -236,10 +193,9 @@ class HomeRoomListViewModel @AssistedInject constructor(
|
||||||
.map { it.isNotEmpty() }
|
.map { it.isNotEmpty() }
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
|
|
||||||
combine(favouritesFlow, dmsFLow, preferencesStore.areFiltersEnabledFlow) { hasFavourite, hasDm, areFiltersEnabled ->
|
return combine(favouritesFlow, dmsFLow) { hasFavourite, hasDm ->
|
||||||
Triple(hasFavourite, hasDm, areFiltersEnabled)
|
hasFavourite to hasDm
|
||||||
}.onEach { (hasFavourite, hasDm, areFiltersEnabled) ->
|
}.map { (hasFavourite, hasDm) ->
|
||||||
if (areFiltersEnabled) {
|
|
||||||
val filtersData = mutableListOf(
|
val filtersData = mutableListOf(
|
||||||
HomeRoomFilter.ALL,
|
HomeRoomFilter.ALL,
|
||||||
HomeRoomFilter.UNREADS
|
HomeRoomFilter.UNREADS
|
||||||
|
@ -254,13 +210,59 @@ class HomeRoomListViewModel @AssistedInject constructor(
|
||||||
HomeRoomFilter.PEOPlE
|
HomeRoomFilter.PEOPlE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
flow.emit(Optional.from(filtersData))
|
Optional.from(filtersData)
|
||||||
} else {
|
}
|
||||||
flow.emit(Optional.empty())
|
|
||||||
}
|
}
|
||||||
}.launchIn(viewModelScope)
|
|
||||||
|
|
||||||
return flow
|
private fun observeRooms() = viewModelScope.launch {
|
||||||
|
val builder = RoomSummaryQueryParams.Builder().also {
|
||||||
|
it.memberships = listOf(Membership.JOIN)
|
||||||
|
}
|
||||||
|
|
||||||
|
val params = getFilteredQueryParams(currentFilter, builder.build())
|
||||||
|
val sortOrder = if (preferencesStore.isAZOrderingEnabledFlow.first()) {
|
||||||
|
RoomSortOrder.NAME
|
||||||
|
} else {
|
||||||
|
RoomSortOrder.ACTIVITY
|
||||||
|
}
|
||||||
|
|
||||||
|
val liveResults = session.roomService().getFilteredPagedRoomSummariesLive(
|
||||||
|
params,
|
||||||
|
pagedListConfig,
|
||||||
|
sortOrder
|
||||||
|
).also {
|
||||||
|
filteredPagedRoomSummariesLive = it
|
||||||
|
}
|
||||||
|
|
||||||
|
spaceStateHandler.getSelectedSpaceFlow()
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.onStart {
|
||||||
|
emit(spaceStateHandler.getCurrentSpace().toOption())
|
||||||
|
}
|
||||||
|
.onEach { selectedSpaceOption ->
|
||||||
|
val selectedSpace = selectedSpaceOption.orNull()
|
||||||
|
liveResults.queryParams = liveResults.queryParams.copy(
|
||||||
|
spaceFilter = selectedSpace?.roomId.toActiveSpaceOrNoFilter()
|
||||||
|
)
|
||||||
|
emitEmptyState()
|
||||||
|
}
|
||||||
|
.also { roomsFlow = it }
|
||||||
|
.launchIn(viewModelScope)
|
||||||
|
|
||||||
|
setState { copy(roomsLivePagedList = liveResults.livePagedList) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeOrderPreferences() {
|
||||||
|
preferencesStore.isAZOrderingEnabledFlow.onEach {
|
||||||
|
observeRooms()
|
||||||
|
}.launchIn(viewModelScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun emitEmptyState() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val emptyState = getEmptyStateData(currentFilter, spaceStateHandler.getCurrentSpace())
|
||||||
|
_emptyStateFlow.emit(Optional.from(emptyState))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFilteredQueryParams(filter: HomeRoomFilter, currentParams: RoomSummaryQueryParams): RoomSummaryQueryParams {
|
private fun getFilteredQueryParams(filter: HomeRoomFilter, currentParams: RoomSummaryQueryParams): RoomSummaryQueryParams {
|
||||||
|
@ -323,16 +325,20 @@ class HomeRoomListViewModel @AssistedInject constructor(
|
||||||
is HomeRoomListAction.LeaveRoom -> handleLeaveRoom(action)
|
is HomeRoomListAction.LeaveRoom -> handleLeaveRoom(action)
|
||||||
is HomeRoomListAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action)
|
is HomeRoomListAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action)
|
||||||
is HomeRoomListAction.ToggleTag -> handleToggleTag(action)
|
is HomeRoomListAction.ToggleTag -> handleToggleTag(action)
|
||||||
is HomeRoomListAction.ChangeRoomFilter -> handleChangeRoomFilter(action)
|
is HomeRoomListAction.ChangeRoomFilter -> handleChangeRoomFilter(action.filter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleChangeRoomFilter(action: HomeRoomListAction.ChangeRoomFilter) {
|
private fun handleChangeRoomFilter(newFilter: HomeRoomFilter) {
|
||||||
currentFilter = action.filter
|
if (currentFilter == newFilter) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentFilter = newFilter
|
||||||
filteredPagedRoomSummariesLive?.let { liveResults ->
|
filteredPagedRoomSummariesLive?.let { liveResults ->
|
||||||
liveResults.queryParams = getFilteredQueryParams(action.filter, liveResults.queryParams)
|
liveResults.queryParams = getFilteredQueryParams(currentFilter, liveResults.queryParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setState { copy(headersData = headersData.copy(currentFilter = currentFilter)) }
|
||||||
emitEmptyState()
|
emitEmptyState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,15 @@
|
||||||
|
|
||||||
package im.vector.app.features.home.room.list.home
|
package im.vector.app.features.home.room.list.home
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.paging.PagedList
|
||||||
import com.airbnb.mvrx.MavericksState
|
import com.airbnb.mvrx.MavericksState
|
||||||
import im.vector.app.core.platform.StateView
|
import im.vector.app.core.platform.StateView
|
||||||
|
import im.vector.app.features.home.room.list.home.header.RoomsHeadersData
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
|
||||||
data class HomeRoomListViewState(
|
data class HomeRoomListViewState(
|
||||||
val state: StateView.State = StateView.State.Loading
|
val state: StateView.State = StateView.State.Content,
|
||||||
|
val headersData: RoomsHeadersData = RoomsHeadersData(),
|
||||||
|
val roomsLivePagedList: LiveData<PagedList<RoomSummary>>? = null
|
||||||
) : MavericksState
|
) : MavericksState
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 im.vector.app.features.home.room.list.home.filter.HomeRoomFilter
|
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
|
||||||
|
|
||||||
sealed class HomeRoomSection {
|
|
||||||
data class RoomSummaryData(
|
|
||||||
val list: LiveData<PagedList<RoomSummary>>,
|
|
||||||
val filtersData: SharedFlow<Optional<List<HomeRoomFilter>>>,
|
|
||||||
) : HomeRoomSection()
|
|
||||||
|
|
||||||
data class RecentRoomsData(
|
|
||||||
val list: LiveData<List<RoomSummary>>
|
|
||||||
) : HomeRoomSection()
|
|
||||||
|
|
||||||
data class InvitesCountData(
|
|
||||||
val count: LiveData<Int>
|
|
||||||
) : HomeRoomSection()
|
|
||||||
}
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.app.features.home.room.list.home.filter
|
package im.vector.app.features.home.room.list.home.header
|
||||||
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* 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.header
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.util.TypedValue
|
||||||
|
import com.airbnb.epoxy.Carousel
|
||||||
|
import com.airbnb.epoxy.CarouselModelBuilder
|
||||||
|
import com.airbnb.epoxy.EpoxyController
|
||||||
|
import com.airbnb.epoxy.EpoxyModel
|
||||||
|
import com.airbnb.epoxy.carousel
|
||||||
|
import com.google.android.material.color.MaterialColors
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.resources.StringProvider
|
||||||
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
import im.vector.app.features.home.room.list.RoomListListener
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class HomeRoomsHeadersController @Inject constructor(
|
||||||
|
val stringProvider: StringProvider,
|
||||||
|
private val avatarRenderer: AvatarRenderer,
|
||||||
|
resources: Resources,
|
||||||
|
) : EpoxyController() {
|
||||||
|
|
||||||
|
private var data: RoomsHeadersData = RoomsHeadersData()
|
||||||
|
|
||||||
|
var onFilterChangedListener: ((HomeRoomFilter) -> Unit)? = null
|
||||||
|
var recentsRoomListener: RoomListListener? = null
|
||||||
|
var invitesClickListener: (() -> Unit)? = null
|
||||||
|
|
||||||
|
private val recentsHPadding = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
4f,
|
||||||
|
resources.displayMetrics
|
||||||
|
).toInt()
|
||||||
|
|
||||||
|
private val recentsTopPadding = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
12f,
|
||||||
|
resources.displayMetrics
|
||||||
|
).toInt()
|
||||||
|
|
||||||
|
override fun buildModels() {
|
||||||
|
val host = this
|
||||||
|
if (data.invitesCount != 0) {
|
||||||
|
addInviteCounter(host.invitesClickListener, data.invitesCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
data.recents?.let {
|
||||||
|
addRecents(host, it)
|
||||||
|
}
|
||||||
|
|
||||||
|
host.data.filtersList?.let {
|
||||||
|
addRoomFilterHeaderItem(host.onFilterChangedListener, it, host.data.currentFilter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addInviteCounter(invitesClickListener: (() -> Unit)?, invitesCount: Int) {
|
||||||
|
inviteCounterItem {
|
||||||
|
id("invites_counter")
|
||||||
|
invitesCount(invitesCount)
|
||||||
|
listener { invitesClickListener?.invoke() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addRecents(host: HomeRoomsHeadersController, recents: List<RoomSummary>) {
|
||||||
|
carousel {
|
||||||
|
id("recents_carousel")
|
||||||
|
padding(
|
||||||
|
Carousel.Padding(
|
||||||
|
host.recentsHPadding,
|
||||||
|
host.recentsTopPadding,
|
||||||
|
host.recentsHPadding,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
onBind { _, view, _ ->
|
||||||
|
val colorSurface = MaterialColors.getColor(view, R.attr.vctr_toolbar_background)
|
||||||
|
view.setBackgroundColor(colorSurface)
|
||||||
|
}
|
||||||
|
withModelsFrom(recents) { roomSummary ->
|
||||||
|
val onClick = host.recentsRoomListener?.let { it::onRoomClicked }
|
||||||
|
val onLongClick = host.recentsRoomListener?.let { it::onRoomLongClicked }
|
||||||
|
|
||||||
|
RecentRoomItem_()
|
||||||
|
.id(roomSummary.roomId)
|
||||||
|
.avatarRenderer(host.avatarRenderer)
|
||||||
|
.matrixItem(roomSummary.toMatrixItem())
|
||||||
|
.unreadNotificationCount(roomSummary.notificationCount)
|
||||||
|
.showHighlighted(roomSummary.highlightCount > 0)
|
||||||
|
.itemLongClickListener { _ -> onLongClick?.invoke(roomSummary) ?: false }
|
||||||
|
.itemClickListener { onClick?.invoke(roomSummary) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addRoomFilterHeaderItem(
|
||||||
|
filterChangedListener: ((HomeRoomFilter) -> Unit)?,
|
||||||
|
filtersList: List<HomeRoomFilter>,
|
||||||
|
currentFilter: HomeRoomFilter?,
|
||||||
|
) {
|
||||||
|
roomFilterHeaderItem {
|
||||||
|
id("filter_header")
|
||||||
|
filtersData(filtersList)
|
||||||
|
selectedFilter(currentFilter)
|
||||||
|
onFilterChangedListener(filterChangedListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun submitData(data: RoomsHeadersData) {
|
||||||
|
this.data = data
|
||||||
|
requestModelBuild()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <T> CarouselModelBuilder.withModelsFrom(
|
||||||
|
items: List<T>,
|
||||||
|
modelBuilder: (T) -> EpoxyModel<*>
|
||||||
|
) {
|
||||||
|
models(items.map { modelBuilder(it) })
|
||||||
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.app.features.home.room.list.home.invites
|
package im.vector.app.features.home.room.list.home.header
|
||||||
|
|
||||||
import com.airbnb.epoxy.EpoxyAttribute
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
import com.airbnb.epoxy.EpoxyModelClass
|
import com.airbnb.epoxy.EpoxyModelClass
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.app.features.home.room.list.home.recent
|
package im.vector.app.features.home.room.list.home.header
|
||||||
|
|
||||||
import android.view.HapticFeedbackConstants
|
import android.view.HapticFeedbackConstants
|
||||||
import android.view.View
|
import android.view.View
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.app.features.home.room.list.home.filter
|
package im.vector.app.features.home.room.list.home.header
|
||||||
|
|
||||||
import com.airbnb.epoxy.EpoxyAttribute
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
import com.airbnb.epoxy.EpoxyModelClass
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
|
@ -32,13 +32,20 @@ abstract class RoomFilterHeaderItem : VectorEpoxyModel<RoomFilterHeaderItem.Hold
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var filtersData: List<HomeRoomFilter>? = null
|
var filtersData: List<HomeRoomFilter>? = null
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var selectedFilter: HomeRoomFilter? = null
|
||||||
|
|
||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
super.bind(holder)
|
super.bind(holder)
|
||||||
with(holder.tabLayout) {
|
with(holder.tabLayout) {
|
||||||
removeAllTabs()
|
removeAllTabs()
|
||||||
|
clearOnTabSelectedListeners()
|
||||||
|
|
||||||
filtersData?.forEach { filter ->
|
filtersData?.forEach { filter ->
|
||||||
addTab(newTab().setText(filter.titleRes).setTag(filter))
|
addTab(
|
||||||
|
newTab().setText(filter.titleRes).setTag(filter),
|
||||||
|
filter == (selectedFilter ?: HomeRoomFilter.ALL)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
|
addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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.header
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
|
||||||
|
data class RoomsHeadersData(
|
||||||
|
val invitesCount: Int = 0,
|
||||||
|
val filtersList: List<HomeRoomFilter>? = null,
|
||||||
|
val currentFilter: HomeRoomFilter? = null,
|
||||||
|
val recents: List<RoomSummary>? = null
|
||||||
|
)
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.invites
|
|
||||||
|
|
||||||
import com.airbnb.epoxy.EpoxyController
|
|
||||||
import im.vector.app.core.resources.StringProvider
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class InvitesCounterController @Inject constructor(
|
|
||||||
val stringProvider: StringProvider
|
|
||||||
) : EpoxyController() {
|
|
||||||
|
|
||||||
private var count = 0
|
|
||||||
var clickListener: (() -> Unit)? = null
|
|
||||||
|
|
||||||
override fun buildModels() {
|
|
||||||
val host = this
|
|
||||||
if (count != 0) {
|
|
||||||
inviteCounterItem {
|
|
||||||
id("invites_counter")
|
|
||||||
invitesCount(host.count)
|
|
||||||
listener { host.clickListener?.invoke() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun submitData(count: Int?) {
|
|
||||||
this.count = count ?: 0
|
|
||||||
requestModelBuild()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,99 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.recent
|
|
||||||
|
|
||||||
import android.content.res.Resources
|
|
||||||
import android.util.TypedValue
|
|
||||||
import com.airbnb.epoxy.Carousel
|
|
||||||
import com.airbnb.epoxy.CarouselModelBuilder
|
|
||||||
import com.airbnb.epoxy.EpoxyController
|
|
||||||
import com.airbnb.epoxy.EpoxyModel
|
|
||||||
import com.airbnb.epoxy.carousel
|
|
||||||
import com.google.android.material.color.MaterialColors
|
|
||||||
import im.vector.app.R
|
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
|
||||||
import im.vector.app.features.home.room.list.RoomListListener
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class RecentRoomCarouselController @Inject constructor(
|
|
||||||
private val avatarRenderer: AvatarRenderer,
|
|
||||||
private val resources: Resources,
|
|
||||||
) : EpoxyController() {
|
|
||||||
|
|
||||||
private var data: List<RoomSummary>? = null
|
|
||||||
var listener: RoomListListener? = null
|
|
||||||
|
|
||||||
private val hPadding = TypedValue.applyDimension(
|
|
||||||
TypedValue.COMPLEX_UNIT_DIP,
|
|
||||||
4f,
|
|
||||||
resources.displayMetrics
|
|
||||||
).toInt()
|
|
||||||
|
|
||||||
private val topPadding = TypedValue.applyDimension(
|
|
||||||
TypedValue.COMPLEX_UNIT_DIP,
|
|
||||||
12f,
|
|
||||||
resources.displayMetrics
|
|
||||||
).toInt()
|
|
||||||
|
|
||||||
fun submitList(recentList: List<RoomSummary>) {
|
|
||||||
this.data = recentList
|
|
||||||
requestModelBuild()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun buildModels() {
|
|
||||||
val host = this
|
|
||||||
data?.let { data ->
|
|
||||||
carousel {
|
|
||||||
id("recents_carousel")
|
|
||||||
padding(Carousel.Padding(
|
|
||||||
host.hPadding,
|
|
||||||
host.topPadding,
|
|
||||||
host.hPadding,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
onBind { _, view, _ ->
|
|
||||||
val colorSurface = MaterialColors.getColor(view, R.attr.vctr_toolbar_background)
|
|
||||||
view.setBackgroundColor(colorSurface)
|
|
||||||
}
|
|
||||||
withModelsFrom(data) { roomSummary ->
|
|
||||||
val onClick = host.listener?.let { it::onRoomClicked }
|
|
||||||
val onLongClick = host.listener?.let { it::onRoomLongClicked }
|
|
||||||
|
|
||||||
RecentRoomItem_()
|
|
||||||
.id(roomSummary.roomId)
|
|
||||||
.avatarRenderer(host.avatarRenderer)
|
|
||||||
.matrixItem(roomSummary.toMatrixItem())
|
|
||||||
.unreadNotificationCount(roomSummary.notificationCount)
|
|
||||||
.showHighlighted(roomSummary.highlightCount > 0)
|
|
||||||
.itemLongClickListener { _ -> onLongClick?.invoke(roomSummary) ?: false }
|
|
||||||
.itemClickListener { onClick?.invoke(roomSummary) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <T> CarouselModelBuilder.withModelsFrom(
|
|
||||||
items: List<T>,
|
|
||||||
modelBuilder: (T) -> EpoxyModel<*>
|
|
||||||
) {
|
|
||||||
models(items.map { modelBuilder(it) })
|
|
||||||
}
|
|
Loading…
Reference in a new issue