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 f6de3589..b54f17db 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 @@ -16,8 +16,8 @@ package im.vector.matrix.android.api.session.room.model -import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.model.tag.RoomTag +import im.vector.matrix.android.api.session.room.timeline.TimelineEvent /** * This class holds some data of a room. @@ -29,7 +29,7 @@ data class RoomSummary( val topic: String = "", val avatarUrl: String = "", val isDirect: Boolean = false, - val lastMessage: Event? = null, + val latestEvent: TimelineEvent? = null, val otherMemberIds: List = emptyList(), val notificationCount: Int = 0, val highlightCount: Int = 0, 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 03035128..a235d787 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 @@ -19,21 +19,26 @@ package im.vector.matrix.android.internal.database.mapper 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.internal.database.model.RoomSummaryEntity +import im.vector.matrix.android.internal.session.room.timeline.TimelineEventFactory +import javax.inject.Inject -internal object RoomSummaryMapper { +internal class RoomSummaryMapper @Inject constructor(private val timelineEventFactory: TimelineEventFactory) { fun map(roomSummaryEntity: RoomSummaryEntity): RoomSummary { val tags = roomSummaryEntity.tags.map { RoomTag(it.tagName, it.tagOrder) } + val latestEvent = roomSummaryEntity.latestEvent?.let { + timelineEventFactory.create(it) + } return RoomSummary( roomId = roomSummaryEntity.roomId, displayName = roomSummaryEntity.displayName ?: "", topic = roomSummaryEntity.topic ?: "", avatarUrl = roomSummaryEntity.avatarUrl ?: "", isDirect = roomSummaryEntity.isDirect, - lastMessage = roomSummaryEntity.lastMessage?.asDomain(), + latestEvent = latestEvent, otherMemberIds = roomSummaryEntity.otherMemberIds.toList(), highlightCount = roomSummaryEntity.highlightCount, notificationCount = roomSummaryEntity.notificationCount, @@ -42,7 +47,3 @@ internal object RoomSummaryMapper { ) } } - -internal fun RoomSummaryEntity.asDomain(): RoomSummary { - return RoomSummaryMapper.map(this) -} \ No newline at end of file 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 32f27adb..5e3a9752 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 @@ -27,7 +27,7 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "", var displayName: String? = "", var avatarUrl: String? = "", var topic: String? = "", - var lastMessage: EventEntity? = null, + var latestEvent: EventEntity? = 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/database/query/EventEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt index 6f4db6f4..f7a71c47 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt @@ -16,10 +16,12 @@ package im.vector.matrix.android.internal.database.query +import im.vector.matrix.android.internal.database.helper.addSendingEvent import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.EventEntity.LinkFilterMode.* import im.vector.matrix.android.internal.database.model.EventEntityFields +import im.vector.matrix.android.internal.database.model.RoomEntity import io.realm.Realm import io.realm.RealmList import io.realm.RealmQuery @@ -65,7 +67,14 @@ internal fun EventEntity.Companion.latestEvent(realm: Realm, roomId: String, includedTypes: List = emptyList(), excludedTypes: List = emptyList()): EventEntity? { - val query = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)?.events?.where() + + val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: return null + val eventList = if (roomEntity.sendingTimelineEvents.isNotEmpty()) { + roomEntity.sendingTimelineEvents + } else { + ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)?.events + } + val query = eventList?.where() if (includedTypes.isNotEmpty()) { query?.`in`(EventEntityFields.TYPE, includedTypes.toTypedArray()) } else if (excludedTypes.isNotEmpty()) { 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 3e2958d5..66cc7405 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 @@ -29,7 +29,7 @@ import im.vector.matrix.android.api.session.room.send.SendService import im.vector.matrix.android.api.session.room.state.StateService import im.vector.matrix.android.api.session.room.timeline.TimelineService import im.vector.matrix.android.internal.database.RealmLiveData -import im.vector.matrix.android.internal.database.mapper.asDomain +import im.vector.matrix.android.internal.database.mapper.RoomSummaryMapper import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields import im.vector.matrix.android.internal.database.query.where @@ -38,6 +38,7 @@ import javax.inject.Inject internal class DefaultRoom @Inject constructor(override val roomId: String, private val monarchy: Monarchy, + private val roomSummaryMapper: RoomSummaryMapper, private val timelineService: TimelineService, private val sendService: SendService, private val stateService: StateService, @@ -58,7 +59,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String, RoomSummaryEntity.where(realm, roomId).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) } Transformations.map(liveRealmData) { results -> - val roomSummaries = results.map { it.asDomain() } + val roomSummaries = results.map { roomSummaryMapper.map(it) } if (roomSummaries.isEmpty()) { // Create a dummy RoomSummary to avoid Crash during Sign Out or clear cache @@ -72,7 +73,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String, override val roomSummary: RoomSummary? get() { var sum: RoomSummaryEntity? = monarchy.fetchCopied { RoomSummaryEntity.where(it, roomId).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME).findFirst() } - return sum?.asDomain() + return sum?.let { roomSummaryMapper.map(it) } } override fun isEncrypted(): Boolean { 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 cd729c93..2c0f1ce9 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 @@ -23,12 +23,11 @@ import im.vector.matrix.android.api.session.room.Room 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.create.CreateRoomParams -import im.vector.matrix.android.internal.database.mapper.asDomain +import im.vector.matrix.android.internal.database.mapper.RoomSummaryMapper import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields import im.vector.matrix.android.internal.database.query.where -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.create.CreateRoomTask import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith @@ -36,6 +35,7 @@ import im.vector.matrix.android.internal.util.fetchManaged import javax.inject.Inject internal class DefaultRoomService @Inject constructor(private val monarchy: Monarchy, + private val roomSummaryMapper: RoomSummaryMapper, private val createRoomTask: CreateRoomTask, private val roomFactory: RoomFactory, private val taskExecutor: TaskExecutor) : RoomService { @@ -55,7 +55,7 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona override fun liveRoomSummaries(): LiveData> { return monarchy.findAllMappedWithChanges( { realm -> RoomSummaryEntity.where(realm).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) }, - { it.asDomain() } + { roomSummaryMapper.map(it) } ) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt index 38935f19..51e5ba0e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt @@ -21,6 +21,7 @@ import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.room.Room +import im.vector.matrix.android.internal.database.mapper.RoomSummaryMapper import im.vector.matrix.android.internal.session.room.membership.DefaultMembershipService import im.vector.matrix.android.internal.session.room.membership.LoadRoomMembersTask import im.vector.matrix.android.internal.session.room.membership.SenderRoomMemberExtractor @@ -38,8 +39,8 @@ import im.vector.matrix.android.internal.session.room.state.DefaultStateService import im.vector.matrix.android.internal.session.room.state.SendStateTask import im.vector.matrix.android.internal.session.room.timeline.DefaultTimelineService import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEventTask +import im.vector.matrix.android.internal.session.room.timeline.InMemoryTimelineEventFactory import im.vector.matrix.android.internal.session.room.timeline.PaginationTask -import im.vector.matrix.android.internal.session.room.timeline.TimelineEventFactory import im.vector.matrix.android.internal.task.TaskExecutor import javax.inject.Inject @@ -47,6 +48,7 @@ internal class RoomFactory @Inject constructor(private val context: Context, private val credentials: Credentials, private val monarchy: Monarchy, private val eventFactory: LocalEchoEventFactory, + private val roomSummaryMapper: RoomSummaryMapper, private val taskExecutor: TaskExecutor, private val loadRoomMembersTask: LoadRoomMembersTask, private val inviteTask: InviteTask, @@ -61,9 +63,7 @@ internal class RoomFactory @Inject constructor(private val context: Context, private val leaveRoomTask: LeaveRoomTask) { fun create(roomId: String): Room { - val roomMemberExtractor = SenderRoomMemberExtractor(roomId) - val relationExtractor = EventRelationExtractor() - val timelineEventFactory = TimelineEventFactory(roomMemberExtractor, relationExtractor, cryptoService) + val timelineEventFactory = InMemoryTimelineEventFactory(SenderRoomMemberExtractor(), EventRelationExtractor(), cryptoService) val timelineService = DefaultTimelineService(roomId, monarchy, taskExecutor, timelineEventFactory, contextOfEventTask, paginationTask) val sendService = DefaultSendService(context, credentials, roomId, eventFactory, cryptoService, monarchy) val stateService = DefaultStateService(roomId, taskExecutor, sendStateTask) @@ -74,6 +74,7 @@ internal class RoomFactory @Inject constructor(private val context: Context, return DefaultRoom( roomId, monarchy, + roomSummaryMapper, timelineService, sendService, stateService, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt index 9cf6ac69..1e0794a4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt @@ -138,4 +138,10 @@ internal abstract class RoomModule { @Binds abstract fun bindTimelineService(timelineService: DefaultTimelineService): TimelineService + @Binds + abstract fun bindSimpleTimelineEventFactory(timelineEventFactory: SimpleTimelineEventFactory): TimelineEventFactory + + @Binds + abstract fun bindCacheableTimelineEventFactory(inMemoryTimelineEventFactory: InMemoryTimelineEventFactory): CacheableTimelineEventFactory + } 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 56622f6d..766e13fa 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 @@ -27,7 +27,6 @@ import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import im.vector.matrix.android.internal.database.query.latestEvent import im.vector.matrix.android.internal.database.query.prev import im.vector.matrix.android.internal.database.query.where -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.membership.RoomDisplayNameResolver import im.vector.matrix.android.internal.session.room.membership.RoomMembers import im.vector.matrix.android.internal.session.sync.model.RoomSyncSummary @@ -40,6 +39,23 @@ internal class RoomSummaryUpdater @Inject constructor(private val credentials: C private val roomDisplayNameResolver: RoomDisplayNameResolver, private val roomAvatarResolver: RoomAvatarResolver) { + // TODO: maybe allow user of SDK to give that list + private val PREVIEWABLE_TYPES = listOf( + EventType.MESSAGE, + EventType.STATE_ROOM_NAME, + EventType.STATE_ROOM_TOPIC, + EventType.STATE_ROOM_MEMBER, + EventType.STATE_HISTORY_VISIBILITY, + EventType.CALL_INVITE, + EventType.CALL_HANGUP, + EventType.CALL_ANSWER, + EventType.ENCRYPTED, + EventType.ENCRYPTION, + EventType.STATE_ROOM_THIRD_PARTY_INVITE, + EventType.STICKER, + EventType.STATE_ROOM_CREATE + ) + fun update(realm: Realm, roomId: String, membership: Membership? = null, @@ -47,7 +63,7 @@ internal class RoomSummaryUpdater @Inject constructor(private val credentials: C unreadNotifications: RoomSyncUnreadNotifications? = null) { val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst() - ?: realm.createObject(roomId) + ?: realm.createObject(roomId) if (roomSummary != null) { if (roomSummary.heroes.isNotEmpty()) { @@ -71,13 +87,13 @@ internal class RoomSummaryUpdater @Inject constructor(private val credentials: C roomSummaryEntity.membership = membership } - val lastEvent = EventEntity.latestEvent(realm, roomId) + val lastEvent = EventEntity.latestEvent(realm, roomId, includedTypes = PREVIEWABLE_TYPES) val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).prev()?.asDomain() val otherRoomMembers = RoomMembers(realm, roomId).getLoaded().filterKeys { it != credentials.userId } roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(roomId).toString() roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(roomId) roomSummaryEntity.topic = lastTopicEvent?.content.toModel()?.topic - roomSummaryEntity.lastMessage = lastEvent + roomSummaryEntity.latestEvent = lastEvent roomSummaryEntity.otherMemberIds.clear() roomSummaryEntity.otherMemberIds.addAll(otherRoomMembers.keys) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/SenderRoomMemberExtractor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/SenderRoomMemberExtractor.kt index f7a0a9a6..890ecc8e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/SenderRoomMemberExtractor.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/SenderRoomMemberExtractor.kt @@ -34,9 +34,10 @@ import io.realm.RealmList import io.realm.RealmQuery import javax.inject.Inject -internal class SenderRoomMemberExtractor @Inject constructor(private val roomId: String) { +internal class SenderRoomMemberExtractor @Inject constructor() { fun extractFrom(event: EventEntity, realm: Realm = event.realm): RoomMember? { + val roomId = event.roomId val sender = event.sender ?: return null // If the event is unlinked we want to fetch unlinked state events val unlinked = event.isUnlinked diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index 1160cc5e..6e80e7c6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -34,6 +34,7 @@ import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.session.content.ThumbnailExtractor +import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater import im.vector.matrix.android.internal.util.StringProvider import im.vector.matrix.android.internal.util.tryTransactionAsync import org.commonmark.parser.Parser @@ -50,8 +51,9 @@ import javax.inject.Inject * * The transactionID is used as loc */ - -internal class LocalEchoEventFactory @Inject constructor(private val credentials: Credentials, private val stringProvider: StringProvider) { +internal class LocalEchoEventFactory @Inject constructor(private val credentials: Credentials, + private val stringProvider: StringProvider, + private val roomSummaryUpdater: RoomSummaryUpdater) { fun createTextEvent(roomId: String, msgType: String, text: String, autoMarkdown: Boolean): Event { if (autoMarkdown && msgType == MessageType.MSGTYPE_TEXT) { @@ -342,10 +344,12 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials } fun saveLocalEcho(monarchy: Monarchy, event: Event) { + if (event.roomId == null) throw IllegalStateException("Your event should have a roomId") monarchy.tryTransactionAsync { realm -> - val roomEntity = RoomEntity.where(realm, roomId = event.roomId!!).findFirst() + val roomEntity = RoomEntity.where(realm, roomId = event.roomId).findFirst() ?: return@tryTransactionAsync roomEntity.addSendingEvent(event) + roomSummaryUpdater.update(realm, event.roomId) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt index 1165b210..5218d130 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt @@ -54,7 +54,7 @@ internal class DefaultTimeline( private val realmConfiguration: RealmConfiguration, private val taskExecutor: TaskExecutor, private val contextOfEventTask: GetContextOfEventTask, - private val timelineEventFactory: TimelineEventFactory, + private val timelineEventFactory: CacheableTimelineEventFactory, private val paginationTask: PaginationTask, private val allowedTypes: List? ) : Timeline { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt index dbd40ad1..98c54330 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt @@ -33,7 +33,7 @@ import javax.inject.Inject internal class DefaultTimelineService @Inject constructor(private val roomId: String, private val monarchy: Monarchy, private val taskExecutor: TaskExecutor, - private val timelineEventFactory: TimelineEventFactory, + private val timelineEventFactory: CacheableTimelineEventFactory, private val contextOfEventTask: GetContextOfEventTask, private val paginationTask: PaginationTask ) : TimelineService { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt index c2f9827d..06ac23d5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt @@ -33,20 +33,46 @@ import timber.log.Timber import java.util.* import javax.inject.Inject +internal interface TimelineEventFactory { + fun create(eventEntity: EventEntity, realm: Realm = eventEntity.realm): TimelineEvent +} + +internal interface CacheableTimelineEventFactory : TimelineEventFactory { + fun clear() +} + /** * This class is responsible for building [TimelineEvent] returned by a [Timeline] through [TimelineService] * It handles decryption, extracting additional data around an event as sender data and relation. */ -internal class TimelineEventFactory @Inject constructor( - private val roomMemberExtractor: SenderRoomMemberExtractor, - private val relationExtractor: EventRelationExtractor, - private val cryptoService: CryptoService) { +internal class SimpleTimelineEventFactory @Inject constructor(private val roomMemberExtractor: SenderRoomMemberExtractor, + private val relationExtractor: EventRelationExtractor +) : TimelineEventFactory { + override fun create(eventEntity: EventEntity, realm: Realm): TimelineEvent { + val senderRoomMember = roomMemberExtractor.extractFrom(eventEntity, realm) + val relations = relationExtractor.extractFrom(eventEntity, realm) + return TimelineEvent( + eventEntity.asDomain(), + eventEntity.localId, + eventEntity.displayIndex, + senderRoomMember?.displayName, + /* TODO Rebase */ true, + senderRoomMember?.avatarUrl, + eventEntity.sendState, + relations + ) + } +} + +internal class InMemoryTimelineEventFactory @Inject constructor(private val roomMemberExtractor: SenderRoomMemberExtractor, + private val relationExtractor: EventRelationExtractor, + private val cryptoService: CryptoService) : CacheableTimelineEventFactory { private val timelineId = UUID.randomUUID().toString() private val senderCache = mutableMapOf() private val decryptionCache = mutableMapOf() - fun create(eventEntity: EventEntity, realm: Realm = eventEntity.realm): TimelineEvent { + override fun create(eventEntity: EventEntity, realm: Realm): TimelineEvent { val sender = eventEntity.sender val cacheKey = sender + eventEntity.localId val senderData = senderCache.getOrPut(cacheKey) { @@ -97,7 +123,7 @@ internal class TimelineEventFactory @Inject constructor( } } - fun clear() { + override fun clear() { senderCache.clear() } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt index 953bc0f8..8d23ac0f 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt @@ -504,7 +504,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro private fun observeInvitationState() { asyncSubscribe(RoomDetailViewState::asyncRoomSummary) { summary -> if (summary.membership == Membership.INVITE) { - summary.lastMessage?.senderId?.let { senderId -> + summary.latestEvent?.root?.senderId?.let { senderId -> session.getUser(senderId) }?.also { setState { copy(asyncInviter = Success(it)) } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/ChronologicalRoomComparator.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/ChronologicalRoomComparator.kt index ccbe89bf..28259065 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/ChronologicalRoomComparator.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/ChronologicalRoomComparator.kt @@ -25,14 +25,14 @@ class ChronologicalRoomComparator @Inject constructor() : Comparator>(Option.empty()) private val _openRoomLiveData = MutableLiveData>() val openRoomLiveData: LiveData> @@ -71,7 +63,6 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room fun accept(action: RoomListActions) { when (action) { is RoomListActions.SelectRoom -> handleSelectRoom(action) - is RoomListActions.FilterRooms -> handleFilterRooms(action) is RoomListActions.ToggleCategory -> handleToggleCategory(action) } } @@ -82,11 +73,6 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room _openRoomLiveData.postValue(LiveEvent(action.roomSummary.roomId)) } - private fun handleFilterRooms(action: RoomListActions.FilterRooms) { - val optionalFilter = Option.fromNullable(action.roomName) - roomListFilter.accept(optionalFilter) - } - private fun handleToggleCategory(action: RoomListActions.ToggleCategory) = setState { this.toggle(action.category) } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryController.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryController.kt index 992b697e..ab943aae 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryController.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryController.kt @@ -18,22 +18,12 @@ package im.vector.riotredesign.features.home.room.list import androidx.annotation.StringRes import com.airbnb.epoxy.TypedEpoxyController -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.RoomSummary -import im.vector.matrix.android.api.session.room.model.message.MessageContent -import im.vector.riotredesign.core.extensions.localDateTime -import im.vector.riotredesign.core.resources.DateProvider import im.vector.riotredesign.core.resources.StringProvider -import im.vector.riotredesign.features.home.AvatarRenderer -import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter -import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter import javax.inject.Inject class RoomSummaryController @Inject constructor(private val stringProvider: StringProvider, - private val eventFormatter: NoticeEventFormatter, - private val timelineDateFormatter: TimelineDateFormatter, - private val avatarRenderer: AvatarRenderer + private val roomSummaryItemFactory: RoomSummaryItemFactory ) : TypedEpoxyController() { var callback: Callback? = null @@ -85,44 +75,9 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri private fun buildRoomModels(summaries: List) { summaries.forEach { roomSummary -> - val unreadCount = roomSummary.notificationCount - val showHighlighted = roomSummary.highlightCount > 0 - - var lastMessageFormatted: CharSequence = "" - var lastMessageTime: CharSequence = "" - val lastMessage = roomSummary.lastMessage - if (lastMessage != null) { - val date = lastMessage.localDateTime() - val currentData = DateProvider.currentLocalDateTime() - val isSameDay = date.toLocalDate() == currentData.toLocalDate() - //TODO: get formatted - if (lastMessage.type == EventType.MESSAGE) { - val content = lastMessage.content?.toModel() - lastMessageFormatted = content?.body ?: "" - } else { - lastMessageFormatted = lastMessage.type - } - lastMessageTime = if (isSameDay) { - timelineDateFormatter.formatMessageHour(date) - } else { - //TODO: change this - timelineDateFormatter.formatMessageDay(date) - } - - - } - roomSummaryItem { - avatarRenderer(avatarRenderer) - id(roomSummary.roomId) - roomId(roomSummary.roomId) - lastEventTime(lastMessageTime) - lastFormattedEvent(lastMessageFormatted) - roomName(roomSummary.displayName) - avatarUrl(roomSummary.avatarUrl) - showHighlighted(showHighlighted) - unreadCount(unreadCount) - listener { callback?.onRoomSelected(roomSummary) } - } + roomSummaryItemFactory + .create(roomSummary) { callback?.onRoomSelected(it) } + .addTo(this) } } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryItemFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryItemFactory.kt new file mode 100644 index 00000000..fe66a0d7 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryItemFactory.kt @@ -0,0 +1,86 @@ +/* + * 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.riotredesign.features.home.room.list + +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.RoomSummary +import im.vector.matrix.android.api.session.room.model.message.MessageContent +import im.vector.riotredesign.R +import im.vector.riotredesign.core.extensions.localDateTime +import im.vector.riotredesign.core.resources.ColorProvider +import im.vector.riotredesign.core.resources.DateProvider +import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter +import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter +import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderName +import me.gujun.android.span.span +import javax.inject.Inject + +class RoomSummaryItemFactory @Inject constructor(private val noticeEventFormatter: NoticeEventFormatter, + private val timelineDateFormatter: TimelineDateFormatter, + private val colorProvider: ColorProvider) { + + fun create(roomSummary: RoomSummary, onRoomSelected: (RoomSummary) -> Unit): RoomSummaryItem { + val unreadCount = roomSummary.notificationCount + val showHighlighted = roomSummary.highlightCount > 0 + + var latestFormattedEvent: CharSequence = "" + var latestEventTime: CharSequence = "" + val latestEvent = roomSummary.latestEvent + if (latestEvent != null) { + val date = latestEvent.root.localDateTime() + val currentData = DateProvider.currentLocalDateTime() + val isSameDay = date.toLocalDate() == currentData.toLocalDate() + latestFormattedEvent = if (latestEvent.root.type == EventType.MESSAGE) { + val senderName = latestEvent.senderName() ?: latestEvent.root.senderId + val content = latestEvent.root.content?.toModel() + val message = content?.body ?: "" + if (roomSummary.isDirect.not() && senderName != null) { + span { + text = senderName + textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_primary) + } + .append(" - ") + .append(message) + } else { + message + } + } else { + span { + text = noticeEventFormatter.format(latestEvent) ?: "" + textStyle = "italic" + } + } + latestEventTime = if (isSameDay) { + timelineDateFormatter.formatMessageHour(date) + } else { + //TODO: change this + timelineDateFormatter.formatMessageDay(date) + } + } + return RoomSummaryItem_() + .id(roomSummary.roomId) + .roomId(roomSummary.roomId) + .lastEventTime(latestEventTime) + .lastFormattedEvent(latestFormattedEvent) + .roomName(roomSummary.displayName) + .avatarUrl(roomSummary.avatarUrl) + .showHighlighted(showHighlighted) + .unreadCount(unreadCount) + .listener { onRoomSelected(roomSummary) } + } +}