Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/6505.wip
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
added filter tabs for new App layout's Home screen
18 changes: 18 additions & 0 deletions library/ui-styles/src/main/res/values/styles_tablayout.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>

<style name="Widget.Vector.TabLayout" parent="Widget.MaterialComponents.TabLayout">
<item name="materialThemeOverlay">@style/ThemeOverlay.Vector.HomeFilterTabLayout</item>
<item name="tabTextAppearance">@style/TextAppearance.Vector.FilterTabTextAppearance</item>
</style>

<style name="TextAppearance.Vector.FilterTabTextAppearance" parent="TextAppearance.Vector.Subtitle">
<item name="textAllCaps">false</item>
</style>

<style name="ThemeOverlay.Vector.HomeFilterTabLayout" parent="Theme.Vector.Launcher">
<item name="colorSurface">?vctr_toolbar_background</item>
<item name="colorOnSurface">?vctr_content_secondary</item>
</style>

</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package im.vector.app.features.home.room.list.home

import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.features.home.room.list.home.filter.HomeRoomFilter
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState

Expand All @@ -25,4 +26,5 @@ sealed class HomeRoomListAction : VectorViewModelAction {
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()
data class ChangeRoomFilter(val filter: HomeRoomFilter) : HomeRoomListAction()
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ 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 im.vector.app.features.home.room.list.home.filter.HomeFilteredRoomsController
import im.vector.app.features.home.room.list.home.filter.HomeRoomFilter
import im.vector.app.features.home.room.list.home.recent.RecentRoomCarouselController
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
Expand Down Expand Up @@ -207,28 +207,36 @@ class HomeRoomListFragment @Inject constructor(
.show()
}

private fun getAdapterForData(data: HomeRoomSection): EpoxyControllerAdapter {
return when (data) {
private fun getAdapterForData(section: HomeRoomSection): EpoxyControllerAdapter {
return when (section) {
is HomeRoomSection.RoomSummaryData -> {
RoomSummaryPagedController(
HomeFilteredRoomsController(
roomSummaryItemFactory,
RoomListDisplayMode.ROOMS
showFilters = section.showFilters,
).also { controller ->
controller.listener = this
data.list.observe(viewLifecycleOwner) { list ->
controller.onFilterChanged = ::onRoomFilterChanged
section.filtersData.onEach {
controller.submitFiltersData(it)
}.launchIn(lifecycleScope)
section.list.observe(viewLifecycleOwner) { list ->
controller.submitList(list)
}
}.adapter
}
is HomeRoomSection.RecentRoomsData -> recentRoomCarouselController.also { controller ->
controller.listener = this
data.list.observe(viewLifecycleOwner) { list ->
section.list.observe(viewLifecycleOwner) { list ->
controller.submitList(list)
}
}.adapter
}
}

private fun onRoomFilterChanged(filter: HomeRoomFilter) {
roomListViewModel.handle(HomeRoomListAction.ChangeRoomFilter(filter))
}

private fun handleSelectRoom(event: HomeRoomListViewEvents.SelectRoom, isInviteAlreadyAccepted: Boolean) {
navigator.openRoom(
context = requireActivity(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,33 +27,38 @@ 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.settings.VectorPreferences
import im.vector.app.features.home.room.list.home.filter.HomeRoomFilter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
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.QueryStringValue
import org.matrix.android.sdk.api.query.SpaceFilter
import org.matrix.android.sdk.api.query.RoomCategoryFilter
import org.matrix.android.sdk.api.query.RoomTagQueryFilter
import org.matrix.android.sdk.api.query.toActiveSpaceOrNoFilter
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.RoomSortOrder
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult
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.roomSummaryQueryParams
import org.matrix.android.sdk.api.session.room.state.isPublic
import org.matrix.android.sdk.flow.flow

class HomeRoomListViewModel @AssistedInject constructor(
@Assisted initialState: HomeRoomListViewState,
private val session: Session,
private val spaceStateHandler: SpaceStateHandler,
private val vectorPreferences: VectorPreferences,
) : VectorViewModel<HomeRoomListViewState, HomeRoomListAction, HomeRoomListViewEvents>(initialState) {

@AssistedFactory
Expand All @@ -73,6 +78,8 @@ class HomeRoomListViewModel @AssistedInject constructor(
private val _sections = MutableSharedFlow<Set<HomeRoomSection>>(replay = 1)
val sections = _sections.asSharedFlow()

private var filteredPagedRoomSummariesLive: UpdatableLivePageResult? = null

init {
configureSections()
}
Expand All @@ -81,7 +88,7 @@ class HomeRoomListViewModel @AssistedInject constructor(
val newSections = mutableSetOf<HomeRoomSection>()

newSections.add(getRecentRoomsSection())
newSections.add(getAllRoomsSection())
newSections.add(getFilteredRoomsSection())

viewModelScope.launch {
_sections.emit(newSections)
Expand All @@ -104,15 +111,21 @@ class HomeRoomListViewModel @AssistedInject constructor(
)
}

private fun getAllRoomsSection(): HomeRoomSection.RoomSummaryData {
private fun getFilteredRoomsSection(): HomeRoomSection.RoomSummaryData {
val builder = RoomSummaryQueryParams.Builder().also {
it.memberships = listOf(Membership.JOIN)
}

val filteredPagedRoomSummariesLive = session.roomService().getFilteredPagedRoomSummariesLive(
builder.build(),
pagedListConfig
)
val params = getFilteredQueryParams(HomeRoomFilter.ALL, builder.build())
val sortOrder = RoomSortOrder.ACTIVITY // #6506

val liveResults = session.roomService().getFilteredPagedRoomSummariesLive(
params,
pagedListConfig,
sortOrder
).also {
this.filteredPagedRoomSummariesLive = it
}

spaceStateHandler.getSelectedSpaceFlow()
.distinctUntilChanged()
Expand All @@ -121,20 +134,83 @@ class HomeRoomListViewModel @AssistedInject constructor(
}
.onEach { selectedSpaceOption ->
val selectedSpace = selectedSpaceOption.orNull()
filteredPagedRoomSummariesLive.queryParams = filteredPagedRoomSummariesLive.queryParams.copy(
spaceFilter = getSpaceFilter(selectedSpaceId = selectedSpace?.roomId)
liveResults.queryParams = liveResults.queryParams.copy(
spaceFilter = selectedSpace?.roomId.toActiveSpaceOrNoFilter()
)
}.launchIn(viewModelScope)

return HomeRoomSection.RoomSummaryData(
list = filteredPagedRoomSummariesLive.livePagedList
list = liveResults.livePagedList,
showFilters = true, // #6506
filtersData = getFiltersDataFlow()
)
}

private fun getSpaceFilter(selectedSpaceId: String?): SpaceFilter {
return when {
vectorPreferences.prefSpacesShowAllRoomInHome() -> selectedSpaceId.toActiveSpaceOrNoFilter()
else -> selectedSpaceId.toActiveSpaceOrOrphanRooms()
private fun getFiltersDataFlow(): SharedFlow<List<HomeRoomFilter>> {
val flow = MutableSharedFlow<List<HomeRoomFilter>>(replay = 1)

val favouritesFlow = session.flow()
.liveRoomSummaries(
RoomSummaryQueryParams.Builder().also { builder ->
builder.roomTagQueryFilter = RoomTagQueryFilter(true, null, null)
}.build()
)
.map { it.isNotEmpty() }
.distinctUntilChanged()

val dmsFLow = session.flow()
.liveRoomSummaries(
RoomSummaryQueryParams.Builder().also { builder ->
builder.memberships = listOf(Membership.JOIN)
builder.roomCategoryFilter = RoomCategoryFilter.ONLY_DM
}.build()
)
.map { it.isNotEmpty() }
.distinctUntilChanged()

favouritesFlow.combine(dmsFLow) { hasFavourite, hasDm ->
hasFavourite to hasDm
}.onEach { (hasFavourite, hasDm) ->
val filtersData = mutableListOf(
HomeRoomFilter.ALL,
HomeRoomFilter.UNREADS
)
if (hasFavourite) {
filtersData.add(
HomeRoomFilter.FAVOURITES
)
}
if (hasDm) {
filtersData.add(
HomeRoomFilter.PEOPlE
)
}

flow.emit(filtersData)
}.launchIn(viewModelScope)

return flow
}

private fun getFilteredQueryParams(filter: HomeRoomFilter, currentParams: RoomSummaryQueryParams): RoomSummaryQueryParams {
return when (filter) {
HomeRoomFilter.ALL -> currentParams.copy(
roomCategoryFilter = null,
roomTagQueryFilter = null
)
HomeRoomFilter.UNREADS -> currentParams.copy(
roomCategoryFilter = RoomCategoryFilter.ONLY_WITH_NOTIFICATIONS,
roomTagQueryFilter = RoomTagQueryFilter(null, false, null)
)
HomeRoomFilter.FAVOURITES ->
currentParams.copy(
roomCategoryFilter = null,
roomTagQueryFilter = RoomTagQueryFilter(true, null, null)
)
HomeRoomFilter.PEOPlE -> currentParams.copy(
roomCategoryFilter = RoomCategoryFilter.ONLY_DM,
roomTagQueryFilter = null
)
}
}

Expand All @@ -144,6 +220,13 @@ class HomeRoomListViewModel @AssistedInject constructor(
is HomeRoomListAction.LeaveRoom -> handleLeaveRoom(action)
is HomeRoomListAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action)
is HomeRoomListAction.ToggleTag -> handleToggleTag(action)
is HomeRoomListAction.ChangeRoomFilter -> handleChangeRoomFilter(action)
}
}

private fun handleChangeRoomFilter(action: HomeRoomListAction.ChangeRoomFilter) {
filteredPagedRoomSummariesLive?.let { liveResults ->
liveResults.queryParams = getFilteredQueryParams(action.filter, liveResults.queryParams)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ 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

sealed class HomeRoomSection {
data class RoomSummaryData(
val list: LiveData<PagedList<RoomSummary>>
val list: LiveData<PagedList<RoomSummary>>,
val showFilters: Boolean,
val filtersData: SharedFlow<List<HomeRoomFilter>>
) : HomeRoomSection()

data class RecentRoomsData(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* 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.filter

import com.airbnb.epoxy.EpoxyModel
import com.airbnb.epoxy.paging.PagedListEpoxyController
import im.vector.app.core.utils.createUIHandler
import im.vector.app.features.home.RoomListDisplayMode
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.RoomSummaryItemPlaceHolder_
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.RoomSummary

class HomeFilteredRoomsController(
private val roomSummaryItemFactory: RoomSummaryItemFactory,
private val showFilters: Boolean,
) : PagedListEpoxyController<RoomSummary>(
// Important it must match the PageList builder notify Looper
modelBuildingHandler = createUIHandler()
) {

private var roomChangeMembershipStates: Map<String, ChangeMembershipState>? = null
set(value) {
field = value
// ideally we could search for visible models and update only those
requestForcedModelBuild()
}

var listener: RoomListListener? = null
var onFilterChanged: ((HomeRoomFilter) -> Unit)? = null

private var filtersData: List<HomeRoomFilter>? = null

override fun addModels(models: List<EpoxyModel<*>>) {
val host = this
if (showFilters) {
roomFilterHeaderItem {
id("filter_header")
filtersData(host.filtersData)
onFilterChangedListener(host.onFilterChanged)
}
}
super.addModels(models)
}

fun submitFiltersData(data: List<HomeRoomFilter>) {
this.filtersData = data
requestForcedModelBuild()
}

override fun buildItemModel(currentPosition: Int, item: RoomSummary?): EpoxyModel<*> {
item ?: return RoomSummaryItemPlaceHolder_().apply { id(currentPosition) }
return roomSummaryItemFactory.create(item, roomChangeMembershipStates.orEmpty(), emptySet(), RoomListDisplayMode.ROOMS, listener)
}
}
Loading