mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 13:38:49 +03:00
Merge pull request #1569 from vector-im/feature/fix_improve_epoxy
Feature/fix improve epoxy
This commit is contained in:
commit
a6f4cd74d5
19 changed files with 103 additions and 113 deletions
|
@ -19,6 +19,7 @@ package im.vector.matrix.android.api.session.room.model
|
||||||
import im.vector.matrix.android.api.crypto.RoomEncryptionTrustLevel
|
import im.vector.matrix.android.api.crypto.RoomEncryptionTrustLevel
|
||||||
import im.vector.matrix.android.api.session.room.model.tag.RoomTag
|
import im.vector.matrix.android.api.session.room.model.tag.RoomTag
|
||||||
import im.vector.matrix.android.api.session.room.send.UserDraft
|
import im.vector.matrix.android.api.session.room.send.UserDraft
|
||||||
|
import im.vector.matrix.android.api.session.room.sender.SenderInfo
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,6 +50,7 @@ data class RoomSummary constructor(
|
||||||
val userDrafts: List<UserDraft> = emptyList(),
|
val userDrafts: List<UserDraft> = emptyList(),
|
||||||
val isEncrypted: Boolean,
|
val isEncrypted: Boolean,
|
||||||
val encryptionEventTs: Long?,
|
val encryptionEventTs: Long?,
|
||||||
|
val typingUsers: List<SenderInfo>,
|
||||||
val inviterId: String? = null,
|
val inviterId: String? = null,
|
||||||
val breadcrumbsIndex: Int = NOT_IN_BREADCRUMBS,
|
val breadcrumbsIndex: Int = NOT_IN_BREADCRUMBS,
|
||||||
val roomEncryptionTrustLevel: RoomEncryptionTrustLevel? = null
|
val roomEncryptionTrustLevel: RoomEncryptionTrustLevel? = null
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.api.session.typing
|
package im.vector.matrix.android.api.session.typing
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import im.vector.matrix.android.api.session.room.sender.SenderInfo
|
import im.vector.matrix.android.api.session.room.sender.SenderInfo
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,9 +28,4 @@ interface TypingUsersTracker {
|
||||||
* Returns the sender information of all currently typing users in a room, excluding yourself.
|
* Returns the sender information of all currently typing users in a room, excluding yourself.
|
||||||
*/
|
*/
|
||||||
fun getTypingUsers(roomId: String): List<SenderInfo>
|
fun getTypingUsers(roomId: String): List<SenderInfo>
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a LiveData of the sender information of all currently typing users in a room, excluding yourself.
|
|
||||||
*/
|
|
||||||
fun getTypingUsersLive(roomId: String): LiveData<List<SenderInfo>>
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,11 @@ 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.RoomSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.tag.RoomTag
|
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.database.model.RoomSummaryEntity
|
||||||
|
import im.vector.matrix.android.internal.session.typing.DefaultTypingUsersTracker
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class RoomSummaryMapper @Inject constructor(private val timelineEventMapper: TimelineEventMapper) {
|
internal class RoomSummaryMapper @Inject constructor(private val timelineEventMapper: TimelineEventMapper,
|
||||||
|
private val typingUsersTracker: DefaultTypingUsersTracker) {
|
||||||
|
|
||||||
fun map(roomSummaryEntity: RoomSummaryEntity): RoomSummary {
|
fun map(roomSummaryEntity: RoomSummaryEntity): RoomSummary {
|
||||||
val tags = roomSummaryEntity.tags.map {
|
val tags = roomSummaryEntity.tags.map {
|
||||||
|
@ -31,6 +33,8 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa
|
||||||
val latestEvent = roomSummaryEntity.latestPreviewableEvent?.let {
|
val latestEvent = roomSummaryEntity.latestPreviewableEvent?.let {
|
||||||
timelineEventMapper.map(it, buildReadReceipts = false)
|
timelineEventMapper.map(it, buildReadReceipts = false)
|
||||||
}
|
}
|
||||||
|
// typings are updated through the sync where room summary entity gets updated no matter what, so it's ok get there
|
||||||
|
val typingUsers = typingUsersTracker.getTypingUsers(roomSummaryEntity.roomId)
|
||||||
|
|
||||||
return RoomSummary(
|
return RoomSummary(
|
||||||
roomId = roomSummaryEntity.roomId,
|
roomId = roomSummaryEntity.roomId,
|
||||||
|
@ -47,6 +51,7 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa
|
||||||
notificationCount = roomSummaryEntity.notificationCount,
|
notificationCount = roomSummaryEntity.notificationCount,
|
||||||
hasUnreadMessages = roomSummaryEntity.hasUnreadMessages,
|
hasUnreadMessages = roomSummaryEntity.hasUnreadMessages,
|
||||||
tags = tags,
|
tags = tags,
|
||||||
|
typingUsers = typingUsers,
|
||||||
membership = roomSummaryEntity.membership,
|
membership = roomSummaryEntity.membership,
|
||||||
versioningState = roomSummaryEntity.versioningState,
|
versioningState = roomSummaryEntity.versioningState,
|
||||||
readMarkerId = roomSummaryEntity.readMarkerId,
|
readMarkerId = roomSummaryEntity.readMarkerId,
|
||||||
|
|
|
@ -202,10 +202,6 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
state = newState
|
state = newState
|
||||||
// We clear typing users if the sync is not running
|
|
||||||
if (newState !is SyncState.Running) {
|
|
||||||
typingUsersTracker.clear()
|
|
||||||
}
|
|
||||||
debouncer.debounce("post_state", Runnable {
|
debouncer.debounce("post_state", Runnable {
|
||||||
liveState.value = newState
|
liveState.value = newState
|
||||||
}, 150)
|
}, 150)
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.session.typing
|
package im.vector.matrix.android.internal.session.typing
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import im.vector.matrix.android.api.session.room.sender.SenderInfo
|
import im.vector.matrix.android.api.session.room.sender.SenderInfo
|
||||||
import im.vector.matrix.android.api.session.typing.TypingUsersTracker
|
import im.vector.matrix.android.api.session.typing.TypingUsersTracker
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
import im.vector.matrix.android.internal.session.SessionScope
|
||||||
|
@ -27,7 +25,6 @@ import javax.inject.Inject
|
||||||
internal class DefaultTypingUsersTracker @Inject constructor() : TypingUsersTracker {
|
internal class DefaultTypingUsersTracker @Inject constructor() : TypingUsersTracker {
|
||||||
|
|
||||||
private val typingUsers = mutableMapOf<String, List<SenderInfo>>()
|
private val typingUsers = mutableMapOf<String, List<SenderInfo>>()
|
||||||
private val typingUsersLiveData = mutableMapOf<String, MutableLiveData<List<SenderInfo>>>()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set all currently typing users for a room (excluding yourself)
|
* Set all currently typing users for a room (excluding yourself)
|
||||||
|
@ -36,27 +33,10 @@ internal class DefaultTypingUsersTracker @Inject constructor() : TypingUsersTrac
|
||||||
val hasNewValue = typingUsers[roomId] != senderInfoList
|
val hasNewValue = typingUsers[roomId] != senderInfoList
|
||||||
if (hasNewValue) {
|
if (hasNewValue) {
|
||||||
typingUsers[roomId] = senderInfoList
|
typingUsers[roomId] = senderInfoList
|
||||||
typingUsersLiveData[roomId]?.postValue(senderInfoList)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Can be called when there is no sync so you don't get stuck with ephemeral data
|
|
||||||
*/
|
|
||||||
fun clear() {
|
|
||||||
val roomIds = typingUsers.keys
|
|
||||||
roomIds.forEach {
|
|
||||||
setTypingUsersFromRoom(it, emptyList())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTypingUsers(roomId: String): List<SenderInfo> {
|
override fun getTypingUsers(roomId: String): List<SenderInfo> {
|
||||||
return typingUsers[roomId] ?: emptyList()
|
return typingUsers[roomId] ?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTypingUsersLive(roomId: String): LiveData<List<SenderInfo>> {
|
|
||||||
return typingUsersLiveData.getOrPut(roomId) {
|
|
||||||
MutableLiveData(getTypingUsers(roomId))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,11 +22,9 @@ import im.vector.matrix.android.api.util.toMatrixItem
|
||||||
import im.vector.riotx.core.epoxy.zeroItem
|
import im.vector.riotx.core.epoxy.zeroItem
|
||||||
import im.vector.riotx.core.utils.DebouncedClickListener
|
import im.vector.riotx.core.utils.DebouncedClickListener
|
||||||
import im.vector.riotx.features.home.AvatarRenderer
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
import im.vector.riotx.features.home.room.typing.TypingHelper
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class BreadcrumbsController @Inject constructor(
|
class BreadcrumbsController @Inject constructor(
|
||||||
private val typingHelper: TypingHelper,
|
|
||||||
private val avatarRenderer: AvatarRenderer
|
private val avatarRenderer: AvatarRenderer
|
||||||
) : EpoxyController() {
|
) : EpoxyController() {
|
||||||
|
|
||||||
|
@ -59,12 +57,12 @@ class BreadcrumbsController @Inject constructor(
|
||||||
?.forEach {
|
?.forEach {
|
||||||
breadcrumbsItem {
|
breadcrumbsItem {
|
||||||
id(it.roomId)
|
id(it.roomId)
|
||||||
|
hasTypingUsers(it.typingUsers.isNotEmpty())
|
||||||
avatarRenderer(avatarRenderer)
|
avatarRenderer(avatarRenderer)
|
||||||
matrixItem(it.toMatrixItem())
|
matrixItem(it.toMatrixItem())
|
||||||
unreadNotificationCount(it.notificationCount)
|
unreadNotificationCount(it.notificationCount)
|
||||||
showHighlighted(it.highlightCount > 0)
|
showHighlighted(it.highlightCount > 0)
|
||||||
hasUnreadMessage(it.hasUnreadMessages)
|
hasUnreadMessage(it.hasUnreadMessages)
|
||||||
typingHelper(typingHelper)
|
|
||||||
hasDraft(it.userDrafts.isNotEmpty())
|
hasDraft(it.userDrafts.isNotEmpty())
|
||||||
itemClickListener(
|
itemClickListener(
|
||||||
DebouncedClickListener(View.OnClickListener { _ ->
|
DebouncedClickListener(View.OnClickListener { _ ->
|
||||||
|
|
|
@ -26,22 +26,20 @@ import im.vector.matrix.android.api.util.MatrixItem
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||||
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||||
import im.vector.riotx.core.extensions.observeNotNull
|
|
||||||
import im.vector.riotx.features.home.AvatarRenderer
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
import im.vector.riotx.features.home.room.list.UnreadCounterBadgeView
|
import im.vector.riotx.features.home.room.list.UnreadCounterBadgeView
|
||||||
import im.vector.riotx.features.home.room.typing.TypingHelper
|
|
||||||
|
|
||||||
@EpoxyModelClass(layout = R.layout.item_breadcrumbs)
|
@EpoxyModelClass(layout = R.layout.item_breadcrumbs)
|
||||||
abstract class BreadcrumbsItem : VectorEpoxyModel<BreadcrumbsItem.Holder>() {
|
abstract class BreadcrumbsItem : VectorEpoxyModel<BreadcrumbsItem.Holder>() {
|
||||||
|
|
||||||
@EpoxyAttribute lateinit var typingHelper: TypingHelper
|
@EpoxyAttribute var hasTypingUsers: Boolean = false
|
||||||
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
|
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
|
||||||
@EpoxyAttribute lateinit var matrixItem: MatrixItem
|
@EpoxyAttribute lateinit var matrixItem: MatrixItem
|
||||||
@EpoxyAttribute var unreadNotificationCount: Int = 0
|
@EpoxyAttribute var unreadNotificationCount: Int = 0
|
||||||
@EpoxyAttribute var showHighlighted: Boolean = false
|
@EpoxyAttribute var showHighlighted: Boolean = false
|
||||||
@EpoxyAttribute var hasUnreadMessage: Boolean = false
|
@EpoxyAttribute var hasUnreadMessage: Boolean = false
|
||||||
@EpoxyAttribute var hasDraft: Boolean = false
|
@EpoxyAttribute var hasDraft: Boolean = false
|
||||||
@EpoxyAttribute var itemClickListener: View.OnClickListener? = null
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var itemClickListener: View.OnClickListener? = null
|
||||||
|
|
||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
super.bind(holder)
|
super.bind(holder)
|
||||||
|
@ -50,9 +48,12 @@ abstract class BreadcrumbsItem : VectorEpoxyModel<BreadcrumbsItem.Holder>() {
|
||||||
avatarRenderer.render(matrixItem, holder.avatarImageView)
|
avatarRenderer.render(matrixItem, holder.avatarImageView)
|
||||||
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted))
|
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted))
|
||||||
holder.draftIndentIndicator.isVisible = hasDraft
|
holder.draftIndentIndicator.isVisible = hasDraft
|
||||||
typingHelper.hasTypingUsers(matrixItem.id).observeNotNull(this) { hasTypingUsers ->
|
holder.typingIndicator.isVisible = hasTypingUsers
|
||||||
holder.typingIndicator.isVisible = hasTypingUsers
|
}
|
||||||
}
|
|
||||||
|
override fun unbind(holder: Holder) {
|
||||||
|
holder.rootView.setOnClickListener(null)
|
||||||
|
super.unbind(holder)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Holder : VectorEpoxyHolder() {
|
class Holder : VectorEpoxyHolder() {
|
||||||
|
|
|
@ -59,7 +59,6 @@ import im.vector.matrix.android.api.session.room.timeline.getTextEditableContent
|
||||||
import im.vector.matrix.android.api.util.toOptional
|
import im.vector.matrix.android.api.util.toOptional
|
||||||
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
|
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
|
||||||
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
|
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
|
||||||
import im.vector.matrix.rx.asObservable
|
|
||||||
import im.vector.matrix.rx.rx
|
import im.vector.matrix.rx.rx
|
||||||
import im.vector.matrix.rx.unwrap
|
import im.vector.matrix.rx.unwrap
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
@ -98,12 +97,12 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
userPreferencesProvider: UserPreferencesProvider,
|
userPreferencesProvider: UserPreferencesProvider,
|
||||||
private val vectorPreferences: VectorPreferences,
|
private val vectorPreferences: VectorPreferences,
|
||||||
private val stringProvider: StringProvider,
|
private val stringProvider: StringProvider,
|
||||||
private val typingHelper: TypingHelper,
|
|
||||||
private val rainbowGenerator: RainbowGenerator,
|
private val rainbowGenerator: RainbowGenerator,
|
||||||
private val session: Session,
|
private val session: Session,
|
||||||
private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider,
|
private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider,
|
||||||
private val stickerPickerActionHandler: StickerPickerActionHandler,
|
private val stickerPickerActionHandler: StickerPickerActionHandler,
|
||||||
private val roomSummaryHolder: RoomSummaryHolder,
|
private val roomSummaryHolder: RoomSummaryHolder,
|
||||||
|
private val typingHelper: TypingHelper,
|
||||||
private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager
|
private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager
|
||||||
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState), Timeline.Listener {
|
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState), Timeline.Listener {
|
||||||
|
|
||||||
|
@ -167,7 +166,6 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
observeSummaryState()
|
observeSummaryState()
|
||||||
getUnreadState()
|
getUnreadState()
|
||||||
observeSyncState()
|
observeSyncState()
|
||||||
observeTypings()
|
|
||||||
observeEventDisplayedActions()
|
observeEventDisplayedActions()
|
||||||
observeDrafts()
|
observeDrafts()
|
||||||
observeUnreadState()
|
observeUnreadState()
|
||||||
|
@ -1061,15 +1059,6 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeTypings() {
|
|
||||||
typingHelper.getTypingMessage(initialState.roomId)
|
|
||||||
.asObservable()
|
|
||||||
.subscribe {
|
|
||||||
setState { copy(typingMessage = it) }
|
|
||||||
}
|
|
||||||
.disposeOnClear()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getUnreadState() {
|
private fun getUnreadState() {
|
||||||
Observable
|
Observable
|
||||||
.combineLatest<List<TimelineEvent>, RoomSummary, UnreadState>(
|
.combineLatest<List<TimelineEvent>, RoomSummary, UnreadState>(
|
||||||
|
@ -1127,8 +1116,11 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
private fun observeSummaryState() {
|
private fun observeSummaryState() {
|
||||||
asyncSubscribe(RoomDetailViewState::asyncRoomSummary) { summary ->
|
asyncSubscribe(RoomDetailViewState::asyncRoomSummary) { summary ->
|
||||||
|
|
||||||
roomSummaryHolder.set(summary)
|
roomSummaryHolder.set(summary)
|
||||||
|
setState {
|
||||||
|
val typingMessage = typingHelper.getTypingMessage(summary.typingUsers)
|
||||||
|
copy(typingMessage = typingMessage)
|
||||||
|
}
|
||||||
if (summary.membership == Membership.INVITE) {
|
if (summary.membership == Membership.INVITE) {
|
||||||
summary.inviterId?.let { inviterId ->
|
summary.inviterId?.let { inviterId ->
|
||||||
session.getUser(inviterId)
|
session.getUser(inviterId)
|
||||||
|
|
|
@ -20,6 +20,7 @@ import androidx.annotation.ColorInt
|
||||||
import im.vector.matrix.android.api.session.room.send.SendState
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.resources.ColorProvider
|
import im.vector.riotx.core.resources.ColorProvider
|
||||||
|
import im.vector.riotx.core.utils.getColorFromUserId
|
||||||
import im.vector.riotx.features.settings.VectorPreferences
|
import im.vector.riotx.features.settings.VectorPreferences
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -27,6 +28,11 @@ class MessageColorProvider @Inject constructor(
|
||||||
private val colorProvider: ColorProvider,
|
private val colorProvider: ColorProvider,
|
||||||
private val vectorPreferences: VectorPreferences) {
|
private val vectorPreferences: VectorPreferences) {
|
||||||
|
|
||||||
|
@ColorInt
|
||||||
|
fun getMemberNameTextColor(userId: String): Int {
|
||||||
|
return colorProvider.getColor(getColorFromUserId(userId))
|
||||||
|
}
|
||||||
|
|
||||||
@ColorInt
|
@ColorInt
|
||||||
fun getMessageTextColor(sendState: SendState): Int {
|
fun getMessageTextColor(sendState: SendState): Int {
|
||||||
return if (vectorPreferences.developerMode()) {
|
return if (vectorPreferences.developerMode()) {
|
||||||
|
|
|
@ -33,14 +33,12 @@ import im.vector.matrix.android.internal.session.room.VerificationState
|
||||||
import im.vector.riotx.core.date.VectorDateFormatter
|
import im.vector.riotx.core.date.VectorDateFormatter
|
||||||
import im.vector.riotx.core.extensions.localDateTime
|
import im.vector.riotx.core.extensions.localDateTime
|
||||||
import im.vector.riotx.core.resources.ColorProvider
|
import im.vector.riotx.core.resources.ColorProvider
|
||||||
import im.vector.riotx.core.utils.getColorFromUserId
|
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.item.E2EDecoration
|
import im.vector.riotx.features.home.room.detail.timeline.item.E2EDecoration
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.item.PollResponseData
|
import im.vector.riotx.features.home.room.detail.timeline.item.PollResponseData
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.item.ReactionInfoData
|
import im.vector.riotx.features.home.room.detail.timeline.item.ReactionInfoData
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData
|
import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.item.ReferencesInfoData
|
import im.vector.riotx.features.home.room.detail.timeline.item.ReferencesInfoData
|
||||||
import me.gujun.android.span.span
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,11 +69,8 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
|
||||||
|| isTileTypeMessage(nextEvent)
|
|| isTileTypeMessage(nextEvent)
|
||||||
|
|
||||||
val time = dateFormatter.formatMessageHour(date)
|
val time = dateFormatter.formatMessageHour(date)
|
||||||
val formattedMemberName = span(event.senderInfo.disambiguatedDisplayName) {
|
|
||||||
textColor = colorProvider.getColor(getColorFromUserId(event.root.senderId))
|
|
||||||
}
|
|
||||||
|
|
||||||
val e2eDecoration = getE2EDecoration(event)
|
val e2eDecoration = getE2EDecoration(event)
|
||||||
|
|
||||||
return MessageInformationData(
|
return MessageInformationData(
|
||||||
eventId = eventId,
|
eventId = eventId,
|
||||||
senderId = event.root.senderId ?: "",
|
senderId = event.root.senderId ?: "",
|
||||||
|
@ -83,7 +78,7 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
|
||||||
time = time,
|
time = time,
|
||||||
ageLocalTS = event.root.ageLocalTs,
|
ageLocalTS = event.root.ageLocalTs,
|
||||||
avatarUrl = event.senderInfo.avatarUrl,
|
avatarUrl = event.senderInfo.avatarUrl,
|
||||||
memberName = formattedMemberName,
|
memberName = event.senderInfo.disambiguatedDisplayName,
|
||||||
showInformation = showInformation,
|
showInformation = showInformation,
|
||||||
orderedReactionList = event.annotations?.reactionsSummary
|
orderedReactionList = event.annotations?.reactionsSummary
|
||||||
// ?.filter { isSingleEmoji(it.key) }
|
// ?.filter { isSingleEmoji(it.key) }
|
||||||
|
|
|
@ -109,6 +109,7 @@ abstract class AbsBaseMessageItem<H : AbsBaseMessageItem.Holder> : BaseEventItem
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun unbind(holder: H) {
|
override fun unbind(holder: H) {
|
||||||
|
holder.reactionsContainer.setOnLongClickListener(null)
|
||||||
holder.readReceiptsView.unbind()
|
holder.readReceiptsView.unbind()
|
||||||
super.unbind(holder)
|
super.unbind(holder)
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
|
||||||
holder.timeView.visibility = View.VISIBLE
|
holder.timeView.visibility = View.VISIBLE
|
||||||
holder.timeView.text = attributes.informationData.time
|
holder.timeView.text = attributes.informationData.time
|
||||||
holder.memberNameView.text = attributes.informationData.memberName
|
holder.memberNameView.text = attributes.informationData.memberName
|
||||||
|
holder.memberNameView.setTextColor(attributes.getMemberNameColor())
|
||||||
attributes.avatarRenderer.render(attributes.informationData.matrixItem, holder.avatarImageView)
|
attributes.avatarRenderer.render(attributes.informationData.matrixItem, holder.avatarImageView)
|
||||||
holder.avatarImageView.setOnLongClickListener(attributes.itemLongClickListener)
|
holder.avatarImageView.setOnLongClickListener(attributes.itemLongClickListener)
|
||||||
holder.memberNameView.setOnLongClickListener(attributes.itemLongClickListener)
|
holder.memberNameView.setOnLongClickListener(attributes.itemLongClickListener)
|
||||||
|
@ -75,6 +76,16 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun unbind(holder: H) {
|
||||||
|
holder.avatarImageView.setOnClickListener(null)
|
||||||
|
holder.avatarImageView.setOnLongClickListener(null)
|
||||||
|
holder.memberNameView.setOnClickListener(null)
|
||||||
|
holder.memberNameView.setOnLongClickListener(null)
|
||||||
|
super.unbind(holder)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Attributes.getMemberNameColor() = messageColorProvider.getMemberNameTextColor(informationData.senderId)
|
||||||
|
|
||||||
abstract class Holder(@IdRes stubId: Int) : AbsBaseMessageItem.Holder(stubId) {
|
abstract class Holder(@IdRes stubId: Int) : AbsBaseMessageItem.Holder(stubId) {
|
||||||
val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView)
|
val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView)
|
||||||
val memberNameView by bind<TextView>(R.id.messageMemberNameView)
|
val memberNameView by bind<TextView>(R.id.messageMemberNameView)
|
||||||
|
@ -96,5 +107,25 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
|
||||||
val avatarCallback: TimelineEventController.AvatarCallback? = null,
|
val avatarCallback: TimelineEventController.AvatarCallback? = null,
|
||||||
override val readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null,
|
override val readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null,
|
||||||
val emojiTypeFace: Typeface? = null
|
val emojiTypeFace: Typeface? = null
|
||||||
) : AbsBaseMessageItem.Attributes
|
) : AbsBaseMessageItem.Attributes {
|
||||||
|
|
||||||
|
// Have to override as it's used to diff epoxy items
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as Attributes
|
||||||
|
|
||||||
|
if (avatarSize != other.avatarSize) return false
|
||||||
|
if (informationData != other.informationData) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = avatarSize
|
||||||
|
result = 31 * result + informationData.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
open var leftGuideline: Int = 0
|
open var leftGuideline: Int = 0
|
||||||
|
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||||
lateinit var dimensionConverter: DimensionConverter
|
lateinit var dimensionConverter: DimensionConverter
|
||||||
|
|
||||||
@CallSuper
|
@CallSuper
|
||||||
|
|
|
@ -36,7 +36,7 @@ abstract class MessageFileItem : AbsMessageItem<MessageFileItem.Holder>() {
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
@DrawableRes
|
@DrawableRes
|
||||||
var iconRes: Int = 0
|
var iconRes: Int = 0
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||||
var clickListener: View.OnClickListener? = null
|
var clickListener: View.OnClickListener? = null
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var izLocalFile = false
|
var izLocalFile = false
|
||||||
|
|
|
@ -37,7 +37,7 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
|
||||||
var playable: Boolean = false
|
var playable: Boolean = false
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var mode = ImageContentRenderer.Mode.THUMBNAIL
|
var mode = ImageContentRenderer.Mode.THUMBNAIL
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||||
var clickListener: View.OnClickListener? = null
|
var clickListener: View.OnClickListener? = null
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
lateinit var imageContentRenderer: ImageContentRenderer
|
lateinit var imageContentRenderer: ImageContentRenderer
|
||||||
|
@ -65,6 +65,8 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
|
||||||
override fun unbind(holder: Holder) {
|
override fun unbind(holder: Holder) {
|
||||||
GlideApp.with(holder.view.context.applicationContext).clear(holder.imageView)
|
GlideApp.with(holder.view.context.applicationContext).clear(holder.imageView)
|
||||||
contentUploadStateTrackerBinder.unbind(attributes.informationData.eventId)
|
contentUploadStateTrackerBinder.unbind(attributes.informationData.eventId)
|
||||||
|
holder.imageView.setOnClickListener(null)
|
||||||
|
holder.imageView.setOnLongClickListener(null)
|
||||||
super.unbind(holder)
|
super.unbind(holder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
|
||||||
var message: CharSequence? = null
|
var message: CharSequence? = null
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var useBigFont: Boolean = false
|
var useBigFont: Boolean = false
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||||
var movementMethod: MovementMethod? = null
|
var movementMethod: MovementMethod? = null
|
||||||
|
|
||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
|
|
|
@ -32,28 +32,28 @@ import im.vector.matrix.android.api.util.MatrixItem
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||||
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||||
import im.vector.riotx.core.extensions.observeK
|
|
||||||
import im.vector.riotx.core.extensions.setTextOrHide
|
import im.vector.riotx.core.extensions.setTextOrHide
|
||||||
import im.vector.riotx.features.crypto.util.toImageRes
|
import im.vector.riotx.features.crypto.util.toImageRes
|
||||||
import im.vector.riotx.features.home.AvatarRenderer
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
import im.vector.riotx.features.home.room.typing.TypingHelper
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
@EpoxyModelClass(layout = R.layout.item_room)
|
@EpoxyModelClass(layout = R.layout.item_room)
|
||||||
abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
|
abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
|
||||||
|
|
||||||
@EpoxyAttribute lateinit var typingHelper: TypingHelper
|
@EpoxyAttribute lateinit var typingMessage: CharSequence
|
||||||
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
|
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
|
||||||
@EpoxyAttribute lateinit var matrixItem: MatrixItem
|
@EpoxyAttribute lateinit var matrixItem: MatrixItem
|
||||||
@EpoxyAttribute lateinit var lastFormattedEvent: CharSequence
|
// Used only for diff calculation
|
||||||
|
@EpoxyAttribute lateinit var lastEvent: String
|
||||||
|
// We use DoNotHash here as Spans are not implementing equals/hashcode
|
||||||
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) lateinit var lastFormattedEvent: CharSequence
|
||||||
@EpoxyAttribute lateinit var lastEventTime: CharSequence
|
@EpoxyAttribute lateinit var lastEventTime: CharSequence
|
||||||
@EpoxyAttribute var encryptionTrustLevel: RoomEncryptionTrustLevel? = null
|
@EpoxyAttribute var encryptionTrustLevel: RoomEncryptionTrustLevel? = null
|
||||||
@EpoxyAttribute var unreadNotificationCount: Int = 0
|
@EpoxyAttribute var unreadNotificationCount: Int = 0
|
||||||
@EpoxyAttribute var hasUnreadMessage: Boolean = false
|
@EpoxyAttribute var hasUnreadMessage: Boolean = false
|
||||||
@EpoxyAttribute var hasDraft: Boolean = false
|
@EpoxyAttribute var hasDraft: Boolean = false
|
||||||
@EpoxyAttribute var showHighlighted: Boolean = false
|
@EpoxyAttribute var showHighlighted: Boolean = false
|
||||||
@EpoxyAttribute var itemLongClickListener: View.OnLongClickListener? = null
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var itemLongClickListener: View.OnLongClickListener? = null
|
||||||
@EpoxyAttribute var itemClickListener: View.OnClickListener? = null
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var itemClickListener: View.OnClickListener? = null
|
||||||
@EpoxyAttribute var showSelected: Boolean = false
|
@EpoxyAttribute var showSelected: Boolean = false
|
||||||
|
|
||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
|
@ -73,11 +73,14 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
|
||||||
holder.roomAvatarDecorationImageView.isVisible = encryptionTrustLevel != null
|
holder.roomAvatarDecorationImageView.isVisible = encryptionTrustLevel != null
|
||||||
holder.roomAvatarDecorationImageView.setImageResource(encryptionTrustLevel.toImageRes())
|
holder.roomAvatarDecorationImageView.setImageResource(encryptionTrustLevel.toImageRes())
|
||||||
renderSelection(holder, showSelected)
|
renderSelection(holder, showSelected)
|
||||||
typingHelper.getTypingMessage(matrixItem.id).observeK(this) {
|
holder.typingView.setTextOrHide(typingMessage)
|
||||||
Timber.v("Observe typing for room ${matrixItem.id}: $it")
|
holder.lastEventView.isInvisible = holder.typingView.isVisible
|
||||||
holder.typingView.setTextOrHide(it)
|
}
|
||||||
holder.lastEventView.isInvisible = holder.typingView.isVisible
|
|
||||||
}
|
override fun unbind(holder: Holder) {
|
||||||
|
holder.rootView.setOnClickListener(null)
|
||||||
|
holder.rootView.setOnLongClickListener(null)
|
||||||
|
super.unbind(holder)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderSelection(holder: Holder, isSelected: Boolean) {
|
private fun renderSelection(holder: Holder, isSelected: Boolean) {
|
||||||
|
|
|
@ -102,13 +102,15 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
|
||||||
dateFormatter.formatMessageDay(date)
|
dateFormatter.formatMessageDay(date)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val typingMessage = typingHelper.getTypingMessage(roomSummary.typingUsers)
|
||||||
return RoomSummaryItem_()
|
return RoomSummaryItem_()
|
||||||
.id(roomSummary.roomId)
|
.id(roomSummary.roomId)
|
||||||
.avatarRenderer(avatarRenderer)
|
.avatarRenderer(avatarRenderer)
|
||||||
.encryptionTrustLevel(roomSummary.roomEncryptionTrustLevel)
|
.encryptionTrustLevel(roomSummary.roomEncryptionTrustLevel)
|
||||||
.matrixItem(roomSummary.toMatrixItem())
|
.matrixItem(roomSummary.toMatrixItem())
|
||||||
.lastEventTime(latestEventTime)
|
.lastEventTime(latestEventTime)
|
||||||
.typingHelper(typingHelper)
|
.typingMessage(typingMessage)
|
||||||
|
.lastEvent(latestFormattedEvent.toString())
|
||||||
.lastFormattedEvent(latestFormattedEvent)
|
.lastFormattedEvent(latestFormattedEvent)
|
||||||
.showHighlighted(showHighlighted)
|
.showHighlighted(showHighlighted)
|
||||||
.showSelected(showSelected)
|
.showSelected(showSelected)
|
||||||
|
|
|
@ -16,48 +16,30 @@
|
||||||
|
|
||||||
package im.vector.riotx.features.home.room.typing
|
package im.vector.riotx.features.home.room.typing
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import im.vector.matrix.android.api.session.room.sender.SenderInfo
|
||||||
import androidx.lifecycle.Transformations
|
|
||||||
import im.vector.matrix.android.api.session.Session
|
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.resources.StringProvider
|
import im.vector.riotx.core.resources.StringProvider
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class TypingHelper @Inject constructor(
|
class TypingHelper @Inject constructor(private val stringProvider: StringProvider) {
|
||||||
private val session: Session,
|
|
||||||
private val stringProvider: StringProvider
|
|
||||||
) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if some users are currently typing in the room (excluding yourself).
|
|
||||||
*/
|
|
||||||
fun hasTypingUsers(roomId: String): LiveData<Boolean> {
|
|
||||||
val liveData = session.typingUsersTracker().getTypingUsersLive(roomId)
|
|
||||||
return Transformations.map(liveData) {
|
|
||||||
it.isNotEmpty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a human readable String of currently typing users in the room (excluding yourself).
|
* Returns a human readable String of currently typing users in the room (excluding yourself).
|
||||||
*/
|
*/
|
||||||
fun getTypingMessage(roomId: String): LiveData<String> {
|
fun getTypingMessage(typingUsers: List<SenderInfo>): String {
|
||||||
val liveData = session.typingUsersTracker().getTypingUsersLive(roomId)
|
return when {
|
||||||
return Transformations.map(liveData) { typingUsers ->
|
typingUsers.isEmpty() ->
|
||||||
when {
|
""
|
||||||
typingUsers.isEmpty() ->
|
typingUsers.size == 1 ->
|
||||||
""
|
stringProvider.getString(R.string.room_one_user_is_typing, typingUsers[0].disambiguatedDisplayName)
|
||||||
typingUsers.size == 1 ->
|
typingUsers.size == 2 ->
|
||||||
stringProvider.getString(R.string.room_one_user_is_typing, typingUsers[0].disambiguatedDisplayName)
|
stringProvider.getString(R.string.room_two_users_are_typing,
|
||||||
typingUsers.size == 2 ->
|
typingUsers[0].disambiguatedDisplayName,
|
||||||
stringProvider.getString(R.string.room_two_users_are_typing,
|
typingUsers[1].disambiguatedDisplayName)
|
||||||
typingUsers[0].disambiguatedDisplayName,
|
else ->
|
||||||
typingUsers[1].disambiguatedDisplayName)
|
stringProvider.getString(R.string.room_many_users_are_typing,
|
||||||
else ->
|
typingUsers[0].disambiguatedDisplayName,
|
||||||
stringProvider.getString(R.string.room_many_users_are_typing,
|
typingUsers[1].disambiguatedDisplayName)
|
||||||
typingUsers[0].disambiguatedDisplayName,
|
|
||||||
typingUsers[1].disambiguatedDisplayName)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue