mirror of
https://github.com/vector-im/riotX-android
synced 2025-10-06 00:02:48 +02:00
Room summary paged initial commit
This commit is contained in:
@@ -33,3 +33,4 @@ sealed class QueryStringValue {
|
|||||||
INSENSITIVE
|
INSENSITIVE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 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
|
||||||
|
|
||||||
|
data class RoomTagQueryFilter(
|
||||||
|
val isFavorite: Boolean?,
|
||||||
|
val isLowPriority: Boolean?,
|
||||||
|
val isServerNotice: Boolean?
|
||||||
|
)
|
@@ -17,6 +17,7 @@
|
|||||||
package org.matrix.android.sdk.api.session.room
|
package org.matrix.android.sdk.api.session.room
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.paging.PagedList
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
import org.matrix.android.sdk.api.MatrixCallback
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||||
@@ -26,7 +27,9 @@ import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
|||||||
import org.matrix.android.sdk.api.session.room.peeking.PeekResult
|
import org.matrix.android.sdk.api.session.room.peeking.PeekResult
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
import org.matrix.android.sdk.internal.session.room.UpdatableFilterLivePageResult
|
||||||
import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription
|
import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription
|
||||||
|
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface defines methods to get rooms. It's implemented at the session level.
|
* This interface defines methods to get rooms. It's implemented at the session level.
|
||||||
@@ -178,4 +181,9 @@ interface RoomService {
|
|||||||
* This call will try to gather some information on this room, but it could fail and get nothing more
|
* This call will try to gather some information on this room, but it could fail and get nothing more
|
||||||
*/
|
*/
|
||||||
fun peekRoom(roomIdOrAlias: String, callback: MatrixCallback<PeekResult>)
|
fun peekRoom(roomIdOrAlias: String, callback: MatrixCallback<PeekResult>)
|
||||||
|
|
||||||
|
fun getPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams): LiveData<PagedList<RoomSummary>>
|
||||||
|
|
||||||
|
fun getNotificationCountForRooms(queryParams: RoomSummaryQueryParams): RoomAggregateNotificationCount
|
||||||
|
fun getFilteredPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams): UpdatableFilterLivePageResult
|
||||||
}
|
}
|
||||||
|
@@ -17,12 +17,19 @@
|
|||||||
package org.matrix.android.sdk.api.session.room
|
package org.matrix.android.sdk.api.session.room
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
|
import org.matrix.android.sdk.api.query.RoomTagQueryFilter
|
||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
|
|
||||||
fun roomSummaryQueryParams(init: (RoomSummaryQueryParams.Builder.() -> Unit) = {}): RoomSummaryQueryParams {
|
fun roomSummaryQueryParams(init: (RoomSummaryQueryParams.Builder.() -> Unit) = {}): RoomSummaryQueryParams {
|
||||||
return RoomSummaryQueryParams.Builder().apply(init).build()
|
return RoomSummaryQueryParams.Builder().apply(init).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class RoomCategoryFilter {
|
||||||
|
ONLY_DM,
|
||||||
|
ONLY_ROOMS,
|
||||||
|
ALL
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class can be used to filter room summaries to use with:
|
* 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]
|
* [org.matrix.android.sdk.api.session.room.Room] and [org.matrix.android.sdk.api.session.room.RoomService]
|
||||||
@@ -31,7 +38,9 @@ data class RoomSummaryQueryParams(
|
|||||||
val roomId: QueryStringValue,
|
val roomId: QueryStringValue,
|
||||||
val displayName: QueryStringValue,
|
val displayName: QueryStringValue,
|
||||||
val canonicalAlias: QueryStringValue,
|
val canonicalAlias: QueryStringValue,
|
||||||
val memberships: List<Membership>
|
val memberships: List<Membership>,
|
||||||
|
val roomCategoryFilter: RoomCategoryFilter?,
|
||||||
|
val roomTagQueryFilter: RoomTagQueryFilter?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
class Builder {
|
class Builder {
|
||||||
@@ -40,12 +49,16 @@ data class RoomSummaryQueryParams(
|
|||||||
var displayName: QueryStringValue = QueryStringValue.IsNotEmpty
|
var displayName: QueryStringValue = QueryStringValue.IsNotEmpty
|
||||||
var canonicalAlias: QueryStringValue = QueryStringValue.NoCondition
|
var canonicalAlias: QueryStringValue = QueryStringValue.NoCondition
|
||||||
var memberships: List<Membership> = Membership.all()
|
var memberships: List<Membership> = Membership.all()
|
||||||
|
var roomCategoryFilter: RoomCategoryFilter? = RoomCategoryFilter.ALL
|
||||||
|
var roomTagQueryFilter: RoomTagQueryFilter? = null
|
||||||
|
|
||||||
fun build() = RoomSummaryQueryParams(
|
fun build() = RoomSummaryQueryParams(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
displayName = displayName,
|
displayName = displayName,
|
||||||
canonicalAlias = canonicalAlias,
|
canonicalAlias = canonicalAlias,
|
||||||
memberships = memberships
|
memberships = memberships,
|
||||||
|
roomCategoryFilter = roomCategoryFilter,
|
||||||
|
roomTagQueryFilter = roomTagQueryFilter
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.matrix.android.sdk.api.session.room.summary
|
||||||
|
|
||||||
|
data class RoomAggregateNotificationCount(
|
||||||
|
val notificationCount: Int,
|
||||||
|
val highlightCount: Int
|
||||||
|
) {
|
||||||
|
fun totalCount() = notificationCount + highlightCount
|
||||||
|
|
||||||
|
fun isHighlight() = highlightCount > 0
|
||||||
|
}
|
@@ -17,22 +17,27 @@
|
|||||||
package org.matrix.android.sdk.internal.database
|
package org.matrix.android.sdk.internal.database
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
import io.realm.DynamicRealm
|
||||||
|
import io.realm.FieldAttribute
|
||||||
import io.realm.RealmMigration
|
import io.realm.RealmMigration
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
||||||
import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntityFields
|
import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntityFields
|
||||||
import org.matrix.android.sdk.internal.database.model.EditionOfEventFields
|
import org.matrix.android.sdk.internal.database.model.EditionOfEventFields
|
||||||
|
import org.matrix.android.sdk.internal.database.model.EventEntityFields
|
||||||
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
|
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
|
||||||
import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields
|
import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields
|
||||||
import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntityFields
|
import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntityFields
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomEntityFields
|
import org.matrix.android.sdk.internal.database.model.RoomEntityFields
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
|
import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
|
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
|
||||||
|
import org.matrix.android.sdk.internal.database.model.RoomTagEntityFields
|
||||||
|
import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val SESSION_STORE_SCHEMA_VERSION = 8L
|
const val SESSION_STORE_SCHEMA_VERSION = 9L
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
|
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
|
||||||
@@ -46,6 +51,7 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
|||||||
if (oldVersion <= 5) migrateTo6(realm)
|
if (oldVersion <= 5) migrateTo6(realm)
|
||||||
if (oldVersion <= 6) migrateTo7(realm)
|
if (oldVersion <= 6) migrateTo7(realm)
|
||||||
if (oldVersion <= 7) migrateTo8(realm)
|
if (oldVersion <= 7) migrateTo8(realm)
|
||||||
|
if (oldVersion <= 8) migrateTo9(realm)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun migrateTo1(realm: DynamicRealm) {
|
private fun migrateTo1(realm: DynamicRealm) {
|
||||||
@@ -149,4 +155,43 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
|||||||
?.removeField("sourceLocalEchoEvents")
|
?.removeField("sourceLocalEchoEvents")
|
||||||
?.addRealmListField(EditAggregatedSummaryEntityFields.EDITIONS.`$`, editionOfEventSchema)
|
?.addRealmListField(EditAggregatedSummaryEntityFields.EDITIONS.`$`, editionOfEventSchema)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun migrateTo9(realm: DynamicRealm) {
|
||||||
|
Timber.d("Step 8 -> 9")
|
||||||
|
|
||||||
|
realm.schema.get("RoomSummaryEntity")
|
||||||
|
?.addField(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Long::class.java, FieldAttribute.INDEXED)
|
||||||
|
?.setNullable(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, true)
|
||||||
|
?.addIndex(RoomSummaryEntityFields.MEMBERSHIP_STR)
|
||||||
|
?.addIndex(RoomSummaryEntityFields.IS_DIRECT)
|
||||||
|
?.addIndex(RoomSummaryEntityFields.VERSIONING_STATE_STR)
|
||||||
|
|
||||||
|
?.addField(RoomSummaryEntityFields.IS_FAVOURITE, Boolean::class.java)
|
||||||
|
?.addIndex(RoomSummaryEntityFields.IS_FAVOURITE)
|
||||||
|
?.addField(RoomSummaryEntityFields.IS_LOW_PRIORITY, Boolean::class.java)
|
||||||
|
?.addIndex(RoomSummaryEntityFields.IS_LOW_PRIORITY)
|
||||||
|
?.addField(RoomSummaryEntityFields.IS_SERVER_NOTICE, Boolean::class.java)
|
||||||
|
?.addIndex(RoomSummaryEntityFields.IS_SERVER_NOTICE)
|
||||||
|
|
||||||
|
?.transform { obj ->
|
||||||
|
|
||||||
|
val isFavorite = obj.getList(RoomSummaryEntityFields.TAGS.`$`).any {
|
||||||
|
it.getString(RoomTagEntityFields.TAG_NAME) == RoomTag.ROOM_TAG_FAVOURITE
|
||||||
|
}
|
||||||
|
obj.setBoolean(RoomSummaryEntityFields.IS_FAVOURITE, isFavorite)
|
||||||
|
|
||||||
|
val isLowPriority = obj.getList(RoomSummaryEntityFields.TAGS.`$`).any {
|
||||||
|
it.getString(RoomTagEntityFields.TAG_NAME) == RoomTag.ROOM_TAG_LOW_PRIORITY
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.setBoolean(RoomSummaryEntityFields.IS_LOW_PRIORITY, isLowPriority)
|
||||||
|
|
||||||
|
// XXX migrate last message origin server ts
|
||||||
|
obj.getObject(RoomSummaryEntityFields.LATEST_PREVIEWABLE_EVENT.`$`)
|
||||||
|
?.getObject(TimelineEventEntityFields.ROOT.`$`)
|
||||||
|
?.getLong(EventEntityFields.ORIGIN_SERVER_TS)?.let {
|
||||||
|
obj.setLong(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -72,6 +72,7 @@ internal class SessionRealmConfigurationFactory @Inject constructor(
|
|||||||
.allowWritesOnUiThread(true)
|
.allowWritesOnUiThread(true)
|
||||||
.modules(SessionRealmModule())
|
.modules(SessionRealmModule())
|
||||||
.schemaVersion(RealmSessionStoreMigration.SESSION_STORE_SCHEMA_VERSION)
|
.schemaVersion(RealmSessionStoreMigration.SESSION_STORE_SCHEMA_VERSION)
|
||||||
|
// .deleteRealmIfMigrationNeeded()
|
||||||
.migration(migration)
|
.migration(migration)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
@@ -26,7 +26,7 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa
|
|||||||
private val typingUsersTracker: DefaultTypingUsersTracker) {
|
private val typingUsersTracker: DefaultTypingUsersTracker) {
|
||||||
|
|
||||||
fun map(roomSummaryEntity: RoomSummaryEntity): RoomSummary {
|
fun map(roomSummaryEntity: RoomSummaryEntity): RoomSummary {
|
||||||
val tags = roomSummaryEntity.tags.map {
|
val tags = roomSummaryEntity.tags().map {
|
||||||
RoomTag(it.tagName, it.tagOrder)
|
RoomTag(it.tagName, it.tagOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -16,61 +16,221 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.internal.database.model
|
package org.matrix.android.sdk.internal.database.model
|
||||||
|
|
||||||
|
import io.realm.RealmList
|
||||||
|
import io.realm.RealmObject
|
||||||
|
import io.realm.annotations.Index
|
||||||
|
import io.realm.annotations.PrimaryKey
|
||||||
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
|
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
|
||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
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.RoomSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.VersioningState
|
import org.matrix.android.sdk.api.session.room.model.VersioningState
|
||||||
import io.realm.RealmList
|
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
||||||
import io.realm.RealmObject
|
import timber.log.Timber
|
||||||
import io.realm.annotations.PrimaryKey
|
|
||||||
|
|
||||||
internal open class RoomSummaryEntity(
|
internal open class RoomSummaryEntity(
|
||||||
@PrimaryKey var roomId: String = "",
|
@PrimaryKey var roomId: String = ""
|
||||||
var displayName: String? = "",
|
|
||||||
var avatarUrl: String? = "",
|
|
||||||
var name: String? = "",
|
|
||||||
var topic: String? = "",
|
|
||||||
var latestPreviewableEvent: TimelineEventEntity? = null,
|
|
||||||
var heroes: RealmList<String> = RealmList(),
|
|
||||||
var joinedMembersCount: Int? = 0,
|
|
||||||
var invitedMembersCount: Int? = 0,
|
|
||||||
var isDirect: Boolean = false,
|
|
||||||
var directUserId: String? = null,
|
|
||||||
var otherMemberIds: RealmList<String> = RealmList(),
|
|
||||||
var notificationCount: Int = 0,
|
|
||||||
var highlightCount: Int = 0,
|
|
||||||
var readMarkerId: String? = null,
|
|
||||||
var hasUnreadMessages: Boolean = false,
|
|
||||||
var tags: RealmList<RoomTagEntity> = RealmList(),
|
|
||||||
var userDrafts: UserDraftsEntity? = null,
|
|
||||||
var breadcrumbsIndex: Int = RoomSummary.NOT_IN_BREADCRUMBS,
|
|
||||||
var canonicalAlias: String? = null,
|
|
||||||
var aliases: RealmList<String> = RealmList(),
|
|
||||||
// this is required for querying
|
|
||||||
var flatAliases: String = "",
|
|
||||||
var isEncrypted: Boolean = false,
|
|
||||||
var encryptionEventTs: Long? = 0,
|
|
||||||
var roomEncryptionTrustLevelStr: String? = null,
|
|
||||||
var inviterId: String? = null,
|
|
||||||
var hasFailedSending: Boolean = false
|
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
|
||||||
|
var displayName: String? = ""
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
var avatarUrl: String? = ""
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
var name: String? = ""
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
var topic: String? = ""
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var latestPreviewableEvent: TimelineEventEntity? = null
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
@Index
|
||||||
|
var lastActivityTime: Long? = null
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var heroes: RealmList<String> = RealmList()
|
||||||
|
|
||||||
|
var joinedMembersCount: Int? = 0
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var invitedMembersCount: Int? = 0
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
@Index
|
||||||
|
var isDirect: Boolean = false
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var directUserId: String? = null
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var otherMemberIds: RealmList<String> = RealmList()
|
||||||
|
|
||||||
|
var notificationCount: Int = 0
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var highlightCount: Int = 0
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var readMarkerId: String? = null
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasUnreadMessages: Boolean = false
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
private var tags: RealmList<RoomTagEntity> = RealmList()
|
||||||
|
|
||||||
|
fun tags(): RealmList<RoomTagEntity> = tags
|
||||||
|
|
||||||
|
fun updateTags(newTags: List<Pair<String, Double?>>) {
|
||||||
|
val toDelete = mutableListOf<RoomTagEntity>()
|
||||||
|
tags.forEach { existingTag ->
|
||||||
|
val updatedTag = newTags.firstOrNull { it.first == existingTag.tagName }
|
||||||
|
if (updatedTag == null) {
|
||||||
|
toDelete.add(existingTag)
|
||||||
|
} else {
|
||||||
|
existingTag.tagOrder = updatedTag.second
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toDelete.onEach { it.deleteFromRealm() }
|
||||||
|
newTags.forEach { newTag ->
|
||||||
|
if (tags.indexOfFirst { it.tagName == newTag.first } == -1) {
|
||||||
|
// we must add it
|
||||||
|
tags.add(
|
||||||
|
RoomTagEntity(newTag.first, newTag.second)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
@Index
|
||||||
|
var isFavourite: Boolean = false
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
@Index
|
||||||
|
var isLowPriority: Boolean = false
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
@Index
|
||||||
|
var isServerNotice: Boolean = false
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var userDrafts: UserDraftsEntity? = null
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var breadcrumbsIndex: Int = RoomSummary.NOT_IN_BREADCRUMBS
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var canonicalAlias: String? = null
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var aliases: RealmList<String> = RealmList()
|
||||||
|
|
||||||
|
fun updateAliases(newAliases: List<String>) {
|
||||||
|
// only update underlying field if there is a diff
|
||||||
|
if (newAliases.toSet() != aliases.toSet()) {
|
||||||
|
Timber.w("VAL: aliases updated")
|
||||||
|
aliases.clear()
|
||||||
|
aliases.addAll(newAliases)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is required for querying
|
||||||
|
var flatAliases: String = ""
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var isEncrypted: Boolean = false
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var encryptionEventTs: Long? = 0
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var roomEncryptionTrustLevelStr: String? = null
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var inviterId: String? = null
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasFailedSending: Boolean = false
|
||||||
|
set(value) {
|
||||||
|
if (value != field) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
@Index
|
||||||
private var membershipStr: String = Membership.NONE.name
|
private var membershipStr: String = Membership.NONE.name
|
||||||
|
|
||||||
var membership: Membership
|
var membership: Membership
|
||||||
get() {
|
get() {
|
||||||
return Membership.valueOf(membershipStr)
|
return Membership.valueOf(membershipStr)
|
||||||
}
|
}
|
||||||
set(value) {
|
set(value) {
|
||||||
membershipStr = value.name
|
if (value.name != membershipStr) {
|
||||||
|
membershipStr = value.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Index
|
||||||
private var versioningStateStr: String = VersioningState.NONE.name
|
private var versioningStateStr: String = VersioningState.NONE.name
|
||||||
var versioningState: VersioningState
|
var versioningState: VersioningState
|
||||||
get() {
|
get() {
|
||||||
return VersioningState.valueOf(versioningStateStr)
|
return VersioningState.valueOf(versioningStateStr)
|
||||||
}
|
}
|
||||||
set(value) {
|
set(value) {
|
||||||
versioningStateStr = value.name
|
if (value.name != versioningStateStr) {
|
||||||
|
versioningStateStr = value.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var roomEncryptionTrustLevel: RoomEncryptionTrustLevel?
|
var roomEncryptionTrustLevel: RoomEncryptionTrustLevel?
|
||||||
@@ -84,7 +244,9 @@ internal open class RoomSummaryEntity(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
set(value) {
|
set(value) {
|
||||||
roomEncryptionTrustLevelStr = value?.name
|
if (value?.name != roomEncryptionTrustLevelStr) {
|
||||||
|
roomEncryptionTrustLevelStr = value?.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object
|
companion object
|
||||||
|
@@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.room
|
|||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.Transformations
|
import androidx.lifecycle.Transformations
|
||||||
|
import androidx.paging.PagedList
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
import org.matrix.android.sdk.api.MatrixCallback
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
@@ -45,6 +46,7 @@ import org.matrix.android.sdk.internal.session.room.membership.joining.JoinRoomT
|
|||||||
import org.matrix.android.sdk.internal.session.room.peeking.PeekRoomTask
|
import org.matrix.android.sdk.internal.session.room.peeking.PeekRoomTask
|
||||||
import org.matrix.android.sdk.internal.session.room.peeking.ResolveRoomStateTask
|
import org.matrix.android.sdk.internal.session.room.peeking.ResolveRoomStateTask
|
||||||
import org.matrix.android.sdk.internal.session.room.read.MarkAllRoomsReadTask
|
import org.matrix.android.sdk.internal.session.room.read.MarkAllRoomsReadTask
|
||||||
|
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||||
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
|
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
|
||||||
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateBreadcrumbsTask
|
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateBreadcrumbsTask
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
@@ -96,6 +98,18 @@ internal class DefaultRoomService @Inject constructor(
|
|||||||
return roomSummaryDataSource.getRoomSummariesLive(queryParams)
|
return roomSummaryDataSource.getRoomSummariesLive(queryParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams) : LiveData<PagedList<RoomSummary>> {
|
||||||
|
return roomSummaryDataSource.getSortedPagedRoomSummariesLive(queryParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFilteredPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams) : UpdatableFilterLivePageResult {
|
||||||
|
return roomSummaryDataSource.getFilteredPagedRoomSummariesLive(queryParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getNotificationCountForRooms(queryParams: RoomSummaryQueryParams): RoomAggregateNotificationCount {
|
||||||
|
return roomSummaryDataSource.getNotificationCountForRooms(queryParams)
|
||||||
|
}
|
||||||
|
|
||||||
override fun getBreadcrumbs(queryParams: RoomSummaryQueryParams): List<RoomSummary> {
|
override fun getBreadcrumbs(queryParams: RoomSummaryQueryParams): List<RoomSummary> {
|
||||||
return roomSummaryDataSource.getBreadcrumbs(queryParams)
|
return roomSummaryDataSource.getBreadcrumbs(queryParams)
|
||||||
}
|
}
|
||||||
@@ -178,3 +192,8 @@ internal class DefaultRoomService @Inject constructor(
|
|||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface UpdatableFilterLivePageResult {
|
||||||
|
val livePagedList: LiveData<PagedList<RoomSummary>>
|
||||||
|
fun updateQuery(queryParams: RoomSummaryQueryParams)
|
||||||
|
}
|
||||||
|
@@ -18,10 +18,17 @@ package org.matrix.android.sdk.internal.session.room.summary
|
|||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.Transformations
|
import androidx.lifecycle.Transformations
|
||||||
|
import androidx.paging.LivePagedListBuilder
|
||||||
|
import androidx.paging.PagedList
|
||||||
import com.zhuinden.monarchy.Monarchy
|
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.session.room.RoomSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
|
||||||
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.model.VersioningState
|
import org.matrix.android.sdk.api.session.room.model.VersioningState
|
||||||
|
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.api.util.toOptional
|
import org.matrix.android.sdk.api.util.toOptional
|
||||||
import org.matrix.android.sdk.internal.database.mapper.RoomSummaryMapper
|
import org.matrix.android.sdk.internal.database.mapper.RoomSummaryMapper
|
||||||
@@ -31,9 +38,8 @@ import org.matrix.android.sdk.internal.database.query.findByAlias
|
|||||||
import org.matrix.android.sdk.internal.database.query.where
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.query.process
|
import org.matrix.android.sdk.internal.query.process
|
||||||
|
import org.matrix.android.sdk.internal.session.room.UpdatableFilterLivePageResult
|
||||||
import org.matrix.android.sdk.internal.util.fetchCopyMap
|
import org.matrix.android.sdk.internal.util.fetchCopyMap
|
||||||
import io.realm.Realm
|
|
||||||
import io.realm.RealmQuery
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
|
internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
|
||||||
@@ -98,6 +104,71 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat
|
|||||||
.sort(RoomSummaryEntityFields.BREADCRUMBS_INDEX)
|
.sort(RoomSummaryEntityFields.BREADCRUMBS_INDEX)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getSortedPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams): LiveData<PagedList<RoomSummary>> {
|
||||||
|
val realmDataSourceFactory = monarchy.createDataSourceFactory { realm ->
|
||||||
|
roomSummariesQuery(realm, queryParams)
|
||||||
|
.sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING)
|
||||||
|
}
|
||||||
|
val dataSourceFactory = realmDataSourceFactory.map {
|
||||||
|
roomSummaryMapper.map(it)
|
||||||
|
}
|
||||||
|
return monarchy.findAllPagedWithChanges(realmDataSourceFactory,
|
||||||
|
LivePagedListBuilder(dataSourceFactory,
|
||||||
|
PagedList.Config.Builder()
|
||||||
|
.setPageSize(10)
|
||||||
|
.setInitialLoadSizeHint(20)
|
||||||
|
.setEnablePlaceholders(false)
|
||||||
|
.setPrefetchDistance(10)
|
||||||
|
.build())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFilteredPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams): UpdatableFilterLivePageResult {
|
||||||
|
val realmDataSourceFactory = monarchy.createDataSourceFactory { realm ->
|
||||||
|
roomSummariesQuery(realm, queryParams)
|
||||||
|
.sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING)
|
||||||
|
}
|
||||||
|
val dataSourceFactory = realmDataSourceFactory.map {
|
||||||
|
roomSummaryMapper.map(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
val mapped = monarchy.findAllPagedWithChanges(realmDataSourceFactory,
|
||||||
|
LivePagedListBuilder(dataSourceFactory,
|
||||||
|
PagedList.Config.Builder()
|
||||||
|
.setPageSize(10)
|
||||||
|
.setInitialLoadSizeHint(20)
|
||||||
|
.setEnablePlaceholders(false)
|
||||||
|
.setPrefetchDistance(10)
|
||||||
|
.build())
|
||||||
|
)
|
||||||
|
|
||||||
|
return object : UpdatableFilterLivePageResult {
|
||||||
|
override val livePagedList: LiveData<PagedList<RoomSummary>>
|
||||||
|
get() = mapped
|
||||||
|
|
||||||
|
override fun updateQuery(queryParams: RoomSummaryQueryParams) {
|
||||||
|
realmDataSourceFactory.updateQuery {
|
||||||
|
roomSummariesQuery(it, queryParams)
|
||||||
|
.sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getNotificationCountForRooms(queryParams: RoomSummaryQueryParams): RoomAggregateNotificationCount {
|
||||||
|
var notificationCount: RoomAggregateNotificationCount? = null
|
||||||
|
monarchy.doWithRealm { realm ->
|
||||||
|
val roomSummariesQuery = roomSummariesQuery(realm, queryParams)
|
||||||
|
val notifCount = roomSummariesQuery.sum(RoomSummaryEntityFields.NOTIFICATION_COUNT).toInt()
|
||||||
|
val highlightCount = roomSummariesQuery.sum(RoomSummaryEntityFields.HIGHLIGHT_COUNT).toInt()
|
||||||
|
notificationCount = RoomAggregateNotificationCount(
|
||||||
|
notifCount,
|
||||||
|
highlightCount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return notificationCount!!
|
||||||
|
}
|
||||||
|
|
||||||
private fun roomSummariesQuery(realm: Realm, queryParams: RoomSummaryQueryParams): RealmQuery<RoomSummaryEntity> {
|
private fun roomSummariesQuery(realm: Realm, queryParams: RoomSummaryQueryParams): RealmQuery<RoomSummaryEntity> {
|
||||||
val query = RoomSummaryEntity.where(realm)
|
val query = RoomSummaryEntity.where(realm)
|
||||||
query.process(RoomSummaryEntityFields.ROOM_ID, queryParams.roomId)
|
query.process(RoomSummaryEntityFields.ROOM_ID, queryParams.roomId)
|
||||||
@@ -105,6 +176,27 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat
|
|||||||
query.process(RoomSummaryEntityFields.CANONICAL_ALIAS, queryParams.canonicalAlias)
|
query.process(RoomSummaryEntityFields.CANONICAL_ALIAS, queryParams.canonicalAlias)
|
||||||
query.process(RoomSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships)
|
query.process(RoomSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships)
|
||||||
query.notEqualTo(RoomSummaryEntityFields.VERSIONING_STATE_STR, VersioningState.UPGRADED_ROOM_JOINED.name)
|
query.notEqualTo(RoomSummaryEntityFields.VERSIONING_STATE_STR, VersioningState.UPGRADED_ROOM_JOINED.name)
|
||||||
|
|
||||||
|
queryParams.roomCategoryFilter?.let {
|
||||||
|
when (it) {
|
||||||
|
RoomCategoryFilter.ONLY_DM -> query.equalTo(RoomSummaryEntityFields.IS_DIRECT, true)
|
||||||
|
RoomCategoryFilter.ONLY_ROOMS -> query.equalTo(RoomSummaryEntityFields.IS_DIRECT, false)
|
||||||
|
RoomCategoryFilter.ALL -> {
|
||||||
|
// nop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
queryParams.roomTagQueryFilter?.let {
|
||||||
|
it.isFavorite?.let { fav ->
|
||||||
|
query.equalTo(RoomSummaryEntityFields.IS_FAVOURITE, fav)
|
||||||
|
}
|
||||||
|
it.isLowPriority?.let { lp ->
|
||||||
|
query.equalTo(RoomSummaryEntityFields.IS_LOW_PRIORITY, lp)
|
||||||
|
}
|
||||||
|
it.isServerNotice?.let { lp ->
|
||||||
|
query.equalTo(RoomSummaryEntityFields.IS_SERVER_NOTICE, lp)
|
||||||
|
}
|
||||||
|
}
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -98,6 +98,11 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||||||
|
|
||||||
val latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
|
val latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
|
||||||
|
|
||||||
|
val lastActivityFromEvent = latestPreviewableEvent?.root?.originServerTs
|
||||||
|
if (lastActivityFromEvent != null) {
|
||||||
|
roomSummaryEntity.lastActivityTime = lastActivityFromEvent
|
||||||
|
}
|
||||||
|
|
||||||
roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0
|
roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0
|
||||||
// avoid this call if we are sure there are unread events
|
// avoid this call if we are sure there are unread events
|
||||||
|| !isEventRead(realm.configuration, userId, roomId, latestPreviewableEvent?.eventId)
|
|| !isEventRead(realm.configuration, userId, roomId, latestPreviewableEvent?.eventId)
|
||||||
@@ -112,8 +117,9 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||||||
|
|
||||||
val roomAliases = ContentMapper.map(lastAliasesEvent?.content).toModel<RoomAliasesContent>()?.aliases
|
val roomAliases = ContentMapper.map(lastAliasesEvent?.content).toModel<RoomAliasesContent>()?.aliases
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
roomSummaryEntity.aliases.clear()
|
// roomSummaryEntity.aliases.clear()
|
||||||
roomSummaryEntity.aliases.addAll(roomAliases)
|
// roomSummaryEntity.aliases.addAll(roomAliases)
|
||||||
|
roomSummaryEntity.updateAliases(roomAliases)
|
||||||
roomSummaryEntity.flatAliases = roomAliases.joinToString(separator = "|", prefix = "|")
|
roomSummaryEntity.flatAliases = roomAliases.joinToString(separator = "|", prefix = "|")
|
||||||
roomSummaryEntity.isEncrypted = encryptionEvent != null
|
roomSummaryEntity.isEncrypted = encryptionEvent != null
|
||||||
roomSummaryEntity.encryptionEventTs = encryptionEvent?.originServerTs
|
roomSummaryEntity.encryptionEventTs = encryptionEvent?.originServerTs
|
||||||
|
@@ -19,8 +19,8 @@ package org.matrix.android.sdk.internal.session.sync
|
|||||||
import org.matrix.android.sdk.api.session.room.model.tag.RoomTagContent
|
import org.matrix.android.sdk.api.session.room.model.tag.RoomTagContent
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomTagEntity
|
import org.matrix.android.sdk.internal.database.model.RoomTagEntity
|
||||||
import org.matrix.android.sdk.internal.database.query.where
|
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
|
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class RoomTagHandler @Inject constructor() {
|
internal class RoomTagHandler @Inject constructor() {
|
||||||
@@ -31,12 +31,8 @@ internal class RoomTagHandler @Inject constructor() {
|
|||||||
}
|
}
|
||||||
val tags = content.tags.entries.map { (tagName, params) ->
|
val tags = content.tags.entries.map { (tagName, params) ->
|
||||||
RoomTagEntity(tagName, params["order"] as? Double)
|
RoomTagEntity(tagName, params["order"] as? Double)
|
||||||
|
Pair(tagName, params["order"] as? Double)
|
||||||
}
|
}
|
||||||
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
|
RoomSummaryEntity.getOrCreate(realm, roomId).updateTags(tags)
|
||||||
?: RoomSummaryEntity(roomId)
|
|
||||||
|
|
||||||
roomSummaryEntity.tags.clear()
|
|
||||||
roomSummaryEntity.tags.addAll(tags)
|
|
||||||
realm.insertOrUpdate(roomSummaryEntity)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -52,7 +52,7 @@ class AppStateHandler @Inject constructor(
|
|||||||
|
|
||||||
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
||||||
fun entersForeground() {
|
fun entersForeground() {
|
||||||
observeRoomsAndGroup()
|
// observeRoomsAndGroup()
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
|
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
|
||||||
|
@@ -127,6 +127,11 @@ abstract class VectorBaseFragment<VB: ViewBinding> : BaseMvRxFragment(), HasScre
|
|||||||
Timber.i("onResume Fragment ${javaClass.simpleName}")
|
Timber.i("onResume Fragment ${javaClass.simpleName}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
Timber.i("onPause Fragment ${javaClass.simpleName}")
|
||||||
|
}
|
||||||
|
|
||||||
@CallSuper
|
@CallSuper
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
@@ -150,6 +155,7 @@ abstract class VectorBaseFragment<VB: ViewBinding> : BaseMvRxFragment(), HasScre
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
Timber.i("onDestroy Fragment ${javaClass.simpleName}")
|
||||||
uiDisposables.dispose()
|
uiDisposables.dispose()
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
@@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.session.room.notification.RoomNotificationStat
|
|||||||
|
|
||||||
sealed class RoomListAction : VectorViewModelAction {
|
sealed class RoomListAction : VectorViewModelAction {
|
||||||
data class SelectRoom(val roomSummary: RoomSummary) : RoomListAction()
|
data class SelectRoom(val roomSummary: RoomSummary) : RoomListAction()
|
||||||
data class ToggleCategory(val category: RoomCategory) : RoomListAction()
|
data class ToggleSection(val section: RoomListViewModel.RoomsSection) : RoomListAction()
|
||||||
data class AcceptInvitation(val roomSummary: RoomSummary) : RoomListAction()
|
data class AcceptInvitation(val roomSummary: RoomSummary) : RoomListAction()
|
||||||
data class RejectInvitation(val roomSummary: RoomSummary) : RoomListAction()
|
data class RejectInvitation(val roomSummary: RoomSummary) : RoomListAction()
|
||||||
data class FilterWith(val filter: String) : RoomListAction()
|
data class FilterWith(val filter: String) : RoomListAction()
|
||||||
|
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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 im.vector.app.R
|
||||||
|
import im.vector.app.core.epoxy.helpFooterItem
|
||||||
|
import im.vector.app.core.resources.StringProvider
|
||||||
|
import im.vector.app.core.resources.UserPreferencesProvider
|
||||||
|
import im.vector.app.features.home.RoomListDisplayMode
|
||||||
|
import im.vector.app.features.home.room.filtered.filteredRoomFooterItem
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class RoomListFooterController @Inject constructor(
|
||||||
|
private val stringProvider: StringProvider,
|
||||||
|
private val userPreferencesProvider: UserPreferencesProvider
|
||||||
|
) : TypedEpoxyController<RoomListViewState>() {
|
||||||
|
|
||||||
|
var listener: RoomListListener? = null
|
||||||
|
|
||||||
|
override fun buildModels(data: RoomListViewState?) {
|
||||||
|
when (data?.displayMode) {
|
||||||
|
RoomListDisplayMode.FILTERED -> {
|
||||||
|
filteredRoomFooterItem {
|
||||||
|
id("filter_footer")
|
||||||
|
listener(listener)
|
||||||
|
currentFilter(data.roomFilter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
if (userPreferencesProvider.shouldShowLongClickOnRoomHelp()) {
|
||||||
|
helpFooterItem {
|
||||||
|
id("long_click_help")
|
||||||
|
text(stringProvider.getString(R.string.help_long_click_on_room_for_more_options))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -27,12 +27,10 @@ import android.view.ViewGroup
|
|||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
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.OnModelBuildFinishedListener
|
import com.airbnb.epoxy.OnModelBuildFinishedListener
|
||||||
import com.airbnb.mvrx.Fail
|
|
||||||
import com.airbnb.mvrx.Incomplete
|
|
||||||
import com.airbnb.mvrx.Success
|
|
||||||
import com.airbnb.mvrx.args
|
import com.airbnb.mvrx.args
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
@@ -44,6 +42,7 @@ import im.vector.app.core.extensions.exhaustive
|
|||||||
import im.vector.app.core.platform.OnBackPressed
|
import im.vector.app.core.platform.OnBackPressed
|
||||||
import im.vector.app.core.platform.StateView
|
import im.vector.app.core.platform.StateView
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
|
import im.vector.app.core.resources.UserPreferencesProvider
|
||||||
import im.vector.app.databinding.FragmentRoomListBinding
|
import im.vector.app.databinding.FragmentRoomListBinding
|
||||||
import im.vector.app.features.home.RoomListDisplayMode
|
import im.vector.app.features.home.RoomListDisplayMode
|
||||||
import im.vector.app.features.home.room.list.actions.RoomListActionsArgs
|
import im.vector.app.features.home.room.list.actions.RoomListActionsArgs
|
||||||
@@ -53,8 +52,7 @@ import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedA
|
|||||||
import im.vector.app.features.home.room.list.widget.NotifsFabMenuView
|
import im.vector.app.features.home.room.list.widget.NotifsFabMenuView
|
||||||
import im.vector.app.features.notifications.NotificationDrawerManager
|
import im.vector.app.features.notifications.NotificationDrawerManager
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
import org.matrix.android.sdk.api.extensions.orTrue
|
||||||
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.RoomSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
||||||
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
||||||
@@ -66,12 +64,13 @@ data class RoomListParams(
|
|||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
class RoomListFragment @Inject constructor(
|
class RoomListFragment @Inject constructor(
|
||||||
private val roomController: RoomSummaryController,
|
private val pagedControllerFactory: RoomSummaryPagedControllerFactory,
|
||||||
val roomListViewModelFactory: RoomListViewModel.Factory,
|
val roomListViewModelFactory: RoomListViewModel.Factory,
|
||||||
private val notificationDrawerManager: NotificationDrawerManager,
|
private val notificationDrawerManager: NotificationDrawerManager,
|
||||||
private val sharedViewPool: RecyclerView.RecycledViewPool
|
private val footerController: RoomListFooterController,
|
||||||
|
private val userPreferencesProvider: UserPreferencesProvider
|
||||||
) : VectorBaseFragment<FragmentRoomListBinding>(),
|
) : VectorBaseFragment<FragmentRoomListBinding>(),
|
||||||
RoomSummaryController.Listener,
|
RoomListListener,
|
||||||
OnBackPressed,
|
OnBackPressed,
|
||||||
NotifsFabMenuView.Listener {
|
NotifsFabMenuView.Listener {
|
||||||
|
|
||||||
@@ -87,6 +86,20 @@ class RoomListFragment @Inject constructor(
|
|||||||
|
|
||||||
private var hasUnreadRooms = false
|
private var hasUnreadRooms = false
|
||||||
|
|
||||||
|
data class SectionKey(
|
||||||
|
val name: String,
|
||||||
|
val isExpanded: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SectionAdapterInfo(
|
||||||
|
var section: SectionKey,
|
||||||
|
val headerHeaderAdapter: SectionHeaderAdapter,
|
||||||
|
val contentAdapter: RoomSummaryPagedController
|
||||||
|
)
|
||||||
|
|
||||||
|
private val adapterInfosList = mutableListOf<SectionAdapterInfo>()
|
||||||
|
private var concatAdapter: ConcatAdapter? = null
|
||||||
|
|
||||||
override fun getMenuRes() = R.menu.room_list
|
override fun getMenuRes() = R.menu.room_list
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
@@ -112,10 +125,10 @@ class RoomListFragment @Inject constructor(
|
|||||||
sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java)
|
sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java)
|
||||||
roomListViewModel.observeViewEvents {
|
roomListViewModel.observeViewEvents {
|
||||||
when (it) {
|
when (it) {
|
||||||
is RoomListViewEvents.Loading -> showLoading(it.message)
|
is RoomListViewEvents.Loading -> showLoading(it.message)
|
||||||
is RoomListViewEvents.Failure -> showFailure(it.throwable)
|
is RoomListViewEvents.Failure -> showFailure(it.throwable)
|
||||||
is RoomListViewEvents.SelectRoom -> handleSelectRoom(it)
|
is RoomListViewEvents.SelectRoom -> handleSelectRoom(it)
|
||||||
is RoomListViewEvents.Done -> Unit
|
is RoomListViewEvents.Done -> Unit
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,15 +140,49 @@ class RoomListFragment @Inject constructor(
|
|||||||
.disposeOnDestroyView()
|
.disposeOnDestroyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun refreshCollapseStates() {
|
||||||
|
var contentInsertIndex = 1
|
||||||
|
roomListViewModel.sections.forEachIndexed { index, roomsSection ->
|
||||||
|
val actualBlock = adapterInfosList[index]
|
||||||
|
val isRoomSectionExpanded = roomsSection.isExpanded.value.orTrue()
|
||||||
|
if (actualBlock.section.isExpanded && !isRoomSectionExpanded) {
|
||||||
|
// we have to remove the content adapter
|
||||||
|
concatAdapter?.removeAdapter(actualBlock.contentAdapter.adapter)
|
||||||
|
} else if (!actualBlock.section.isExpanded && isRoomSectionExpanded) {
|
||||||
|
// we must add it back!
|
||||||
|
concatAdapter?.addAdapter(contentInsertIndex, actualBlock.contentAdapter.adapter)
|
||||||
|
}
|
||||||
|
contentInsertIndex = if (isRoomSectionExpanded) {
|
||||||
|
contentInsertIndex + 2
|
||||||
|
} else {
|
||||||
|
contentInsertIndex + 1
|
||||||
|
}
|
||||||
|
actualBlock.section = actualBlock.section.copy(
|
||||||
|
isExpanded = isRoomSectionExpanded
|
||||||
|
)
|
||||||
|
actualBlock.headerHeaderAdapter.updateSection(
|
||||||
|
actualBlock.headerHeaderAdapter.section.copy(isExpanded = isRoomSectionExpanded)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun showFailure(throwable: Throwable) {
|
override fun showFailure(throwable: Throwable) {
|
||||||
showErrorInSnackbar(throwable)
|
showErrorInSnackbar(throwable)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
roomController.removeModelBuildListener(modelBuildListener)
|
adapterInfosList.onEach { it.contentAdapter.removeModelBuildListener(modelBuildListener) }
|
||||||
|
adapterInfosList.clear()
|
||||||
|
// roomController.removeModelBuildListener(modelBuildListener)
|
||||||
modelBuildListener = null
|
modelBuildListener = null
|
||||||
views.roomListView.cleanup()
|
views.roomListView.cleanup()
|
||||||
roomController.listener = null
|
// controllers.onEach {
|
||||||
|
// it.listener = null
|
||||||
|
// // concatAdapter.removeAdapter(it.adapter)
|
||||||
|
// }
|
||||||
|
// controllers.clear()
|
||||||
|
// roomController.listener = null
|
||||||
|
// favRoomController.listener = null
|
||||||
stateRestorer.clear()
|
stateRestorer.clear()
|
||||||
views.createChatFabMenu.listener = null
|
views.createChatFabMenu.listener = null
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
@@ -148,8 +195,8 @@ class RoomListFragment @Inject constructor(
|
|||||||
private fun setupCreateRoomButton() {
|
private fun setupCreateRoomButton() {
|
||||||
when (roomListParams.displayMode) {
|
when (roomListParams.displayMode) {
|
||||||
RoomListDisplayMode.NOTIFICATIONS -> views.createChatFabMenu.isVisible = true
|
RoomListDisplayMode.NOTIFICATIONS -> views.createChatFabMenu.isVisible = true
|
||||||
RoomListDisplayMode.PEOPLE -> views.createChatRoomButton.isVisible = true
|
RoomListDisplayMode.PEOPLE -> views.createChatRoomButton.isVisible = true
|
||||||
RoomListDisplayMode.ROOMS -> views.createGroupRoomButton.isVisible = true
|
RoomListDisplayMode.ROOMS -> views.createGroupRoomButton.isVisible = true
|
||||||
else -> Unit // No button in this mode
|
else -> Unit // No button in this mode
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,21 +251,68 @@ class RoomListFragment @Inject constructor(
|
|||||||
stateRestorer = LayoutManagerStateRestorer(layoutManager).register()
|
stateRestorer = LayoutManagerStateRestorer(layoutManager).register()
|
||||||
views.roomListView.layoutManager = layoutManager
|
views.roomListView.layoutManager = layoutManager
|
||||||
views.roomListView.itemAnimator = RoomListAnimator()
|
views.roomListView.itemAnimator = RoomListAnimator()
|
||||||
views.roomListView.setRecycledViewPool(sharedViewPool)
|
// views.roomListView.setRecycledViewPool(sharedViewPool)
|
||||||
layoutManager.recycleChildrenOnDetach = true
|
layoutManager.recycleChildrenOnDetach = true
|
||||||
roomController.listener = this
|
|
||||||
modelBuildListener = OnModelBuildFinishedListener { it.dispatchTo(stateRestorer) }
|
modelBuildListener = OnModelBuildFinishedListener { it.dispatchTo(stateRestorer) }
|
||||||
roomController.addModelBuildListener(modelBuildListener)
|
|
||||||
views.roomListView.adapter = roomController.adapter
|
val concatAdapter = ConcatAdapter()
|
||||||
views.stateView.contentView = views.roomListView
|
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
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
val contentAdapter = pagedControllerFactory.createRoomSummaryPagedController().also {
|
||||||
|
section.livePages.observe(viewLifecycleOwner) { pl ->
|
||||||
|
it.submitList(pl)
|
||||||
|
sectionAdapter.updateSection(sectionAdapter.section.copy(isHidden = pl.isEmpty() || hasOnlyOneSection))
|
||||||
|
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
|
||||||
|
}
|
||||||
|
adapterInfosList.add(
|
||||||
|
SectionAdapterInfo(
|
||||||
|
SectionKey(
|
||||||
|
name = section.sectionName,
|
||||||
|
isExpanded = section.isExpanded.value.orTrue()
|
||||||
|
),
|
||||||
|
sectionAdapter,
|
||||||
|
contentAdapter
|
||||||
|
)
|
||||||
|
)
|
||||||
|
concatAdapter.addAdapter(sectionAdapter)
|
||||||
|
concatAdapter.addAdapter(contentAdapter.adapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the footer controller
|
||||||
|
this.footerController.listener = this
|
||||||
|
concatAdapter.addAdapter(footerController.adapter)
|
||||||
|
|
||||||
|
this.concatAdapter = concatAdapter
|
||||||
|
views.roomListView.adapter = concatAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
private val showFabRunnable = Runnable {
|
private val showFabRunnable = Runnable {
|
||||||
if (isAdded) {
|
if (isAdded) {
|
||||||
when (roomListParams.displayMode) {
|
when (roomListParams.displayMode) {
|
||||||
RoomListDisplayMode.NOTIFICATIONS -> views.createChatFabMenu.show()
|
RoomListDisplayMode.NOTIFICATIONS -> views.createChatFabMenu.show()
|
||||||
RoomListDisplayMode.PEOPLE -> views.createChatRoomButton.show()
|
RoomListDisplayMode.PEOPLE -> views.createChatRoomButton.show()
|
||||||
RoomListDisplayMode.ROOMS -> views.createGroupRoomButton.show()
|
RoomListDisplayMode.ROOMS -> views.createGroupRoomButton.show()
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,28 +320,28 @@ class RoomListFragment @Inject constructor(
|
|||||||
|
|
||||||
private fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) {
|
private fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) {
|
||||||
when (quickAction) {
|
when (quickAction) {
|
||||||
is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> {
|
is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> {
|
||||||
roomListViewModel.handle(RoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES_NOISY))
|
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))
|
roomListViewModel.handle(RoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES))
|
||||||
}
|
}
|
||||||
is RoomListQuickActionsSharedAction.NotificationsMentionsOnly -> {
|
is RoomListQuickActionsSharedAction.NotificationsMentionsOnly -> {
|
||||||
roomListViewModel.handle(RoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MENTIONS_ONLY))
|
roomListViewModel.handle(RoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MENTIONS_ONLY))
|
||||||
}
|
}
|
||||||
is RoomListQuickActionsSharedAction.NotificationsMute -> {
|
is RoomListQuickActionsSharedAction.NotificationsMute -> {
|
||||||
roomListViewModel.handle(RoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MUTE))
|
roomListViewModel.handle(RoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.MUTE))
|
||||||
}
|
}
|
||||||
is RoomListQuickActionsSharedAction.Settings -> {
|
is RoomListQuickActionsSharedAction.Settings -> {
|
||||||
navigator.openRoomProfile(requireActivity(), quickAction.roomId)
|
navigator.openRoomProfile(requireActivity(), quickAction.roomId)
|
||||||
}
|
}
|
||||||
is RoomListQuickActionsSharedAction.Favorite -> {
|
is RoomListQuickActionsSharedAction.Favorite -> {
|
||||||
roomListViewModel.handle(RoomListAction.ToggleTag(quickAction.roomId, RoomTag.ROOM_TAG_FAVOURITE))
|
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))
|
roomListViewModel.handle(RoomListAction.ToggleTag(quickAction.roomId, RoomTag.ROOM_TAG_LOW_PRIORITY))
|
||||||
}
|
}
|
||||||
is RoomListQuickActionsSharedAction.Leave -> {
|
is RoomListQuickActionsSharedAction.Leave -> {
|
||||||
promptLeaveRoom(quickAction.roomId)
|
promptLeaveRoom(quickAction.roomId)
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
@@ -278,12 +372,7 @@ class RoomListFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(roomListViewModel) { state ->
|
override fun invalidate() = withState(roomListViewModel) { state ->
|
||||||
when (state.asyncFilteredRooms) {
|
footerController.setData(state)
|
||||||
is Incomplete -> renderLoading()
|
|
||||||
is Success -> renderSuccess(state)
|
|
||||||
is Fail -> renderFailure(state.asyncFilteredRooms.error)
|
|
||||||
}
|
|
||||||
roomController.update(state)
|
|
||||||
// Mark all as read menu
|
// Mark all as read menu
|
||||||
when (roomListParams.displayMode) {
|
when (roomListParams.displayMode) {
|
||||||
RoomListDisplayMode.NOTIFICATIONS,
|
RoomListDisplayMode.NOTIFICATIONS,
|
||||||
@@ -299,68 +388,38 @@ class RoomListFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderSuccess(state: RoomListViewState) {
|
private fun checkEmptyState() {
|
||||||
val allRooms = state.asyncRooms()
|
val hasNoRoom = adapterInfosList.all { it.headerHeaderAdapter.section.isHidden }
|
||||||
val filteredRooms = state.asyncFilteredRooms()
|
if (hasNoRoom) {
|
||||||
if (filteredRooms.isNullOrEmpty()) {
|
val emptyState = when (roomListParams.displayMode) {
|
||||||
renderEmptyState(allRooms)
|
RoomListDisplayMode.NOTIFICATIONS -> {
|
||||||
} else {
|
|
||||||
views.stateView.state = StateView.State.Content
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun renderEmptyState(allRooms: List<RoomSummary>?) {
|
|
||||||
val hasNoRoom = allRooms
|
|
||||||
?.filter {
|
|
||||||
it.membership == Membership.JOIN || it.membership == Membership.INVITE
|
|
||||||
}
|
|
||||||
.isNullOrEmpty()
|
|
||||||
val emptyState = when (roomListParams.displayMode) {
|
|
||||||
RoomListDisplayMode.NOTIFICATIONS -> {
|
|
||||||
if (hasNoRoom) {
|
|
||||||
StateView.State.Empty(
|
|
||||||
title = getString(R.string.room_list_catchup_welcome_title),
|
|
||||||
image = ContextCompat.getDrawable(requireContext(), R.drawable.ic_home_bottom_catchup),
|
|
||||||
message = getString(R.string.room_list_catchup_welcome_body)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
StateView.State.Empty(
|
StateView.State.Empty(
|
||||||
title = getString(R.string.room_list_catchup_empty_title),
|
title = getString(R.string.room_list_catchup_empty_title),
|
||||||
image = ContextCompat.getDrawable(requireContext(), R.drawable.ic_noun_party_popper),
|
image = ContextCompat.getDrawable(requireContext(), R.drawable.ic_noun_party_popper),
|
||||||
message = getString(R.string.room_list_catchup_empty_body))
|
message = getString(R.string.room_list_catchup_empty_body))
|
||||||
}
|
}
|
||||||
|
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 ->
|
||||||
|
StateView.State.Empty(
|
||||||
|
title = getString(R.string.room_list_rooms_empty_title),
|
||||||
|
image = ContextCompat.getDrawable(requireContext(), R.drawable.empty_state_room),
|
||||||
|
isBigImage = true,
|
||||||
|
message = getString(R.string.room_list_rooms_empty_body)
|
||||||
|
)
|
||||||
|
else ->
|
||||||
|
// Always display the content in this mode, because if the footer
|
||||||
|
StateView.State.Content
|
||||||
}
|
}
|
||||||
RoomListDisplayMode.PEOPLE ->
|
views.stateView.state = emptyState
|
||||||
StateView.State.Empty(
|
} else {
|
||||||
title = getString(R.string.room_list_people_empty_title),
|
views.stateView.state = StateView.State.Content
|
||||||
image = ContextCompat.getDrawable(requireContext(), R.drawable.empty_state_dm),
|
|
||||||
isBigImage = true,
|
|
||||||
message = getString(R.string.room_list_people_empty_body)
|
|
||||||
)
|
|
||||||
RoomListDisplayMode.ROOMS ->
|
|
||||||
StateView.State.Empty(
|
|
||||||
title = getString(R.string.room_list_rooms_empty_title),
|
|
||||||
image = ContextCompat.getDrawable(requireContext(), R.drawable.empty_state_room),
|
|
||||||
isBigImage = true,
|
|
||||||
message = getString(R.string.room_list_rooms_empty_body)
|
|
||||||
)
|
|
||||||
else ->
|
|
||||||
// Always display the content in this mode, because if the footer
|
|
||||||
StateView.State.Content
|
|
||||||
}
|
}
|
||||||
views.stateView.state = emptyState
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun renderLoading() {
|
|
||||||
views.stateView.state = StateView.State.Loading
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun renderFailure(error: Throwable) {
|
|
||||||
val message = when (error) {
|
|
||||||
is Failure.NetworkConnection -> getString(R.string.network_error_please_check_and_retry)
|
|
||||||
else -> getString(R.string.unknown_error)
|
|
||||||
}
|
|
||||||
views.stateView.state = StateView.State.Error(message)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed(toolbarButton: Boolean): Boolean {
|
override fun onBackPressed(toolbarButton: Boolean): Boolean {
|
||||||
@@ -377,7 +436,11 @@ class RoomListFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onRoomLongClicked(room: RoomSummary): Boolean {
|
override fun onRoomLongClicked(room: RoomSummary): Boolean {
|
||||||
roomController.onRoomLongClicked()
|
userPreferencesProvider.neverShowLongClickOnRoomHelpAgain()
|
||||||
|
withState(roomListViewModel) {
|
||||||
|
// refresh footer
|
||||||
|
footerController.setData(it)
|
||||||
|
}
|
||||||
RoomListQuickActionsBottomSheet
|
RoomListQuickActionsBottomSheet
|
||||||
.newInstance(room.roomId, RoomListActionsArgs.Mode.FULL)
|
.newInstance(room.roomId, RoomListActionsArgs.Mode.FULL)
|
||||||
.show(childFragmentManager, "ROOM_LIST_QUICK_ACTIONS")
|
.show(childFragmentManager, "ROOM_LIST_QUICK_ACTIONS")
|
||||||
@@ -394,10 +457,6 @@ class RoomListFragment @Inject constructor(
|
|||||||
roomListViewModel.handle(RoomListAction.RejectInvitation(room))
|
roomListViewModel.handle(RoomListAction.RejectInvitation(room))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onToggleRoomCategory(roomCategory: RoomCategory) {
|
|
||||||
roomListViewModel.handle(RoomListAction.ToggleCategory(roomCategory))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createRoom(initialName: String) {
|
override fun createRoom(initialName: String) {
|
||||||
navigator.openCreateRoom(requireActivity(), initialName)
|
navigator.openCreateRoom(requireActivity(), initialName)
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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 im.vector.app.features.home.room.filtered.FilteredRoomFooterItem
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
|
||||||
|
interface RoomListListener : FilteredRoomFooterItem.FilteredRoomFooterItemListener {
|
||||||
|
fun onRoomClicked(room: RoomSummary)
|
||||||
|
fun onRoomLongClicked(room: RoomSummary): Boolean
|
||||||
|
fun onRejectRoomInvitation(room: RoomSummary)
|
||||||
|
fun onAcceptRoomInvitation(room: RoomSummary)
|
||||||
|
}
|
@@ -16,37 +16,50 @@
|
|||||||
|
|
||||||
package im.vector.app.features.home.room.list
|
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.lifecycle.viewModelScope
|
||||||
|
import androidx.paging.PagedList
|
||||||
import com.airbnb.mvrx.FragmentViewModelContext
|
import com.airbnb.mvrx.FragmentViewModelContext
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.exhaustive
|
import im.vector.app.core.extensions.exhaustive
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
import im.vector.app.core.utils.DataSource
|
import im.vector.app.core.resources.StringProvider
|
||||||
|
import im.vector.app.features.home.RoomListDisplayMode
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.NoOpMatrixCallback
|
|
||||||
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.RoomTagQueryFilter
|
||||||
import org.matrix.android.sdk.api.session.Session
|
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.model.Membership
|
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.RoomSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
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.state.isPublic
|
||||||
import org.matrix.android.sdk.rx.rx
|
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||||
|
import org.matrix.android.sdk.internal.session.room.UpdatableFilterLivePageResult
|
||||||
|
import org.matrix.android.sdk.rx.asObservable
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.lang.Exception
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
|
class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
|
||||||
private val session: Session,
|
private val session: Session,
|
||||||
private val roomSummariesSource: DataSource<List<RoomSummary>>)
|
private val stringProvider: StringProvider)
|
||||||
: VectorViewModel<RoomListViewState, RoomListAction, RoomListViewEvents>(initialState) {
|
: VectorViewModel<RoomListViewState, RoomListAction, RoomListViewEvents>(initialState) {
|
||||||
|
|
||||||
interface Factory {
|
interface Factory {
|
||||||
fun create(initialState: RoomListViewState): RoomListViewModel
|
fun create(initialState: RoomListViewState): RoomListViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var updatableQuery: UpdatableFilterLivePageResult? = null
|
||||||
|
|
||||||
companion object : MvRxViewModelFactory<RoomListViewModel, RoomListViewState> {
|
companion object : MvRxViewModelFactory<RoomListViewModel, RoomListViewState> {
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@@ -56,28 +69,121 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val displayMode = initialState.displayMode
|
data class RoomsSection(
|
||||||
private val roomListDisplayModeFilter = RoomListDisplayModeFilter(displayMode)
|
val sectionName: String,
|
||||||
|
val livePages: LiveData<PagedList<RoomSummary>>,
|
||||||
|
val isExpanded: MutableLiveData<Boolean> = MutableLiveData(true),
|
||||||
|
val notificationCount: MutableLiveData<RoomAggregateNotificationCount> =
|
||||||
|
MutableLiveData(RoomAggregateNotificationCount(0, 0))
|
||||||
|
)
|
||||||
|
|
||||||
|
val sections: List<RoomsSection> by lazy {
|
||||||
|
val sections = mutableListOf<RoomsSection>()
|
||||||
|
if (initialState.displayMode == RoomListDisplayMode.PEOPLE) {
|
||||||
|
|
||||||
|
addSection(sections, R.string.invitations_header) {
|
||||||
|
it.memberships = listOf(Membership.INVITE)
|
||||||
|
it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM
|
||||||
|
}
|
||||||
|
|
||||||
|
addSection(sections, R.string.bottom_action_people_x) {
|
||||||
|
it.memberships = listOf(Membership.JOIN)
|
||||||
|
it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM
|
||||||
|
}
|
||||||
|
} else if (initialState.displayMode == RoomListDisplayMode.ROOMS) {
|
||||||
|
|
||||||
|
addSection(sections, R.string.invitations_header) {
|
||||||
|
it.memberships = listOf(Membership.INVITE)
|
||||||
|
it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
|
||||||
|
}
|
||||||
|
|
||||||
|
addSection(sections, R.string.bottom_action_favourites) {
|
||||||
|
it.memberships = listOf(Membership.JOIN)
|
||||||
|
it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
|
||||||
|
it.roomTagQueryFilter = RoomTagQueryFilter(true, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
addSection(sections, R.string.bottom_action_rooms) {
|
||||||
|
it.memberships = listOf(Membership.JOIN)
|
||||||
|
it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
|
||||||
|
it.roomTagQueryFilter = RoomTagQueryFilter(false, false, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
addSection(sections, R.string.low_priority_header) {
|
||||||
|
it.memberships = listOf(Membership.JOIN)
|
||||||
|
it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
|
||||||
|
it.roomTagQueryFilter = RoomTagQueryFilter(null, true, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
addSection(sections, R.string.system_alerts_header) {
|
||||||
|
it.memberships = listOf(Membership.JOIN)
|
||||||
|
it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
|
||||||
|
it.roomTagQueryFilter = RoomTagQueryFilter(null, null, true)
|
||||||
|
}
|
||||||
|
} else if (initialState.displayMode == RoomListDisplayMode.FILTERED) {
|
||||||
|
withQueryParams({
|
||||||
|
it.memberships = Membership.activeMemberships()
|
||||||
|
// it.displayName = QueryStringValue.Contains("")
|
||||||
|
}) { qpm ->
|
||||||
|
val name = stringProvider.getString(R.string.bottom_action_rooms)
|
||||||
|
session.getFilteredPagedRoomSummariesLive(qpm)
|
||||||
|
.let { livePagedList ->
|
||||||
|
updatableQuery = livePagedList
|
||||||
|
sections.add(RoomsSection(name, livePagedList.livePagedList))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sections
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
observeRoomSummaries()
|
|
||||||
observeMembershipChanges()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handle(action: RoomListAction) {
|
override fun handle(action: RoomListAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is RoomListAction.SelectRoom -> handleSelectRoom(action)
|
is RoomListAction.SelectRoom -> handleSelectRoom(action)
|
||||||
is RoomListAction.ToggleCategory -> handleToggleCategory(action)
|
is RoomListAction.AcceptInvitation -> handleAcceptInvitation(action)
|
||||||
is RoomListAction.AcceptInvitation -> handleAcceptInvitation(action)
|
is RoomListAction.RejectInvitation -> handleRejectInvitation(action)
|
||||||
is RoomListAction.RejectInvitation -> handleRejectInvitation(action)
|
is RoomListAction.FilterWith -> handleFilter(action)
|
||||||
is RoomListAction.FilterWith -> handleFilter(action)
|
is RoomListAction.MarkAllRoomsRead -> handleMarkAllRoomsRead()
|
||||||
is RoomListAction.MarkAllRoomsRead -> handleMarkAllRoomsRead()
|
is RoomListAction.LeaveRoom -> handleLeaveRoom(action)
|
||||||
is RoomListAction.LeaveRoom -> handleLeaveRoom(action)
|
|
||||||
is RoomListAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action)
|
is RoomListAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action)
|
||||||
is RoomListAction.ToggleTag -> handleToggleTag(action)
|
is RoomListAction.ToggleTag -> handleToggleTag(action)
|
||||||
|
is RoomListAction.ToggleSection -> handleToggleSection(action.section)
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun addSection(sections: MutableList<RoomsSection>, @StringRes nameRes: Int, query: (RoomSummaryQueryParams.Builder) -> Unit) {
|
||||||
|
withQueryParams({
|
||||||
|
query.invoke(it)
|
||||||
|
}) { roomQueryParams ->
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
sections.add(RoomsSection(name, livePagedList))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun withQueryParams(builder: (RoomSummaryQueryParams.Builder) -> Unit, block: (RoomSummaryQueryParams) -> Unit) {
|
||||||
|
RoomSummaryQueryParams.Builder().apply {
|
||||||
|
builder.invoke(this)
|
||||||
|
}.build().let {
|
||||||
|
block(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun isPublicRoom(roomId: String): Boolean {
|
fun isPublicRoom(roomId: String): Boolean {
|
||||||
return session.getRoom(roomId)?.isPublic().orFalse()
|
return session.getRoom(roomId)?.isPublic().orFalse()
|
||||||
}
|
}
|
||||||
@@ -88,8 +194,11 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
|
|||||||
_viewEvents.post(RoomListViewEvents.SelectRoom(action.roomSummary))
|
_viewEvents.post(RoomListViewEvents.SelectRoom(action.roomSummary))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleToggleCategory(action: RoomListAction.ToggleCategory) = setState {
|
private fun handleToggleSection(section: RoomsSection) {
|
||||||
this.toggle(action.category)
|
sections.find { it.sectionName == section.sectionName }
|
||||||
|
?.let { section ->
|
||||||
|
section.isExpanded.postValue(!section.isExpanded.value.orFalse())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleFilter(action: RoomListAction.FilterWith) {
|
private fun handleFilter(action: RoomListAction.FilterWith) {
|
||||||
@@ -98,23 +207,12 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
|
|||||||
roomFilter = action.filter
|
roomFilter = action.filter
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
updatableQuery?.updateQuery(
|
||||||
|
roomSummaryQueryParams {
|
||||||
private fun observeRoomSummaries() {
|
this.memberships = Membership.activeMemberships()
|
||||||
roomSummariesSource
|
this.displayName = QueryStringValue.Contains(action.filter, QueryStringValue.Case.INSENSITIVE)
|
||||||
.observe()
|
|
||||||
.observeOn(Schedulers.computation())
|
|
||||||
.execute { asyncRooms ->
|
|
||||||
copy(asyncRooms = asyncRooms)
|
|
||||||
}
|
|
||||||
|
|
||||||
roomSummariesSource
|
|
||||||
.observe()
|
|
||||||
.observeOn(Schedulers.computation())
|
|
||||||
.map { buildRoomSummaries(it) }
|
|
||||||
.execute { async ->
|
|
||||||
copy(asyncFilteredRooms = async)
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleAcceptInvitation(action: RoomListAction.AcceptInvitation) = withState { state ->
|
private fun handleAcceptInvitation(action: RoomListAction.AcceptInvitation) = withState { state ->
|
||||||
@@ -164,12 +262,12 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleMarkAllRoomsRead() = withState { state ->
|
private fun handleMarkAllRoomsRead() = withState { state ->
|
||||||
state.asyncFilteredRooms.invoke()
|
// state.asyncFilteredRooms.invoke()
|
||||||
?.flatMap { it.value }
|
// ?.flatMap { it.value }
|
||||||
?.filter { it.membership == Membership.JOIN }
|
// ?.filter { it.membership == Membership.JOIN }
|
||||||
?.map { it.roomId }
|
// ?.map { it.roomId }
|
||||||
?.toList()
|
// ?.toList()
|
||||||
?.let { session.markAllAsRead(it, NoOpMatrixCallback()) }
|
// ?.let { session.markAllAsRead(it, NoOpMatrixCallback()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleChangeNotificationMode(action: RoomListAction.ChangeRoomNotificationState) {
|
private fun handleChangeNotificationMode(action: RoomListAction.ChangeRoomNotificationState) {
|
||||||
@@ -211,7 +309,7 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
|
|||||||
|
|
||||||
private fun String.otherTag(): String? {
|
private fun String.otherTag(): String? {
|
||||||
return when (this) {
|
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
|
RoomTag.ROOM_TAG_LOW_PRIORITY -> RoomTag.ROOM_TAG_FAVOURITE
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
@@ -226,46 +324,4 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState,
|
|||||||
_viewEvents.post(value)
|
_viewEvents.post(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeMembershipChanges() {
|
|
||||||
session.rx()
|
|
||||||
.liveRoomChangeMembershipState()
|
|
||||||
.subscribe {
|
|
||||||
Timber.v("ChangeMembership states: $it")
|
|
||||||
setState { copy(roomMembershipChanges = it) }
|
|
||||||
}
|
|
||||||
.disposeOnClear()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildRoomSummaries(rooms: List<RoomSummary>): RoomSummaries {
|
|
||||||
// Set up init size on directChats and groupRooms as they are the biggest ones
|
|
||||||
val invites = ArrayList<RoomSummary>()
|
|
||||||
val favourites = ArrayList<RoomSummary>()
|
|
||||||
val directChats = ArrayList<RoomSummary>(rooms.size)
|
|
||||||
val groupRooms = ArrayList<RoomSummary>(rooms.size)
|
|
||||||
val lowPriorities = ArrayList<RoomSummary>()
|
|
||||||
val serverNotices = ArrayList<RoomSummary>()
|
|
||||||
|
|
||||||
rooms
|
|
||||||
.filter { roomListDisplayModeFilter.test(it) }
|
|
||||||
.forEach { room ->
|
|
||||||
val tags = room.tags.map { it.name }
|
|
||||||
when {
|
|
||||||
room.membership == Membership.INVITE -> invites.add(room)
|
|
||||||
tags.contains(RoomTag.ROOM_TAG_SERVER_NOTICE) -> serverNotices.add(room)
|
|
||||||
tags.contains(RoomTag.ROOM_TAG_FAVOURITE) -> favourites.add(room)
|
|
||||||
tags.contains(RoomTag.ROOM_TAG_LOW_PRIORITY) -> lowPriorities.add(room)
|
|
||||||
room.isDirect -> directChats.add(room)
|
|
||||||
else -> groupRooms.add(room)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return RoomSummaries().apply {
|
|
||||||
put(RoomCategory.INVITE, invites)
|
|
||||||
put(RoomCategory.FAVOURITE, favourites)
|
|
||||||
put(RoomCategory.DIRECT, directChats)
|
|
||||||
put(RoomCategory.GROUP, groupRooms)
|
|
||||||
put(RoomCategory.LOW_PRIORITY, lowPriorities)
|
|
||||||
put(RoomCategory.SERVER_NOTICE, serverNotices)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -16,20 +16,20 @@
|
|||||||
|
|
||||||
package im.vector.app.features.home.room.list
|
package im.vector.app.features.home.room.list
|
||||||
|
|
||||||
import im.vector.app.features.home.HomeRoomListDataSource
|
import im.vector.app.core.resources.StringProvider
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Provider
|
import javax.inject.Provider
|
||||||
|
|
||||||
class RoomListViewModelFactory @Inject constructor(private val session: Provider<Session>,
|
class RoomListViewModelFactory @Inject constructor(private val session: Provider<Session>,
|
||||||
private val homeRoomListDataSource: Provider<HomeRoomListDataSource>)
|
private val stringProvider: StringProvider)
|
||||||
: RoomListViewModel.Factory {
|
: RoomListViewModel.Factory {
|
||||||
|
|
||||||
override fun create(initialState: RoomListViewState): RoomListViewModel {
|
override fun create(initialState: RoomListViewState): RoomListViewModel {
|
||||||
return RoomListViewModel(
|
return RoomListViewModel(
|
||||||
initialState,
|
initialState,
|
||||||
session.get(),
|
session.get(),
|
||||||
homeRoomListDataSource.get()
|
stringProvider
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,59 +17,26 @@
|
|||||||
package im.vector.app.features.home.room.list
|
package im.vector.app.features.home.room.list
|
||||||
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import com.airbnb.mvrx.Async
|
|
||||||
import com.airbnb.mvrx.MvRxState
|
import com.airbnb.mvrx.MvRxState
|
||||||
import com.airbnb.mvrx.Uninitialized
|
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.features.home.RoomListDisplayMode
|
import im.vector.app.features.home.RoomListDisplayMode
|
||||||
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.Membership
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
|
||||||
data class RoomListViewState(
|
data class RoomListViewState(
|
||||||
val displayMode: RoomListDisplayMode,
|
val displayMode: RoomListDisplayMode,
|
||||||
val asyncRooms: Async<List<RoomSummary>> = Uninitialized,
|
|
||||||
val roomFilter: String = "",
|
val roomFilter: String = "",
|
||||||
val asyncFilteredRooms: Async<RoomSummaries> = Uninitialized,
|
val roomMembershipChanges: Map<String, ChangeMembershipState> = emptyMap()
|
||||||
val roomMembershipChanges: Map<String, ChangeMembershipState> = emptyMap(),
|
|
||||||
val isInviteExpanded: Boolean = true,
|
|
||||||
val isFavouriteRoomsExpanded: Boolean = true,
|
|
||||||
val isDirectRoomsExpanded: Boolean = true,
|
|
||||||
val isGroupRoomsExpanded: Boolean = true,
|
|
||||||
val isLowPriorityRoomsExpanded: Boolean = true,
|
|
||||||
val isServerNoticeRoomsExpanded: Boolean = true
|
|
||||||
) : MvRxState {
|
) : MvRxState {
|
||||||
|
|
||||||
constructor(args: RoomListParams) : this(displayMode = args.displayMode)
|
constructor(args: RoomListParams) : this(displayMode = args.displayMode)
|
||||||
|
|
||||||
fun isCategoryExpanded(roomCategory: RoomCategory): Boolean {
|
val hasUnread: Boolean = false
|
||||||
return when (roomCategory) {
|
// get() = asyncFilteredRooms.invoke()
|
||||||
RoomCategory.INVITE -> isInviteExpanded
|
// ?.flatMap { it.value }
|
||||||
RoomCategory.FAVOURITE -> isFavouriteRoomsExpanded
|
// ?.filter { it.membership == Membership.JOIN }
|
||||||
RoomCategory.DIRECT -> isDirectRoomsExpanded
|
// ?.any { it.hasUnreadMessages }
|
||||||
RoomCategory.GROUP -> isGroupRoomsExpanded
|
// ?: false
|
||||||
RoomCategory.LOW_PRIORITY -> isLowPriorityRoomsExpanded
|
|
||||||
RoomCategory.SERVER_NOTICE -> isServerNoticeRoomsExpanded
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toggle(roomCategory: RoomCategory): RoomListViewState {
|
|
||||||
return when (roomCategory) {
|
|
||||||
RoomCategory.INVITE -> copy(isInviteExpanded = !isInviteExpanded)
|
|
||||||
RoomCategory.FAVOURITE -> copy(isFavouriteRoomsExpanded = !isFavouriteRoomsExpanded)
|
|
||||||
RoomCategory.DIRECT -> copy(isDirectRoomsExpanded = !isDirectRoomsExpanded)
|
|
||||||
RoomCategory.GROUP -> copy(isGroupRoomsExpanded = !isGroupRoomsExpanded)
|
|
||||||
RoomCategory.LOW_PRIORITY -> copy(isLowPriorityRoomsExpanded = !isLowPriorityRoomsExpanded)
|
|
||||||
RoomCategory.SERVER_NOTICE -> copy(isServerNoticeRoomsExpanded = !isServerNoticeRoomsExpanded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val hasUnread: Boolean
|
|
||||||
get() = asyncFilteredRooms.invoke()
|
|
||||||
?.flatMap { it.value }
|
|
||||||
?.filter { it.membership == Membership.JOIN }
|
|
||||||
?.any { it.hasUnreadMessages }
|
|
||||||
?: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typealias RoomSummaries = LinkedHashMap<RoomCategory, List<RoomSummary>>
|
typealias RoomSummaries = LinkedHashMap<RoomCategory, List<RoomSummary>>
|
||||||
|
@@ -1,170 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019 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.annotation.StringRes
|
|
||||||
import com.airbnb.epoxy.EpoxyController
|
|
||||||
import im.vector.app.R
|
|
||||||
import im.vector.app.core.epoxy.helpFooterItem
|
|
||||||
import im.vector.app.core.resources.StringProvider
|
|
||||||
import im.vector.app.core.resources.UserPreferencesProvider
|
|
||||||
import im.vector.app.features.home.RoomListDisplayMode
|
|
||||||
import im.vector.app.features.home.room.filtered.FilteredRoomFooterItem
|
|
||||||
import im.vector.app.features.home.room.filtered.filteredRoomFooterItem
|
|
||||||
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 javax.inject.Inject
|
|
||||||
|
|
||||||
class RoomSummaryController @Inject constructor(private val stringProvider: StringProvider,
|
|
||||||
private val roomSummaryItemFactory: RoomSummaryItemFactory,
|
|
||||||
private val roomListNameFilter: RoomListNameFilter,
|
|
||||||
private val userPreferencesProvider: UserPreferencesProvider
|
|
||||||
) : EpoxyController() {
|
|
||||||
|
|
||||||
var listener: Listener? = null
|
|
||||||
|
|
||||||
private var viewState: RoomListViewState? = null
|
|
||||||
|
|
||||||
init {
|
|
||||||
// We are requesting a model build directly as the first build of epoxy is on the main thread.
|
|
||||||
// It avoids to build the whole list of rooms on the main thread.
|
|
||||||
requestModelBuild()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun update(viewState: RoomListViewState) {
|
|
||||||
this.viewState = viewState
|
|
||||||
requestModelBuild()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onRoomLongClicked() {
|
|
||||||
userPreferencesProvider.neverShowLongClickOnRoomHelpAgain()
|
|
||||||
requestModelBuild()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun buildModels() {
|
|
||||||
val nonNullViewState = viewState ?: return
|
|
||||||
when (nonNullViewState.displayMode) {
|
|
||||||
RoomListDisplayMode.FILTERED -> buildFilteredRooms(nonNullViewState)
|
|
||||||
else -> buildRooms(nonNullViewState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildFilteredRooms(viewState: RoomListViewState) {
|
|
||||||
val summaries = viewState.asyncRooms() ?: return
|
|
||||||
|
|
||||||
roomListNameFilter.filter = viewState.roomFilter
|
|
||||||
|
|
||||||
val filteredSummaries = summaries
|
|
||||||
.filter { it.membership == Membership.JOIN && roomListNameFilter.test(it) }
|
|
||||||
|
|
||||||
buildRoomModels(filteredSummaries,
|
|
||||||
viewState.roomMembershipChanges,
|
|
||||||
emptySet())
|
|
||||||
|
|
||||||
addFilterFooter(viewState)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildRooms(viewState: RoomListViewState) {
|
|
||||||
var showHelp = false
|
|
||||||
val roomSummaries = viewState.asyncFilteredRooms()
|
|
||||||
roomSummaries?.forEach { (category, summaries) ->
|
|
||||||
if (summaries.isEmpty()) {
|
|
||||||
return@forEach
|
|
||||||
} else {
|
|
||||||
val isExpanded = viewState.isCategoryExpanded(category)
|
|
||||||
buildRoomCategory(viewState, summaries, category.titleRes, viewState.isCategoryExpanded(category)) {
|
|
||||||
listener?.onToggleRoomCategory(category)
|
|
||||||
}
|
|
||||||
if (isExpanded) {
|
|
||||||
buildRoomModels(summaries,
|
|
||||||
viewState.roomMembershipChanges,
|
|
||||||
emptySet())
|
|
||||||
// Never set showHelp to true for invitation
|
|
||||||
if (category != RoomCategory.INVITE) {
|
|
||||||
showHelp = userPreferencesProvider.shouldShowLongClickOnRoomHelp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showHelp) {
|
|
||||||
buildLongClickHelp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildLongClickHelp() {
|
|
||||||
helpFooterItem {
|
|
||||||
id("long_click_help")
|
|
||||||
text(stringProvider.getString(R.string.help_long_click_on_room_for_more_options))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addFilterFooter(viewState: RoomListViewState) {
|
|
||||||
filteredRoomFooterItem {
|
|
||||||
id("filter_footer")
|
|
||||||
listener(listener)
|
|
||||||
currentFilter(viewState.roomFilter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildRoomCategory(viewState: RoomListViewState,
|
|
||||||
summaries: List<RoomSummary>,
|
|
||||||
@StringRes titleRes: Int,
|
|
||||||
isExpanded: Boolean,
|
|
||||||
mutateExpandedState: () -> Unit) {
|
|
||||||
// TODO should add some business logic later
|
|
||||||
val unreadCount = if (summaries.isEmpty()) {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
summaries.map { it.notificationCount }.sumBy { i -> i }
|
|
||||||
}
|
|
||||||
val showHighlighted = summaries.any { it.highlightCount > 0 }
|
|
||||||
roomCategoryItem {
|
|
||||||
id(titleRes)
|
|
||||||
title(stringProvider.getString(titleRes))
|
|
||||||
expanded(isExpanded)
|
|
||||||
unreadNotificationCount(unreadCount)
|
|
||||||
showHighlighted(showHighlighted)
|
|
||||||
listener {
|
|
||||||
mutateExpandedState()
|
|
||||||
update(viewState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildRoomModels(summaries: List<RoomSummary>,
|
|
||||||
roomChangedMembershipStates: Map<String, ChangeMembershipState>,
|
|
||||||
selectedRoomIds: Set<String>) {
|
|
||||||
summaries.forEach { roomSummary ->
|
|
||||||
roomSummaryItemFactory
|
|
||||||
.create(roomSummary,
|
|
||||||
roomChangedMembershipStates,
|
|
||||||
selectedRoomIds,
|
|
||||||
listener)
|
|
||||||
.addTo(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Listener : FilteredRoomFooterItem.FilteredRoomFooterItemListener {
|
|
||||||
fun onToggleRoomCategory(roomCategory: RoomCategory)
|
|
||||||
fun onRoomClicked(room: RoomSummary)
|
|
||||||
fun onRoomLongClicked(room: RoomSummary): Boolean
|
|
||||||
fun onRejectRoomInvitation(room: RoomSummary)
|
|
||||||
fun onAcceptRoomInvitation(room: RoomSummary)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -40,7 +40,7 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
|
|||||||
fun create(roomSummary: RoomSummary,
|
fun create(roomSummary: RoomSummary,
|
||||||
roomChangeMembershipStates: Map<String, ChangeMembershipState>,
|
roomChangeMembershipStates: Map<String, ChangeMembershipState>,
|
||||||
selectedRoomIds: Set<String>,
|
selectedRoomIds: Set<String>,
|
||||||
listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> {
|
listener: RoomListListener?): VectorEpoxyModel<*> {
|
||||||
return when (roomSummary.membership) {
|
return when (roomSummary.membership) {
|
||||||
Membership.INVITE -> {
|
Membership.INVITE -> {
|
||||||
val changeMembershipState = roomChangeMembershipStates[roomSummary.roomId] ?: ChangeMembershipState.Unknown
|
val changeMembershipState = roomChangeMembershipStates[roomSummary.roomId] ?: ChangeMembershipState.Unknown
|
||||||
@@ -52,7 +52,7 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
|
|||||||
|
|
||||||
private fun createInvitationItem(roomSummary: RoomSummary,
|
private fun createInvitationItem(roomSummary: RoomSummary,
|
||||||
changeMembershipState: ChangeMembershipState,
|
changeMembershipState: ChangeMembershipState,
|
||||||
listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> {
|
listener: RoomListListener?): VectorEpoxyModel<*> {
|
||||||
val secondLine = if (roomSummary.isDirect) {
|
val secondLine = if (roomSummary.isDirect) {
|
||||||
roomSummary.inviterId
|
roomSummary.inviterId
|
||||||
} else {
|
} else {
|
||||||
|
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* 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.EpoxyModel
|
||||||
|
import com.airbnb.epoxy.paging.PagedListEpoxyController
|
||||||
|
import im.vector.app.core.resources.StringProvider
|
||||||
|
import im.vector.app.core.resources.UserPreferencesProvider
|
||||||
|
import im.vector.app.core.utils.createUIHandler
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class RoomSummaryPagedControllerFactory @Inject constructor(private val stringProvider: StringProvider,
|
||||||
|
private val userPreferencesProvider: UserPreferencesProvider,
|
||||||
|
private val roomSummaryItemFactory: RoomSummaryItemFactory) {
|
||||||
|
|
||||||
|
fun createRoomSummaryPagedController(): RoomSummaryPagedController {
|
||||||
|
return RoomSummaryPagedController(stringProvider, userPreferencesProvider, roomSummaryItemFactory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RoomSummaryPagedController constructor(private val stringProvider: StringProvider,
|
||||||
|
private val userPreferencesProvider: UserPreferencesProvider,
|
||||||
|
private val roomSummaryItemFactory: RoomSummaryItemFactory)
|
||||||
|
: PagedListEpoxyController<RoomSummary>(
|
||||||
|
// Important it must match the PageList builder notify Looper
|
||||||
|
modelBuildingHandler = createUIHandler()
|
||||||
|
) {
|
||||||
|
|
||||||
|
var listener: RoomListListener? = null
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
// GenericItem_().apply { id("null_item_pos_$currentPosition") }
|
||||||
|
|
||||||
|
return roomSummaryItemFactory.create(unwrappedItem, emptyMap(), 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()
|
||||||
|
// }
|
||||||
|
}
|
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.graphics.drawable.DrawableCompat
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.utils.DebouncedClickListener
|
||||||
|
import im.vector.app.databinding.ItemRoomCategoryBinding
|
||||||
|
import im.vector.app.features.themes.ThemeUtils
|
||||||
|
|
||||||
|
class SectionHeaderAdapter constructor(
|
||||||
|
private val onClickAction: (() -> Unit)
|
||||||
|
) : RecyclerView.Adapter<SectionHeaderAdapter.VH>() {
|
||||||
|
|
||||||
|
data class SectionViewModel(
|
||||||
|
val name: String,
|
||||||
|
val isExpanded: Boolean = true,
|
||||||
|
val notificationCount: Int = 0,
|
||||||
|
val isHighlighted: Boolean = false,
|
||||||
|
val isHidden: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
lateinit var section: SectionViewModel
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun updateSection(newSection: SectionViewModel) {
|
||||||
|
if (!::section.isInitialized || newSection != section) {
|
||||||
|
section = newSection
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
setHasStableIds(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemId(position: Int) = section.hashCode().toLong()
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int) = R.layout.item_room_category
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
|
||||||
|
return VH.create(parent, this.onClickAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: VH, position: Int) {
|
||||||
|
holder.bind(section)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = 1.takeIf { section.isHidden.not() } ?: 0
|
||||||
|
|
||||||
|
class VH constructor(
|
||||||
|
private val binding: ItemRoomCategoryBinding,
|
||||||
|
onClickAction: (() -> Unit)
|
||||||
|
) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
binding.root.setOnClickListener(DebouncedClickListener({
|
||||||
|
onClickAction.invoke()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bind(section: SectionViewModel) {
|
||||||
|
binding.roomCategoryTitleView.text = section.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 expandedArrowDrawable = ContextCompat.getDrawable(binding.root.context, expandedArrowDrawableRes)?.also {
|
||||||
|
DrawableCompat.setTint(it, tintColor)
|
||||||
|
}
|
||||||
|
binding.roomCategoryUnreadCounterBadgeView.render(UnreadCounterBadgeView.State(section.notificationCount, section.isHighlighted))
|
||||||
|
binding.roomCategoryTitleView.setCompoundDrawablesWithIntrinsicBounds(null, null, expandedArrowDrawable, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun create(parent: ViewGroup, onClickAction: () -> Unit): VH {
|
||||||
|
val view = LayoutInflater.from(parent.context)
|
||||||
|
.inflate(R.layout.item_room_category, parent, false)
|
||||||
|
val binding = ItemRoomCategoryBinding.bind(view)
|
||||||
|
return VH(binding, onClickAction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user