Fix suggested rooms

This commit is contained in:
Valere 2021-04-12 09:31:35 +02:00
parent 0d3c2b4bef
commit b635663ae3
8 changed files with 231 additions and 67 deletions

View File

@ -28,6 +28,7 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.airbnb.epoxy.EpoxyController
import com.airbnb.epoxy.OnModelBuildFinishedListener
import com.airbnb.mvrx.args
import com.airbnb.mvrx.fragmentViewModel
@ -92,11 +93,11 @@ class RoomListFragment @Inject constructor(
data class SectionAdapterInfo(
var section: SectionKey,
val headerHeaderAdapter: SectionHeaderAdapter,
val contentAdapter: RoomSummaryPagedController
val contentAdapter: EpoxyController
)
private val adapterInfosList = mutableListOf<SectionAdapterInfo>()
private var concatAdapter : ConcatAdapter? = null
private var concatAdapter: ConcatAdapter? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -125,7 +126,7 @@ class RoomListFragment @Inject constructor(
// it's for invites local echo
adapterInfosList.filter { it.section.notifyOfLocalEcho }
.onEach {
it.contentAdapter.roomChangeMembershipStates = ms
(it.contentAdapter as? RoomSummaryPagedController)?.roomChangeMembershipStates = ms
}
}
}
@ -180,8 +181,8 @@ class RoomListFragment @Inject constructor(
private fun setupCreateRoomButton() {
when (roomListParams.displayMode) {
RoomListDisplayMode.NOTIFICATIONS -> views.createChatFabMenu.isVisible = true
RoomListDisplayMode.PEOPLE -> views.createChatRoomButton.isVisible = true
RoomListDisplayMode.ROOMS -> views.createGroupRoomButton.isVisible = true
RoomListDisplayMode.PEOPLE -> views.createChatRoomButton.isVisible = true
RoomListDisplayMode.ROOMS -> views.createGroupRoomButton.isVisible = true
else -> Unit // No button in this mode
}
@ -249,23 +250,62 @@ class RoomListFragment @Inject constructor(
it.updateSection(SectionHeaderAdapter.RoomsSectionData(section.sectionName))
}
val contentAdapter = pagedControllerFactory.createRoomSummaryPagedController()
.also { controller ->
section.livePages.observe(viewLifecycleOwner) { pl ->
controller.submitList(pl)
sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy(isHidden = pl.isEmpty()))
checkEmptyState()
val contentAdapter =
when {
section.livePages != null -> {
pagedControllerFactory.createRoomSummaryPagedController()
.also { controller ->
section.livePages.observe(viewLifecycleOwner) { pl ->
controller.submitList(pl)
sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy(isHidden = pl.isEmpty()))
checkEmptyState()
}
section.notificationCount.observe(viewLifecycleOwner) { counts ->
sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy(
notificationCount = counts.totalCount,
isHighlighted = counts.isHighlight
))
}
section.isExpanded.observe(viewLifecycleOwner) { _ ->
refreshCollapseStates()
}
controller.listener = this
}
}
section.notificationCount.observe(viewLifecycleOwner) { counts ->
sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy(
notificationCount = counts.totalCount,
isHighlighted = counts.isHighlight
))
section.liveSuggested != null -> {
pagedControllerFactory.createSuggestedRoomListController()
.also { controller ->
section.liveSuggested.observe(viewLifecycleOwner) { info ->
controller.setData(info)
sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy(isHidden = info.rooms.isEmpty()))
checkEmptyState()
}
section.isExpanded.observe(viewLifecycleOwner) { _ ->
refreshCollapseStates()
}
controller.listener = this
}
}
section.isExpanded.observe(viewLifecycleOwner) { _ ->
refreshCollapseStates()
else -> {
pagedControllerFactory.createRoomSummaryListController()
.also { controller ->
section.liveList?.observe(viewLifecycleOwner) { list ->
controller.setData(list)
sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy(isHidden = list.isEmpty()))
checkEmptyState()
}
section.notificationCount.observe(viewLifecycleOwner) { counts ->
sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy(
notificationCount = counts.totalCount,
isHighlighted = counts.isHighlight
))
}
section.isExpanded.observe(viewLifecycleOwner) { _ ->
refreshCollapseStates()
}
controller.listener = this
}
}
controller.listener = this
}
adapterInfosList.add(
SectionAdapterInfo(
@ -294,8 +334,8 @@ class RoomListFragment @Inject constructor(
if (isAdded) {
when (roomListParams.displayMode) {
RoomListDisplayMode.NOTIFICATIONS -> views.createChatFabMenu.show()
RoomListDisplayMode.PEOPLE -> views.createChatRoomButton.show()
RoomListDisplayMode.ROOMS -> views.createGroupRoomButton.show()
RoomListDisplayMode.PEOPLE -> views.createChatRoomButton.show()
RoomListDisplayMode.ROOMS -> views.createGroupRoomButton.show()
else -> Unit
}
}
@ -368,14 +408,14 @@ class RoomListFragment @Inject constructor(
image = ContextCompat.getDrawable(requireContext(), R.drawable.ic_noun_party_popper),
message = getString(R.string.room_list_catchup_empty_body))
}
RoomListDisplayMode.PEOPLE ->
RoomListDisplayMode.PEOPLE ->
StateView.State.Empty(
title = getString(R.string.room_list_people_empty_title),
image = ContextCompat.getDrawable(requireContext(), R.drawable.empty_state_dm),
isBigImage = true,
message = getString(R.string.room_list_people_empty_body)
)
RoomListDisplayMode.ROOMS ->
RoomListDisplayMode.ROOMS ->
StateView.State.Empty(
title = getString(R.string.room_list_rooms_empty_title),
image = ContextCompat.getDrawable(requireContext(), R.drawable.empty_state_room),

View File

@ -17,8 +17,10 @@
package im.vector.app.features.home.room.list
import androidx.annotation.StringRes
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.liveData
import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.Loading
@ -32,6 +34,7 @@ import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.home.RoomListDisplayMode
import io.reactivex.Observable
import io.reactivex.rxkotlin.Observables
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -70,6 +73,8 @@ class RoomListViewModel @Inject constructor(
private var activeSpaceAwareQueries: List<ActiveSpaceQueryUpdater>? = null
val suggestedRoomJoiningState: MutableLiveData<Map<String, Async<Unit>>> = MutableLiveData(emptyMap())
interface ActiveSpaceQueryUpdater {
fun updateForSpaceId(roomId: String?)
}
@ -86,31 +91,12 @@ class RoomListViewModel @Inject constructor(
appStateHandler.selectedSpaceDataSource.observe()
// .observeOn(Schedulers.computation())
.distinctUntilChanged()
.switchMap { activeSpaceOption ->
.subscribe { activeSpaceOption ->
val selectedSpace = activeSpaceOption.orNull()
activeSpaceAwareQueries?.onEach { updater ->
updater.updateForSpaceId(selectedSpace?.roomId?.takeIf { MatrixPatterns.isRoomId(it) })
}
// activeSpaceAwareQueries?.forEach {
// it.updateQuery {
// it.copy(
// activeSpaceId = ActiveSpaceFilter.ActiveSpace(selectedSpace?.roomId?.takeIf { MatrixPatterns.isRoomId(it) })
// )
// }
// }
if (selectedSpace == null) {
Observable.just(emptyList())
} else {
liveData(context = viewModelScope.coroutineContext + Dispatchers.IO) {
val spaceSum = tryOrNull { session.spaceService().querySpaceChildren(selectedSpace.roomId, suggestedOnly = true) }
val value = spaceSum?.second ?: emptyList()
emit(value)
}.asObservable()
}
}
.execute { info ->
copy(asyncSuggestedRooms = info)
}
}.disposeOnClear()
appStateHandler.selectedSpaceDataSource.observe()
// .observeOn(Schedulers.computation())
@ -244,6 +230,51 @@ class RoomListViewModel @Inject constructor(
it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
it.roomTagQueryFilter = RoomTagQueryFilter(null, null, true)
}
// add suggested rooms
val suggestedRoomsObservable = // MutableLiveData<List<SpaceChildInfo>>()
appStateHandler.selectedSpaceDataSource.observe()
.distinctUntilChanged()
.switchMap { activeSpaceOption ->
val selectedSpace = activeSpaceOption.orNull()
if (selectedSpace == null) {
Observable.just(emptyList())
} else {
liveData(context = viewModelScope.coroutineContext + Dispatchers.IO) {
val spaceSum = tryOrNull { session.spaceService().querySpaceChildren(selectedSpace.roomId, suggestedOnly = true) }
val value = spaceSum?.second ?: emptyList()
// i need to check if it's already joined.
val filtered = value.filter {
session.getRoomSummary(it.childRoomId)?.membership?.isActive() != true
}
emit(filtered)
}.asObservable()
}
}
// .subscribe {
// Timber.w("VAL: Suggested rooms is ${it}")
// liveSuggestedRooms.postValue(it)
// }.disposeOnClear()
val liveSuggestedRooms = MutableLiveData<SuggestedRoomInfo>()
Observables.combineLatest(
suggestedRoomsObservable,
suggestedRoomJoiningState.asObservable()
) { rooms, joinStates ->
SuggestedRoomInfo(
rooms,
joinStates
)
}.subscribe {
liveSuggestedRooms.postValue(it)
}.disposeOnClear()
sections.add(
RoomsSection(
sectionName = stringProvider.getString(R.string.suggested_header),
liveSuggested = liveSuggestedRooms,
notifyOfLocalEcho = false
)
)
} else if (initialState.displayMode == RoomListDisplayMode.FILTERED) {
withQueryParams(
{
@ -499,33 +530,22 @@ class RoomListViewModel @Inject constructor(
}
private fun handleJoinSuggestedRoom(action: RoomListAction.JoinSuggestedRoom) {
setState {
copy(
suggestedRoomJoiningState = this.suggestedRoomJoiningState.toMutableMap().apply {
this[action.roomId] = Loading()
}.toMap()
)
}
suggestedRoomJoiningState.postValue(suggestedRoomJoiningState.value.orEmpty().toMutableMap().apply {
this[action.roomId] = Loading()
}.toMap())
viewModelScope.launch {
try {
awaitCallback<Unit> {
session.joinRoom(action.roomId, null, action.viaServers ?: emptyList(), it)
}
setState {
copy(
suggestedRoomJoiningState = this.suggestedRoomJoiningState.toMutableMap().apply {
this[action.roomId] = Success(Unit)
}.toMap()
)
}
suggestedRoomJoiningState.postValue(suggestedRoomJoiningState.value.orEmpty().toMutableMap().apply {
this[action.roomId] = Success(Unit)
}.toMap())
} catch (failure: Throwable) {
setState {
copy(
suggestedRoomJoiningState = this.suggestedRoomJoiningState.toMutableMap().apply {
this[action.roomId] = Fail(failure)
}.toMap()
)
}
suggestedRoomJoiningState.postValue(suggestedRoomJoiningState.value.orEmpty().toMutableMap().apply {
this[action.roomId] = Fail(failure)
}.toMap())
}
}
}

View File

@ -29,7 +29,6 @@ data class RoomListViewState(
val roomFilter: String = "",
val roomMembershipChanges: Map<String, ChangeMembershipState> = emptyMap(),
val asyncSuggestedRooms: Async<List<SpaceChildInfo>> = Uninitialized,
val suggestedRoomJoiningState: Map<String, Async<Unit>> = emptyMap(),
val currentUserName: String? = null,
val currentSpace: Async<RoomSummary?> = Uninitialized
) : MvRxState {

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2021 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
import com.airbnb.epoxy.TypedEpoxyController
import org.matrix.android.sdk.api.session.room.model.RoomSummary
class RoomSummaryListController(
private val roomSummaryItemFactory: RoomSummaryItemFactory
) : TypedEpoxyController<List<RoomSummary>>() {
var listener: RoomListListener? = null
override fun buildModels(data: List<RoomSummary>?) {
data?.forEach {
add(roomSummaryItemFactory.create(it, emptyMap(), emptySet(), listener))
}
}
}

View File

@ -30,6 +30,14 @@ class RoomSummaryPagedControllerFactory @Inject constructor(
fun createRoomSummaryPagedController(): RoomSummaryPagedController {
return RoomSummaryPagedController(roomSummaryItemFactory)
}
fun createRoomSummaryListController(): RoomSummaryListController {
return RoomSummaryListController(roomSummaryItemFactory)
}
fun createSuggestedRoomListController(): SuggestedRoomListController {
return SuggestedRoomListController(roomSummaryItemFactory)
}
}
class RoomSummaryPagedController(

View File

@ -24,7 +24,10 @@ import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotification
data class RoomsSection(
val sectionName: String,
val livePages: LiveData<PagedList<RoomSummary>>,
// can be a paged list or a regular list
val livePages: LiveData<PagedList<RoomSummary>>? = null,
val liveList: LiveData<List<RoomSummary>>? = null,
val liveSuggested: LiveData<SuggestedRoomInfo>? = null,
val isExpanded: MutableLiveData<Boolean> = MutableLiveData(true),
val notificationCount: MutableLiveData<RoomAggregateNotificationCount> = MutableLiveData(RoomAggregateNotificationCount(0, 0)),
val notifyOfLocalEcho: Boolean = false

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2021 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
import com.airbnb.mvrx.Async
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
class SuggestedRoomInfo(
val rooms: List<SpaceChildInfo>,
val joinEcho: Map<String, Async<Unit>>
)

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2021 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
import com.airbnb.epoxy.TypedEpoxyController
class SuggestedRoomListController(
private val roomSummaryItemFactory: RoomSummaryItemFactory
) : TypedEpoxyController<SuggestedRoomInfo>() {
var listener: RoomListListener? = null
override fun buildModels(data: SuggestedRoomInfo?) {
data?.rooms?.forEach { info ->
roomSummaryItemFactory.createSuggestion(info, data.joinEcho) {
listener?.onJoinSuggestedRoom(info)
}.let {
add(it)
}
}
}
}