diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt index aae72dd4..e4bf1bd3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt @@ -29,10 +29,11 @@ data class RoomSummary( val topic: String = "", val avatarUrl: String = "", val isDirect: Boolean = false, - val latestEvent: TimelineEvent? = null, + val latestPreviewableEvent: TimelineEvent? = null, val otherMemberIds: List = emptyList(), val notificationCount: Int = 0, val highlightCount: Int = 0, + val hasUnreadMessages: Boolean = false, val tags: List = emptyList(), val membership: Membership = Membership.NONE, val versioningState: VersioningState = VersioningState.NONE diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt index 95d4d8bc..28e1c112 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt @@ -20,6 +20,7 @@ import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.tag.RoomTag +import im.vector.matrix.android.api.session.room.read.ReadService import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import java.util.* @@ -30,12 +31,12 @@ internal class RoomSummaryMapper @Inject constructor( val timelineEventMapper: TimelineEventMapper ) { - fun map(roomSummaryEntity: RoomSummaryEntity): RoomSummary { + fun map(roomSummaryEntity: RoomSummaryEntity, readService: ReadService?): RoomSummary { val tags = roomSummaryEntity.tags.map { RoomTag(it.tagName, it.tagOrder) } - val latestEvent = roomSummaryEntity.latestEvent?.let { + val latestEvent = roomSummaryEntity.latestPreviewableEvent?.let { timelineEventMapper.map(it) } if (latestEvent?.root?.isEncrypted() == true && latestEvent.root.mxDecryptionResult == null) { @@ -43,26 +44,30 @@ internal class RoomSummaryMapper @Inject constructor( //for now decrypt sync try { val result = cryptoService.decryptEvent(latestEvent.root, latestEvent.root.roomId + UUID.randomUUID().toString()) - latestEvent.root.mxDecryptionResult = OlmDecryptionResult( - payload = result.clearEvent, - senderKey = result.senderCurve25519Key, - keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) }, - forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain - ) + latestEvent.root.mxDecryptionResult = OlmDecryptionResult( + payload = result.clearEvent, + senderKey = result.senderCurve25519Key, + keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) }, + forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain + ) } catch (e: MXCryptoError) { } } + val hasUnreadMessages = roomSummaryEntity.notificationCount > 0 + //avoid this call if we are sure there are unread events + || latestEvent?.root?.eventId?.let { readService?.isEventRead(it)?.not() } ?: false return RoomSummary( roomId = roomSummaryEntity.roomId, displayName = roomSummaryEntity.displayName ?: "", topic = roomSummaryEntity.topic ?: "", avatarUrl = roomSummaryEntity.avatarUrl ?: "", isDirect = roomSummaryEntity.isDirect, - latestEvent = latestEvent, + latestPreviewableEvent = latestEvent, otherMemberIds = roomSummaryEntity.otherMemberIds.toList(), highlightCount = roomSummaryEntity.highlightCount, notificationCount = roomSummaryEntity.notificationCount, + hasUnreadMessages = hasUnreadMessages, tags = tags, membership = roomSummaryEntity.membership, versioningState = roomSummaryEntity.versioningState diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt index 6fe81f4c..02c61ec1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt @@ -26,7 +26,7 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "", var displayName: String? = "", var avatarUrl: String? = "", var topic: String? = "", - var latestEvent: TimelineEventEntity? = null, + var latestPreviewableEvent: TimelineEventEntity? = null, var heroes: RealmList = RealmList(), var joinedMembersCount: Int? = 0, var invitedMembersCount: Int? = 0, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt index 492dd035..24703803 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt @@ -58,7 +58,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String, RoomSummaryEntity.where(realm, roomId).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) } return Transformations.map(liveRealmData) { results -> - val roomSummaries = results.map { roomSummaryMapper.map(it) } + val roomSummaries = results.map { roomSummaryMapper.map(it, this) } if (roomSummaries.isEmpty()) { // Create a dummy RoomSummary to avoid Crash during Sign Out or clear cache @@ -72,7 +72,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String, override fun roomSummary(): RoomSummary? { return monarchy.fetchAllMappedSync( { realm -> RoomSummaryEntity.where(realm, roomId).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) }, - { roomSummaryMapper.map(it) } + { roomSummaryMapper.map(it, this) } ).firstOrNull() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt index bd5462b1..53ecad4b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt @@ -24,6 +24,7 @@ import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.VersioningState import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams +import im.vector.matrix.android.api.session.room.read.ReadService import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.internal.database.mapper.RoomSummaryMapper import im.vector.matrix.android.internal.database.model.RoomEntity @@ -69,7 +70,7 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona .isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) .notEqualTo(RoomSummaryEntityFields.VERSIONING_STATE_STR, VersioningState.UPGRADED_ROOM_JOINED.name) }, - { roomSummaryMapper.map(it) } + { roomSummaryMapper.map(it, getRoom(it.roomId)) } ) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt index dda8b932..8d5ac88d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt @@ -85,7 +85,7 @@ internal class RoomSummaryUpdater @Inject constructor(private val credentials: C roomSummaryEntity.membership = membership } - val latestEvent = TimelineEventEntity.latestEvent(realm, roomId, includesSending = true, includedTypes = PREVIEWABLE_TYPES) + val latestPreviewableEvent = TimelineEventEntity.latestEvent(realm, roomId, includesSending = true, includedTypes = PREVIEWABLE_TYPES) val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).prev()?.asDomain() val otherRoomMembers = RoomMembers(realm, roomId) @@ -98,7 +98,7 @@ internal class RoomSummaryUpdater @Inject constructor(private val credentials: C roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(roomId).toString() roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(roomId) roomSummaryEntity.topic = lastTopicEvent?.content.toModel()?.topic - roomSummaryEntity.latestEvent = latestEvent + roomSummaryEntity.latestPreviewableEvent = latestPreviewableEvent roomSummaryEntity.otherMemberIds.clear() roomSummaryEntity.otherMemberIds.addAll(otherRoomMembers) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt index 74b56e77..9859c0c7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt @@ -113,12 +113,12 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch private fun handleJoinedRoom(realm: Realm, roomId: String, roomSync: RoomSync, - isInitalSync: Boolean): RoomEntity { + isInitialSync: Boolean): RoomEntity { Timber.v("Handle join sync for room $roomId") if (roomSync.ephemeral != null && roomSync.ephemeral.events.isNotEmpty()) { - handleEphemeral(realm, roomId, roomSync.ephemeral, isInitalSync) + handleEphemeral(realm, roomId, roomSync.ephemeral, isInitialSync) } if (roomSync.accountData != null && roomSync.accountData.events.isNullOrEmpty().not()) { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index 1cd8cc4a..8ec133c6 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -658,7 +658,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro private fun observeSummaryState() { asyncSubscribe(RoomDetailViewState::asyncRoomSummary) { summary -> if (summary.membership == Membership.INVITE) { - summary.latestEvent?.root?.senderId?.let { senderId -> + summary.latestPreviewableEvent?.root?.senderId?.let { senderId -> session.getUser(senderId) }?.also { setState { copy(asyncInviter = Success(it)) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/ChronologicalRoomComparator.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/ChronologicalRoomComparator.kt index c4a1633b..d25198f5 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/ChronologicalRoomComparator.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/ChronologicalRoomComparator.kt @@ -25,14 +25,14 @@ class ChronologicalRoomComparator @Inject constructor() : Comparator() { @EpoxyAttribute lateinit var title: CharSequence @EpoxyAttribute var expanded: Boolean = false - @EpoxyAttribute var unreadCount: Int = 0 + @EpoxyAttribute var unreadNotificationCount: Int = 0 @EpoxyAttribute var showHighlighted: Boolean = false @EpoxyAttribute var listener: (() -> Unit)? = null @@ -42,7 +42,7 @@ abstract class RoomCategoryItem : VectorEpoxyModel() { val expandedArrowDrawable = ContextCompat.getDrawable(holder.rootView.context, expandedArrowDrawableRes)?.also { DrawableCompat.setTint(it, tintColor) } - holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadCount, showHighlighted)) + holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted)) holder.titleView.setCompoundDrawablesWithIntrinsicBounds(expandedArrowDrawable, null, null, null) holder.titleView.text = title holder.rootView.setOnClickListener { listener?.invoke() } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt index 03bedbc7..42e3a3db 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt @@ -101,7 +101,7 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri id(titleRes) title(stringProvider.getString(titleRes).toUpperCase()) expanded(isExpanded) - unreadCount(unreadCount) + unreadNotificationCount(unreadCount) showHighlighted(showHighlighted) listener { mutateExpandedState() diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItem.kt index 72f5b973..2ee1f306 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItem.kt @@ -16,9 +16,11 @@ package im.vector.riotx.features.home.room.list +import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView +import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.riotx.R @@ -36,7 +38,8 @@ abstract class RoomSummaryItem : VectorEpoxyModel() { @EpoxyAttribute lateinit var lastFormattedEvent: CharSequence @EpoxyAttribute lateinit var lastEventTime: CharSequence @EpoxyAttribute var avatarUrl: String? = null - @EpoxyAttribute var unreadCount: Int = 0 + @EpoxyAttribute var unreadNotificationCount: Int = 0 + @EpoxyAttribute var hasUnreadMessage: Boolean = false @EpoxyAttribute var showHighlighted: Boolean = false @EpoxyAttribute var listener: (() -> Unit)? = null @@ -47,13 +50,15 @@ abstract class RoomSummaryItem : VectorEpoxyModel() { holder.titleView.text = roomName holder.lastEventTimeView.text = lastEventTime holder.lastEventView.text = lastFormattedEvent - holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadCount, showHighlighted)) + holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted)) + holder.unreadIndentIndicator.isVisible = hasUnreadMessage avatarRenderer.render(avatarUrl, roomId, roomName.toString(), holder.avatarImageView) } class Holder : VectorEpoxyHolder() { val titleView by bind(R.id.roomNameView) val unreadCounterBadgeView by bind(R.id.roomUnreadCounterBadgeView) + val unreadIndentIndicator by bind(R.id.roomUnreadIndicator) val lastEventView by bind(R.id.roomLastEventView) val lastEventTimeView by bind(R.id.roomLastEventTimeView) val avatarImageView by bind(R.id.roomAvatarImageView) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItemFactory.kt index 38f15974..e1d5f2a1 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItemFactory.kt @@ -16,6 +16,7 @@ package im.vector.riotx.features.home.room.list +import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.Membership @@ -38,7 +39,8 @@ class RoomSummaryItemFactory @Inject constructor(private val noticeEventFormatte private val dateFormatter: VectorDateFormatter, private val colorProvider: ColorProvider, private val stringProvider: StringProvider, - private val avatarRenderer: AvatarRenderer) { + private val avatarRenderer: AvatarRenderer, + private val session: Session) { fun create(roomSummary: RoomSummary, joiningRoomsIds: Set, @@ -59,9 +61,9 @@ class RoomSummaryItemFactory @Inject constructor(private val noticeEventFormatte rejectingErrorRoomsIds: Set, listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> { val secondLine = if (roomSummary.isDirect) { - roomSummary.latestEvent?.root?.senderId + roomSummary.latestPreviewableEvent?.root?.senderId } else { - roomSummary.latestEvent?.root?.senderId?.let { + roomSummary.latestPreviewableEvent?.root?.senderId?.let { stringProvider.getString(R.string.invited_by, it) } } @@ -88,7 +90,7 @@ class RoomSummaryItemFactory @Inject constructor(private val noticeEventFormatte var latestFormattedEvent: CharSequence = "" var latestEventTime: CharSequence = "" - val latestEvent = roomSummary.latestEvent + val latestEvent = roomSummary.latestPreviewableEvent if (latestEvent != null) { val date = latestEvent.root.localDateTime() val currentDate = DateProvider.currentLocalDateTime() @@ -131,7 +133,8 @@ class RoomSummaryItemFactory @Inject constructor(private val noticeEventFormatte .roomName(roomSummary.displayName) .avatarUrl(roomSummary.avatarUrl) .showHighlighted(showHighlighted) - .unreadCount(unreadCount) + .unreadNotificationCount(unreadCount) + .hasUnreadMessage(roomSummary.hasUnreadMessages) .listener { listener?.onRoomSelected(roomSummary) } } diff --git a/vector/src/main/res/layout/item_room.xml b/vector/src/main/res/layout/item_room.xml index b110e200..7eb2083e 100644 --- a/vector/src/main/res/layout/item_room.xml +++ b/vector/src/main/res/layout/item_room.xml @@ -10,12 +10,22 @@ android:focusable="true" android:foreground="?attr/selectableItemBackground"> + +