BMA's cleanup

This commit is contained in:
Benoit Marty 2021-04-01 18:19:05 +02:00 committed by Benoit Marty
parent b47ced68b5
commit b9f73c6cc3
18 changed files with 266 additions and 243 deletions

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
*
* 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 org.matrix.android.sdk.api.query
enum class RoomCategoryFilter {
ONLY_DM,
ONLY_ROOMS,
ONLY_WITH_NOTIFICATIONS,
ALL
}

View file

@ -181,25 +181,28 @@ interface RoomService {
*/
fun peekRoom(roomIdOrAlias: String, callback: MatrixCallback<PeekResult>)
fun getPagedRoomSummariesLive(
queryParams: RoomSummaryQueryParams,
pagedListConfig: PagedList.Config = PagedList.Config.Builder()
.setPageSize(10)
.setInitialLoadSizeHint(20)
.setEnablePlaceholders(false)
.setPrefetchDistance(10)
.build()
): LiveData<PagedList<RoomSummary>>
/**
* TODO Doc
*/
fun getPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams,
pagedListConfig: PagedList.Config = defaultPagedListConfig): LiveData<PagedList<RoomSummary>>
/**
* TODO Doc
*/
fun getFilteredPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams,
pagedListConfig: PagedList.Config = defaultPagedListConfig): UpdatableFilterLivePageResult
/**
* TODO Doc
*/
fun getNotificationCountForRooms(queryParams: RoomSummaryQueryParams): RoomAggregateNotificationCount
fun getFilteredPagedRoomSummariesLive(
queryParams: RoomSummaryQueryParams,
pagedListConfig: PagedList.Config = PagedList.Config.Builder()
.setPageSize(10)
.setInitialLoadSizeHint(20)
.setEnablePlaceholders(false)
.setPrefetchDistance(10)
.build()
): UpdatableFilterLivePageResult
private val defaultPagedListConfig
get() = PagedList.Config.Builder()
.setPageSize(10)
.setInitialLoadSizeHint(20)
.setEnablePlaceholders(false)
.setPrefetchDistance(10)
.build()
}

View file

@ -17,6 +17,7 @@
package org.matrix.android.sdk.api.session.room
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.query.RoomCategoryFilter
import org.matrix.android.sdk.api.query.RoomTagQueryFilter
import org.matrix.android.sdk.api.session.room.model.Membership
@ -24,13 +25,6 @@ fun roomSummaryQueryParams(init: (RoomSummaryQueryParams.Builder.() -> Unit) = {
return RoomSummaryQueryParams.Builder().apply(init).build()
}
enum class RoomCategoryFilter {
ONLY_DM,
ONLY_ROOMS,
ONLY_WITH_NOTIFICATIONS,
ALL
}
/**
* This class can be used to filter room summaries to use with:
* [org.matrix.android.sdk.api.session.room.Room] and [org.matrix.android.sdk.api.session.room.RoomService]

View file

@ -20,7 +20,6 @@ data class RoomAggregateNotificationCount(
val notificationCount: Int,
val highlightCount: Int
) {
fun totalCount() = notificationCount + highlightCount
fun isHighlight() = highlightCount > 0
val totalCount = notificationCount + highlightCount
val isHighlight = highlightCount > 0
}

View file

@ -72,7 +72,6 @@ internal class SessionRealmConfigurationFactory @Inject constructor(
.allowWritesOnUiThread(true)
.modules(SessionRealmModule())
.schemaVersion(RealmSessionStoreMigration.SESSION_STORE_SCHEMA_VERSION)
// .deleteRealmIfMigrationNeeded()
.migration(migration)
.build()

View file

@ -106,7 +106,7 @@ internal open class RoomSummaryEntity(
private var tags: RealmList<RoomTagEntity> = RealmList()
fun tags(): RealmList<RoomTagEntity> = tags
fun tags(): List<RoomTagEntity> = tags
fun updateTags(newTags: List<Pair<String, Double?>>) {
val toDelete = mutableListOf<RoomTagEntity>()
@ -118,9 +118,9 @@ internal open class RoomSummaryEntity(
existingTag.tagOrder = updatedTag.second
}
}
toDelete.onEach { it.deleteFromRealm() }
toDelete.forEach { it.deleteFromRealm() }
newTags.forEach { newTag ->
if (tags.indexOfFirst { it.tagName == newTag.first } == -1) {
if (tags.all { it.tagName != newTag.first }) {
// we must add it
tags.add(
RoomTagEntity(newTag.first, newTag.second)
@ -128,9 +128,9 @@ internal open class RoomSummaryEntity(
}
}
isFavourite = newTags.indexOfFirst { it.first == RoomTag.ROOM_TAG_FAVOURITE } != -1
isLowPriority = newTags.indexOfFirst { it.first == RoomTag.ROOM_TAG_LOW_PRIORITY } != -1
isServerNotice = newTags.indexOfFirst { it.first == RoomTag.ROOM_TAG_SERVER_NOTICE } != -1
isFavourite = newTags.any { it.first == RoomTag.ROOM_TAG_FAVOURITE }
isLowPriority = newTags.any { it.first == RoomTag.ROOM_TAG_LOW_PRIORITY }
isServerNotice = newTags.any { it.first == RoomTag.ROOM_TAG_SERVER_NOTICE }
}
@Index
@ -170,18 +170,15 @@ internal open class RoomSummaryEntity(
fun updateAliases(newAliases: List<String>) {
// only update underlying field if there is a diff
if (newAliases.toSet() != aliases.toSet()) {
Timber.w("VAL: aliases updated")
if (newAliases.distinct().sorted() != aliases.distinct().sorted()) {
aliases.clear()
aliases.addAll(newAliases)
flatAliases = newAliases.joinToString(separator = "|", prefix = "|")
}
}
// this is required for querying
var flatAliases: String = ""
set(value) {
if (value != field) field = value
}
var isEncrypted: Boolean = false
set(value) {

View file

@ -24,7 +24,7 @@ import com.zhuinden.monarchy.Monarchy
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.Sort
import org.matrix.android.sdk.api.session.room.RoomCategoryFilter
import org.matrix.android.sdk.api.query.RoomCategoryFilter
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
import org.matrix.android.sdk.api.session.room.UpdatableFilterLivePageResult
import org.matrix.android.sdk.api.session.room.model.RoomSummary
@ -104,7 +104,8 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat
.sort(RoomSummaryEntityFields.BREADCRUMBS_INDEX)
}
fun getSortedPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, pagedListConfig: PagedList.Config): LiveData<PagedList<RoomSummary>> {
fun getSortedPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams,
pagedListConfig: PagedList.Config): LiveData<PagedList<RoomSummary>> {
val realmDataSourceFactory = monarchy.createDataSourceFactory { realm ->
roomSummariesQuery(realm, queryParams)
.sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING)
@ -112,12 +113,14 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat
val dataSourceFactory = realmDataSourceFactory.map {
roomSummaryMapper.map(it)
}
return monarchy.findAllPagedWithChanges(realmDataSourceFactory,
return monarchy.findAllPagedWithChanges(
realmDataSourceFactory,
LivePagedListBuilder(dataSourceFactory, pagedListConfig)
)
}
fun getFilteredPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, pagedListConfig: PagedList.Config): UpdatableFilterLivePageResult {
fun getFilteredPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams,
pagedListConfig: PagedList.Config): UpdatableFilterLivePageResult {
val realmDataSourceFactory = monarchy.createDataSourceFactory { realm ->
roomSummariesQuery(realm, queryParams)
.sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING)
@ -126,13 +129,13 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat
roomSummaryMapper.map(it)
}
val mapped = monarchy.findAllPagedWithChanges(realmDataSourceFactory,
val mapped = monarchy.findAllPagedWithChanges(
realmDataSourceFactory,
LivePagedListBuilder(dataSourceFactory, pagedListConfig)
)
return object : UpdatableFilterLivePageResult {
override val livePagedList: LiveData<PagedList<RoomSummary>>
get() = mapped
override val livePagedList: LiveData<PagedList<RoomSummary>> = mapped
override fun updateQuery(queryParams: RoomSummaryQueryParams) {
realmDataSourceFactory.updateQuery {
@ -167,10 +170,10 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat
queryParams.roomCategoryFilter?.let {
when (it) {
RoomCategoryFilter.ONLY_DM -> query.equalTo(RoomSummaryEntityFields.IS_DIRECT, true)
RoomCategoryFilter.ONLY_ROOMS -> query.equalTo(RoomSummaryEntityFields.IS_DIRECT, false)
RoomCategoryFilter.ONLY_DM -> query.equalTo(RoomSummaryEntityFields.IS_DIRECT, true)
RoomCategoryFilter.ONLY_ROOMS -> query.equalTo(RoomSummaryEntityFields.IS_DIRECT, false)
RoomCategoryFilter.ONLY_WITH_NOTIFICATIONS -> query.greaterThan(RoomSummaryEntityFields.NOTIFICATION_COUNT, 0)
RoomCategoryFilter.ALL -> {
RoomCategoryFilter.ALL -> {
// nop
}
}
@ -182,8 +185,8 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat
it.isLowPriority?.let { lp ->
query.equalTo(RoomSummaryEntityFields.IS_LOW_PRIORITY, lp)
}
it.isServerNotice?.let { lp ->
query.equalTo(RoomSummaryEntityFields.IS_SERVER_NOTICE, lp)
it.isServerNotice?.let { sn ->
query.equalTo(RoomSummaryEntityFields.IS_SERVER_NOTICE, sn)
}
}
return query

View file

@ -117,10 +117,7 @@ internal class RoomSummaryUpdater @Inject constructor(
val roomAliases = ContentMapper.map(lastAliasesEvent?.content).toModel<RoomAliasesContent>()?.aliases
.orEmpty()
// roomSummaryEntity.aliases.clear()
// roomSummaryEntity.aliases.addAll(roomAliases)
roomSummaryEntity.updateAliases(roomAliases)
roomSummaryEntity.flatAliases = roomAliases.joinToString(separator = "|", prefix = "|")
roomSummaryEntity.isEncrypted = encryptionEvent != null
roomSummaryEntity.encryptionEventTs = encryptionEvent?.originServerTs

View file

@ -19,8 +19,6 @@ package im.vector.app
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import im.vector.app.features.grouplist.SelectedGroupDataSource
import im.vector.app.features.home.room.list.ChronologicalRoomComparator
import io.reactivex.disposables.CompositeDisposable
import javax.inject.Inject
import javax.inject.Singleton
@ -29,11 +27,10 @@ import javax.inject.Singleton
* This class handles the global app state.
* It requires to be added to ProcessLifecycleOwner.get().lifecycle
*/
// TODO Keep this class for now, will maybe be used fro Space
@Singleton
class AppStateHandler @Inject constructor(
private val sessionDataSource: ActiveSessionDataSource,
private val selectedGroupDataSource: SelectedGroupDataSource,
private val chronologicalRoomComparator: ChronologicalRoomComparator) : LifecycleObserver {
) : LifecycleObserver {
private val compositeDisposable = CompositeDisposable()

View file

@ -127,6 +127,7 @@ abstract class VectorBaseFragment<VB: ViewBinding> : BaseMvRxFragment(), HasScre
Timber.i("onResume Fragment ${javaClass.simpleName}")
}
@CallSuper
override fun onPause() {
super.onPause()
Timber.i("onPause Fragment ${javaClass.simpleName}")
@ -154,6 +155,7 @@ abstract class VectorBaseFragment<VB: ViewBinding> : BaseMvRxFragment(), HasScre
super.onDestroyView()
}
@CallSuper
override fun onDestroy() {
Timber.i("onDestroy Fragment ${javaClass.simpleName}")
uiDisposables.dispose()

View file

@ -31,8 +31,8 @@ import im.vector.app.features.grouplist.SelectedGroupDataSource
import im.vector.app.features.ui.UiStateRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.query.RoomCategoryFilter
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.room.RoomCategoryFilter
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.internal.util.awaitCallback
@ -82,7 +82,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
override fun handle(action: HomeDetailAction) {
when (action) {
is HomeDetailAction.SwitchDisplayMode -> handleSwitchDisplayMode(action)
HomeDetailAction.MarkAllRoomsRead -> handleMarkAllRoomsRead()
HomeDetailAction.MarkAllRoomsRead -> handleMarkAllRoomsRead()
}
}
@ -103,12 +103,11 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
viewModelScope.launch(Dispatchers.Default) {
val roomIds = session.getRoomSummaries(
roomSummaryQueryParams {
this.memberships = listOf(Membership.JOIN)
this.roomCategoryFilter = RoomCategoryFilter.ONLY_WITH_NOTIFICATIONS
memberships = listOf(Membership.JOIN)
roomCategoryFilter = RoomCategoryFilter.ONLY_WITH_NOTIFICATIONS
}
).map {
it.roomId
}
)
.map { it.roomId }
try {
awaitCallback<Unit> {
session.markAllAsRead(roomIds, it)
@ -146,7 +145,8 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
roomSummaryQueryParams {
memberships = Membership.activeMemberships()
}
).asObservable()
)
.asObservable()
.throttleFirst(300, TimeUnit.MILLISECONDS)
.subscribe {
val dmInvites = session.getRoomSummaries(
@ -179,13 +179,13 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
setState {
copy(
notificationCountCatchup = dmRooms.totalCount() + otherRooms.totalCount() + roomsInvite + dmInvites,
notificationHighlightCatchup = dmRooms.isHighlight() || otherRooms.isHighlight(),
notificationCountPeople = dmRooms.totalCount() + dmInvites,
notificationHighlightPeople = dmRooms.isHighlight() || dmInvites > 0,
notificationCountRooms = otherRooms.totalCount() + roomsInvite,
notificationHighlightRooms = otherRooms.isHighlight() || roomsInvite > 0,
hasUnreadMessages = dmRooms.totalCount() + otherRooms.totalCount() > 0
notificationCountCatchup = dmRooms.totalCount + otherRooms.totalCount + roomsInvite + dmInvites,
notificationHighlightCatchup = dmRooms.isHighlight || otherRooms.isHighlight,
notificationCountPeople = dmRooms.totalCount + dmInvites,
notificationHighlightPeople = dmRooms.isHighlight || dmInvites > 0,
notificationCountRooms = otherRooms.totalCount + roomsInvite,
notificationHighlightRooms = otherRooms.isHighlight || roomsInvite > 0,
hasUnreadMessages = dmRooms.totalCount + otherRooms.totalCount > 0
)
}
}

View file

@ -40,13 +40,17 @@ class ShortcutsHandler @Inject constructor(
fun observeRoomsAndBuildShortcuts(): Disposable {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) {
// No op
return Observable.empty<Unit>().subscribe()
return Disposables.empty()
}
return activeSessionHolder.getSafeActiveSession()?.getPagedRoomSummariesLive(roomSummaryQueryParams {
this.memberships = listOf(Membership.JOIN)
this.roomTagQueryFilter = RoomTagQueryFilter(isFavorite = true, null, null)
})?.asObservable()
return activeSessionHolder.getSafeActiveSession()
?.getPagedRoomSummariesLive(
roomSummaryQueryParams {
memberships = listOf(Membership.JOIN)
roomTagQueryFilter = RoomTagQueryFilter(isFavorite = true, null, null)
}
)
?.asObservable()
?.subscribe { rooms ->
val shortcuts = rooms
.take(n = 4) // Android only allows us to create 4 shortcuts
@ -55,7 +59,6 @@ class ShortcutsHandler @Inject constructor(
ShortcutManagerCompat.removeAllDynamicShortcuts(context)
ShortcutManagerCompat.addDynamicShortcuts(context, shortcuts)
}
?: Disposables.empty()
}

View file

@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.session.room.notification.RoomNotificationStat
sealed class RoomListAction : VectorViewModelAction {
data class SelectRoom(val roomSummary: RoomSummary) : RoomListAction()
data class ToggleSection(val section: RoomListViewModel.RoomsSection) : RoomListAction()
data class ToggleSection(val section: RoomsSection) : RoomListAction()
data class AcceptInvitation(val roomSummary: RoomSummary) : RoomListAction()
data class RejectInvitation(val roomSummary: RoomSummary) : RoomListAction()
data class FilterWith(val filter: String) : RoomListAction()

View file

@ -95,7 +95,7 @@ class RoomListFragment @Inject constructor(
)
private val adapterInfosList = mutableListOf<SectionAdapterInfo>()
private var concatAdapter: ConcatAdapter? = null
private val concatAdapter = ConcatAdapter()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -104,10 +104,10 @@ class RoomListFragment @Inject constructor(
sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java)
roomListViewModel.observeViewEvents {
when (it) {
is RoomListViewEvents.Loading -> showLoading(it.message)
is RoomListViewEvents.Failure -> showFailure(it.throwable)
is RoomListViewEvents.Loading -> showLoading(it.message)
is RoomListViewEvents.Failure -> showFailure(it.throwable)
is RoomListViewEvents.SelectRoom -> handleSelectRoom(it)
is RoomListViewEvents.Done -> Unit
is RoomListViewEvents.Done -> Unit
}.exhaustive
}
@ -134,10 +134,10 @@ class RoomListFragment @Inject constructor(
val isRoomSectionExpanded = roomsSection.isExpanded.value.orTrue()
if (actualBlock.section.isExpanded && !isRoomSectionExpanded) {
// we have to remove the content adapter
concatAdapter?.removeAdapter(actualBlock.contentAdapter.adapter)
concatAdapter.removeAdapter(actualBlock.contentAdapter.adapter)
} else if (!actualBlock.section.isExpanded && isRoomSectionExpanded) {
// we must add it back!
concatAdapter?.addAdapter(contentInsertIndex, actualBlock.contentAdapter.adapter)
concatAdapter.addAdapter(contentInsertIndex, actualBlock.contentAdapter.adapter)
}
contentInsertIndex = if (isRoomSectionExpanded) {
contentInsertIndex + 2
@ -148,7 +148,7 @@ class RoomListFragment @Inject constructor(
isExpanded = isRoomSectionExpanded
)
actualBlock.headerHeaderAdapter.updateSection(
actualBlock.headerHeaderAdapter.section.copy(isExpanded = isRoomSectionExpanded)
actualBlock.headerHeaderAdapter.roomsSectionData.copy(isExpanded = isRoomSectionExpanded)
)
}
}
@ -160,16 +160,10 @@ class RoomListFragment @Inject constructor(
override fun onDestroyView() {
adapterInfosList.onEach { it.contentAdapter.removeModelBuildListener(modelBuildListener) }
adapterInfosList.clear()
// roomController.removeModelBuildListener(modelBuildListener)
modelBuildListener = null
views.roomListView.cleanup()
// controllers.onEach {
// it.listener = null
// // concatAdapter.removeAdapter(it.adapter)
// }
// controllers.clear()
// roomController.listener = null
// favRoomController.listener = null
footerController.listener = null
// TODO Cleanup listener on the ConcatAdapter's adapters?
stateRestorer.clear()
views.createChatFabMenu.listener = null
super.onDestroyView()
@ -182,8 +176,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
}
@ -238,40 +232,35 @@ class RoomListFragment @Inject constructor(
stateRestorer = LayoutManagerStateRestorer(layoutManager).register()
views.roomListView.layoutManager = layoutManager
views.roomListView.itemAnimator = RoomListAnimator()
// views.roomListView.setRecycledViewPool(sharedViewPool)
layoutManager.recycleChildrenOnDetach = true
modelBuildListener = OnModelBuildFinishedListener { it.dispatchTo(stateRestorer) }
val concatAdapter = ConcatAdapter()
// val hasOnlyOneSection = roomListViewModel.sections.size == 1
roomListViewModel.sections.forEach { section ->
val sectionAdapter = SectionHeaderAdapter {
roomListViewModel.handle(RoomListAction.ToggleSection(section))
}.also {
it.updateSection(SectionHeaderAdapter.SectionViewModel(
section.sectionName
))
it.updateSection(SectionHeaderAdapter.RoomsSectionData(section.sectionName))
}
val contentAdapter = pagedControllerFactory.createRoomSummaryPagedController().also {
section.livePages.observe(viewLifecycleOwner) { pl ->
it.submitList(pl)
sectionAdapter.updateSection(sectionAdapter.section.copy(isHidden = pl.isEmpty()))
checkEmptyState()
}
section.notificationCount.observe(viewLifecycleOwner) { counts ->
sectionAdapter.updateSection(sectionAdapter.section.copy(
notificationCount = counts.totalCount(),
isHighlighted = counts.isHighlight()
))
}
section.isExpanded.observe(viewLifecycleOwner) { _ ->
refreshCollapseStates()
}
it.listener = this
}
val contentAdapter = 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
}
adapterInfosList.add(
SectionAdapterInfo(
SectionKey(
@ -288,10 +277,9 @@ class RoomListFragment @Inject constructor(
}
// Add the footer controller
this.footerController.listener = this
footerController.listener = this
concatAdapter.addAdapter(footerController.adapter)
this.concatAdapter = concatAdapter
views.roomListView.adapter = concatAdapter
}
@ -299,8 +287,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
}
}
@ -308,28 +296,28 @@ class RoomListFragment @Inject constructor(
private fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) {
when (quickAction) {
is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> {
is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> {
roomListViewModel.handle(RoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES_NOISY))
}
is RoomListQuickActionsSharedAction.NotificationsAll -> {
is RoomListQuickActionsSharedAction.NotificationsAll -> {
roomListViewModel.handle(RoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES))
}
is RoomListQuickActionsSharedAction.NotificationsMentionsOnly -> {
roomListViewModel.handle(RoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MENTIONS_ONLY))
}
is RoomListQuickActionsSharedAction.NotificationsMute -> {
is RoomListQuickActionsSharedAction.NotificationsMute -> {
roomListViewModel.handle(RoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MUTE))
}
is RoomListQuickActionsSharedAction.Settings -> {
is RoomListQuickActionsSharedAction.Settings -> {
navigator.openRoomProfile(requireActivity(), quickAction.roomId)
}
is RoomListQuickActionsSharedAction.Favorite -> {
is RoomListQuickActionsSharedAction.Favorite -> {
roomListViewModel.handle(RoomListAction.ToggleTag(quickAction.roomId, RoomTag.ROOM_TAG_FAVOURITE))
}
is RoomListQuickActionsSharedAction.LowPriority -> {
is RoomListQuickActionsSharedAction.LowPriority -> {
roomListViewModel.handle(RoomListAction.ToggleTag(quickAction.roomId, RoomTag.ROOM_TAG_LOW_PRIORITY))
}
is RoomListQuickActionsSharedAction.Leave -> {
is RoomListQuickActionsSharedAction.Leave -> {
promptLeaveRoom(quickAction.roomId)
}
}.exhaustive
@ -364,7 +352,7 @@ class RoomListFragment @Inject constructor(
}
private fun checkEmptyState() {
val hasNoRoom = adapterInfosList.all { it.headerHeaderAdapter.section.isHidden }
val hasNoRoom = adapterInfosList.all { it.headerHeaderAdapter.roomsSectionData.isHidden }
if (hasNoRoom) {
val emptyState = when (roomListParams.displayMode) {
RoomListDisplayMode.NOTIFICATIONS -> {
@ -373,14 +361,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,10 +17,7 @@
package im.vector.app.features.home.room.list
import androidx.annotation.StringRes
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import androidx.paging.PagedList
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
@ -34,27 +31,26 @@ import kotlinx.coroutines.Dispatchers
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.RoomCategoryFilter
import org.matrix.android.sdk.api.query.RoomTagQueryFilter
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.room.RoomCategoryFilter
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
import org.matrix.android.sdk.api.session.room.UpdatableFilterLivePageResult
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomSummary
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.api.session.room.summary.RoomAggregateNotificationCount
import org.matrix.android.sdk.rx.asObservable
import org.matrix.android.sdk.rx.rx
import timber.log.Timber
import javax.inject.Inject
class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
private val session: Session,
private val stringProvider: StringProvider)
: VectorViewModel<RoomListViewState, RoomListAction, RoomListViewEvents>(initialState) {
class RoomListViewModel @Inject constructor(
initialState: RoomListViewState,
private val session: Session,
private val stringProvider: StringProvider
) : VectorViewModel<RoomListViewState, RoomListAction, RoomListViewEvents>(initialState) {
interface Factory {
fun create(initialState: RoomListViewState): RoomListViewModel
@ -84,19 +80,9 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
}
}
data class RoomsSection(
val sectionName: String,
val livePages: LiveData<PagedList<RoomSummary>>,
val isExpanded: MutableLiveData<Boolean> = MutableLiveData(true),
val notificationCount: MutableLiveData<RoomAggregateNotificationCount> =
MutableLiveData(RoomAggregateNotificationCount(0, 0)),
val notifyOfLocalEcho: Boolean = false
)
val sections: List<RoomsSection> by lazy {
val sections = mutableListOf<RoomsSection>()
if (initialState.displayMode == RoomListDisplayMode.PEOPLE) {
addSection(sections, R.string.invitations_header, true) {
it.memberships = listOf(Membership.INVITE)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM
@ -113,7 +99,6 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM
}
} else if (initialState.displayMode == RoomListDisplayMode.ROOMS) {
addSection(sections, R.string.invitations_header, true) {
it.memberships = listOf(Membership.INVITE)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
@ -143,18 +128,20 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
it.roomTagQueryFilter = RoomTagQueryFilter(null, null, true)
}
} else if (initialState.displayMode == RoomListDisplayMode.FILTERED) {
withQueryParams({
it.memberships = Membership.activeMemberships()
}) { qpm ->
val name = stringProvider.getString(R.string.bottom_action_rooms)
session.getFilteredPagedRoomSummariesLive(qpm)
.let { livePagedList ->
updatableQuery = livePagedList
sections.add(RoomsSection(name, livePagedList.livePagedList))
}
}
withQueryParams(
{
it.memberships = Membership.activeMemberships()
},
{ qpm ->
val name = stringProvider.getString(R.string.bottom_action_rooms)
session.getFilteredPagedRoomSummariesLive(qpm)
.let { updatableFilterLivePageResult ->
updatableQuery = updatableFilterLivePageResult
sections.add(RoomsSection(name, updatableFilterLivePageResult.livePagedList))
}
}
)
} else if (initialState.displayMode == RoomListDisplayMode.NOTIFICATIONS) {
addSection(sections, R.string.invitations_header, true) {
it.memberships = listOf(Membership.INVITE)
it.roomCategoryFilter = RoomCategoryFilter.ALL
@ -171,14 +158,14 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
override fun handle(action: RoomListAction) {
when (action) {
is RoomListAction.SelectRoom -> handleSelectRoom(action)
is RoomListAction.AcceptInvitation -> handleAcceptInvitation(action)
is RoomListAction.RejectInvitation -> handleRejectInvitation(action)
is RoomListAction.FilterWith -> handleFilter(action)
is RoomListAction.LeaveRoom -> handleLeaveRoom(action)
is RoomListAction.SelectRoom -> handleSelectRoom(action)
is RoomListAction.AcceptInvitation -> handleAcceptInvitation(action)
is RoomListAction.RejectInvitation -> handleRejectInvitation(action)
is RoomListAction.FilterWith -> handleFilter(action)
is RoomListAction.LeaveRoom -> handleLeaveRoom(action)
is RoomListAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action)
is RoomListAction.ToggleTag -> handleToggleTag(action)
is RoomListAction.ToggleSection -> handleToggleSection(action.section)
is RoomListAction.ToggleTag -> handleToggleTag(action)
is RoomListAction.ToggleSection -> handleToggleSection(action.section)
}.exhaustive
}
@ -186,40 +173,41 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
@StringRes nameRes: Int,
notifyOfLocalEcho: Boolean = false,
query: (RoomSummaryQueryParams.Builder) -> Unit) {
withQueryParams({
query.invoke(it)
}) { roomQueryParams ->
withQueryParams(
{ query.invoke(it) },
{ roomQueryParams ->
val name = stringProvider.getString(nameRes)
session.getPagedRoomSummariesLive(roomQueryParams)
.let { livePagedList ->
val name = stringProvider.getString(nameRes)
session.getPagedRoomSummariesLive(roomQueryParams)
.let { livePagedList ->
// use it also as a source to update count
livePagedList.asObservable()
.observeOn(Schedulers.computation())
.subscribe {
sections.find { it.sectionName == name }
?.notificationCount
?.postValue(session.getNotificationCountForRooms(roomQueryParams))
}.disposeOnClear()
// use it also as a source to update count
livePagedList.asObservable()
.observeOn(Schedulers.computation())
.subscribe {
sections.find { it.sectionName == name }
?.notificationCount
?.postValue(session.getNotificationCountForRooms(roomQueryParams))
}
.disposeOnClear()
sections.add(
RoomsSection(
sectionName = name,
livePages = livePagedList,
notifyOfLocalEcho = notifyOfLocalEcho
sections.add(
RoomsSection(
sectionName = name,
livePages = livePagedList,
notifyOfLocalEcho = notifyOfLocalEcho
)
)
)
}
}
}
}
)
}
private fun withQueryParams(builder: (RoomSummaryQueryParams.Builder) -> Unit, block: (RoomSummaryQueryParams) -> Unit) {
RoomSummaryQueryParams.Builder().apply {
builder.invoke(this)
}.build().let {
block(it)
}
RoomSummaryQueryParams.Builder()
.apply { builder.invoke(this) }
.build()
.let { block(it) }
}
fun isPublicRoom(roomId: String): Boolean {
@ -233,10 +221,13 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
}
private fun handleToggleSection(roomSection: RoomsSection) {
roomSection.isExpanded.postValue(!roomSection.isExpanded.value.orFalse())
/* TODO Cleanup if it is working
sections.find { it.sectionName == roomSection.sectionName }
?.let { section ->
section.isExpanded.postValue(!section.isExpanded.value.orFalse())
}
*/
}
private fun handleFilter(action: RoomListAction.FilterWith) {
@ -247,8 +238,8 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
}
updatableQuery?.updateQuery(
roomSummaryQueryParams {
this.memberships = Membership.activeMemberships()
this.displayName = QueryStringValue.Contains(action.filter, QueryStringValue.Case.INSENSITIVE)
memberships = Membership.activeMemberships()
displayName = QueryStringValue.Contains(action.filter, QueryStringValue.Case.INSENSITIVE)
}
)
}
@ -351,7 +342,7 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
private fun String.otherTag(): String? {
return when (this) {
RoomTag.ROOM_TAG_FAVOURITE -> RoomTag.ROOM_TAG_LOW_PRIORITY
RoomTag.ROOM_TAG_FAVOURITE -> RoomTag.ROOM_TAG_LOW_PRIORITY
RoomTag.ROOM_TAG_LOW_PRIORITY -> RoomTag.ROOM_TAG_FAVOURITE
else -> null
}

View file

@ -23,16 +23,19 @@ import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import javax.inject.Inject
class RoomSummaryPagedControllerFactory @Inject constructor(private val roomSummaryItemFactory: RoomSummaryItemFactory) {
class RoomSummaryPagedControllerFactory @Inject constructor(
private val roomSummaryItemFactory: RoomSummaryItemFactory
) {
fun createRoomSummaryPagedController(): RoomSummaryPagedController {
return RoomSummaryPagedController(roomSummaryItemFactory)
}
}
class RoomSummaryPagedController constructor(private val roomSummaryItemFactory: RoomSummaryItemFactory)
: PagedListEpoxyController<RoomSummary>(
// Important it must match the PageList builder notify Looper
class RoomSummaryPagedController(
private val roomSummaryItemFactory: RoomSummaryItemFactory
) : PagedListEpoxyController<RoomSummary>(
// Important it must match the PageList builder notify Looper
modelBuildingHandler = createUIHandler()
) {
@ -46,28 +49,20 @@ class RoomSummaryPagedController constructor(private val roomSummaryItemFactory:
}
override fun buildItemModel(currentPosition: Int, item: RoomSummary?): EpoxyModel<*> {
val unwrappedItem = item
// for place holder if enabled
?: return roomSummaryItemFactory.createRoomItem(
RoomSummary(
roomId = "null_item_pos_$currentPosition",
name = "",
encryptionEventTs = null,
isEncrypted = false,
typingUsers = emptyList()
), emptySet(), null, null)
item ?: return roomSummaryItemFactory.createRoomItem(
roomSummary = RoomSummary(
roomId = "null_item_pos_$currentPosition",
name = "",
encryptionEventTs = null,
isEncrypted = false,
typingUsers = emptyList()
),
selectedRoomIds = emptySet(),
onClick = null,
onLongClick = null
)
// GenericItem_().apply { id("null_item_pos_$currentPosition") }
return roomSummaryItemFactory.create(unwrappedItem, roomChangeMembershipStates ?: emptyMap(), emptySet(), listener)
return roomSummaryItemFactory.create(item, roomChangeMembershipStates.orEmpty(), emptySet(), listener)
}
// override fun onModelBound(holder: EpoxyViewHolder, boundModel: EpoxyModel<*>, position: Int, previouslyBoundModel: EpoxyModel<*>?) {
// Timber.w("VAL: Will load around $position")
// super.onModelBound(holder, boundModel, position, previouslyBoundModel)
// }
// fun onRoomLongClicked() {
// userPreferencesProvider.neverShowLongClickOnRoomHelpAgain()
// requestModelBuild()
// }
}

View file

@ -0,0 +1,31 @@
/*
* 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 androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.paging.PagedList
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
data class RoomsSection(
val sectionName: String,
val livePages: LiveData<PagedList<RoomSummary>>,
val isExpanded: MutableLiveData<Boolean> = MutableLiveData(true),
val notificationCount: MutableLiveData<RoomAggregateNotificationCount> = MutableLiveData(RoomAggregateNotificationCount(0, 0)),
val notifyOfLocalEcho: Boolean = false
)

View file

@ -30,7 +30,7 @@ class SectionHeaderAdapter constructor(
private val onClickAction: (() -> Unit)
) : RecyclerView.Adapter<SectionHeaderAdapter.VH>() {
data class SectionViewModel(
data class RoomsSectionData(
val name: String,
val isExpanded: Boolean = true,
val notificationCount: Int = 0,
@ -38,12 +38,12 @@ class SectionHeaderAdapter constructor(
val isHidden: Boolean = true
)
lateinit var section: SectionViewModel
lateinit var roomsSectionData: RoomsSectionData
private set
fun updateSection(newSection: SectionViewModel) {
if (!::section.isInitialized || newSection != section) {
section = newSection
fun updateSection(newRoomsSectionData: RoomsSectionData) {
if (!::roomsSectionData.isInitialized || newRoomsSectionData != roomsSectionData) {
roomsSectionData = newRoomsSectionData
notifyDataSetChanged()
}
}
@ -52,7 +52,7 @@ class SectionHeaderAdapter constructor(
setHasStableIds(true)
}
override fun getItemId(position: Int) = section.hashCode().toLong()
override fun getItemId(position: Int) = roomsSectionData.hashCode().toLong()
override fun getItemViewType(position: Int) = R.layout.item_room_category
@ -61,10 +61,10 @@ class SectionHeaderAdapter constructor(
}
override fun onBindViewHolder(holder: VH, position: Int) {
holder.bind(section)
holder.bind(roomsSectionData)
}
override fun getItemCount(): Int = if (section.isHidden) 0 else 1
override fun getItemCount(): Int = if (roomsSectionData.isHidden) 0 else 1
class VH constructor(
private val binding: ItemRoomCategoryBinding,
@ -77,14 +77,14 @@ class SectionHeaderAdapter constructor(
}))
}
fun bind(section: SectionViewModel) {
binding.roomCategoryTitleView.text = section.name
fun bind(roomsSectionData: RoomsSectionData) {
binding.roomCategoryTitleView.text = roomsSectionData.name
val tintColor = ThemeUtils.getColor(binding.root.context, R.attr.riotx_text_secondary)
val expandedArrowDrawableRes = if (section.isExpanded) R.drawable.ic_expand_more_white else R.drawable.ic_expand_less_white
val expandedArrowDrawableRes = if (roomsSectionData.isExpanded) R.drawable.ic_expand_more_white else R.drawable.ic_expand_less_white
val expandedArrowDrawable = ContextCompat.getDrawable(binding.root.context, expandedArrowDrawableRes)?.also {
DrawableCompat.setTint(it, tintColor)
}
binding.roomCategoryUnreadCounterBadgeView.render(UnreadCounterBadgeView.State(section.notificationCount, section.isHighlighted))
binding.roomCategoryUnreadCounterBadgeView.render(UnreadCounterBadgeView.State(roomsSectionData.notificationCount, roomsSectionData.isHighlighted))
binding.roomCategoryTitleView.setCompoundDrawablesWithIntrinsicBounds(null, null, expandedArrowDrawable, null)
}