Merge pull request #188 from vector-im/feature/disambiguation

Disambiguation of display names
This commit is contained in:
Benoit Marty 2019-06-20 17:29:01 +02:00 committed by GitHub
commit 1eb374fa49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 78 additions and 122 deletions

View File

@ -59,7 +59,7 @@ object RoomDataHelper {
eventId = Random.nextLong().toString(), eventId = Random.nextLong().toString(),
content = content, content = content,
prevContent = prevContent, prevContent = prevContent,
sender = sender, senderId = sender,
stateKey = stateKey stateKey = stateKey
) )
} }

View File

@ -71,12 +71,11 @@ data class Event(
@Json(name = "content") val content: Content? = null, @Json(name = "content") val content: Content? = null,
@Json(name = "prev_content") val prevContent: Content? = null, @Json(name = "prev_content") val prevContent: Content? = null,
@Json(name = "origin_server_ts") val originServerTs: Long? = null, @Json(name = "origin_server_ts") val originServerTs: Long? = null,
@Json(name = "sender") val sender: String? = null, @Json(name = "sender") val senderId: String? = null,
@Json(name = "state_key") val stateKey: String? = null, @Json(name = "state_key") val stateKey: String? = null,
@Json(name = "room_id") val roomId: String? = null, @Json(name = "room_id") val roomId: String? = null,
@Json(name = "unsigned") val unsignedData: UnsignedData? = null, @Json(name = "unsigned") val unsignedData: UnsignedData? = null,
@Json(name = "redacts") val redacts: String? = null @Json(name = "redacts") val redacts: String? = null

) { ) {


/** /**

View File

@ -31,6 +31,7 @@ data class TimelineEvent(
val localId: String, val localId: String,
val displayIndex: Int, val displayIndex: Int,
val senderName: String?, val senderName: String?,
val isUniqueDisplayName: Boolean,
val senderAvatar: String?, val senderAvatar: String?,
val sendState: SendState, val sendState: SendState,
val annotations: EventAnnotationsSummary? = null val annotations: EventAnnotationsSummary? = null
@ -53,6 +54,18 @@ data class TimelineEvent(
} }
} }


fun getDisambiguatedDisplayName(): String {
return if (isUniqueDisplayName) {
senderName
} else {
senderName?.let { name ->
"$name (${root.senderId})"
}
}
?: root.senderId
?: ""
}

/** /**
* Get the metadata associated with a key. * Get the metadata associated with a key.
* @param key the key to get the metadata * @param key the key to get the metadata

View File

@ -65,7 +65,7 @@ open class IncomingRoomKeyRequest {
* @param event the event * @param event the event
*/ */
constructor(event: Event) { constructor(event: Event) {
userId = event.sender userId = event.senderId
val roomKeyShareRequest = event.getClearContent().toModel<RoomKeyShareRequest>()!! val roomKeyShareRequest = event.getClearContent().toModel<RoomKeyShareRequest>()!!
deviceId = roomKeyShareRequest.requestingDeviceId deviceId = roomKeyShareRequest.requestingDeviceId
requestId = roomKeyShareRequest.requestId requestId = roomKeyShareRequest.requestId

View File

@ -131,7 +131,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
* @param event the event * @param event the event
*/ */
private fun requestKeysForEvent(event: Event) { private fun requestKeysForEvent(event: Event) {
val sender = event.sender!! val sender = event.senderId!!
val encryptedEventContent = event.content.toModel<EncryptedEventContent>()!! val encryptedEventContent = event.content.toModel<EncryptedEventContent>()!!


val recipients = ArrayList<Map<String, String>>() val recipients = ArrayList<Map<String, String>>()

View File

@ -121,9 +121,9 @@ internal class MXOlmDecryption(
MXCryptoError.UNABLE_TO_DECRYPT, String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "sender"))) MXCryptoError.UNABLE_TO_DECRYPT, String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "sender")))
} }


if (!TextUtils.equals(olmPayloadContent.sender, event.sender)) { if (!TextUtils.equals(olmPayloadContent.sender, event.senderId)) {
Timber.e("Event " + event.eventId + ": original sender " + olmPayloadContent.sender Timber.e("Event " + event.eventId + ": original sender " + olmPayloadContent.sender
+ " does not match reported sender " + event.sender) + " does not match reported sender " + event.senderId)
throw MXDecryptionException(MXCryptoError(MXCryptoError.FORWARDED_MESSAGE_ERROR_CODE, throw MXDecryptionException(MXCryptoError(MXCryptoError.FORWARDED_MESSAGE_ERROR_CODE,
MXCryptoError.UNABLE_TO_DECRYPT, String.format(MXCryptoError.FORWARDED_MESSAGE_REASON, olmPayloadContent.sender))) MXCryptoError.UNABLE_TO_DECRYPT, String.format(MXCryptoError.FORWARDED_MESSAGE_REASON, olmPayloadContent.sender)))
} }

View File

@ -148,7 +148,7 @@ internal class DefaultSasVerificationService(private val credentials: Credential
private suspend fun onStartRequestReceived(event: Event) { private suspend fun onStartRequestReceived(event: Event) {
val startReq = event.getClearContent().toModel<KeyVerificationStart>()!! val startReq = event.getClearContent().toModel<KeyVerificationStart>()!!


val otherUserId = event.sender val otherUserId = event.senderId
if (!startReq.isValid()) { if (!startReq.isValid()) {
Timber.e("## received invalid verification request") Timber.e("## received invalid verification request")
if (startReq.transactionID != null) { if (startReq.transactionID != null) {
@ -236,7 +236,7 @@ internal class DefaultSasVerificationService(private val credentials: Credential
Timber.e("## Received invalid accept request") Timber.e("## Received invalid accept request")
return return
} }
val otherUserId = event.sender!! val otherUserId = event.senderId!!


Timber.v("## SAS onCancelReceived otherUser:$otherUserId reason:${cancelReq.reason}") Timber.v("## SAS onCancelReceived otherUser:$otherUserId reason:${cancelReq.reason}")
val existing = getExistingTransaction(otherUserId, cancelReq.transactionID!!) val existing = getExistingTransaction(otherUserId, cancelReq.transactionID!!)
@ -258,7 +258,7 @@ internal class DefaultSasVerificationService(private val credentials: Credential
Timber.e("## Received invalid accept request") Timber.e("## Received invalid accept request")
return return
} }
val otherUserId = event.sender!! val otherUserId = event.senderId!!
val existing = getExistingTransaction(otherUserId, acceptReq.transactionID!!) val existing = getExistingTransaction(otherUserId, acceptReq.transactionID!!)
if (existing == null) { if (existing == null) {
Timber.e("## Received invalid accept request") Timber.e("## Received invalid accept request")
@ -282,7 +282,7 @@ internal class DefaultSasVerificationService(private val credentials: Credential
Timber.e("## Received invalid key request") Timber.e("## Received invalid key request")
return return
} }
val otherUserId = event.sender!! val otherUserId = event.senderId!!
val existing = getExistingTransaction(otherUserId, keyReq.transactionID!!) val existing = getExistingTransaction(otherUserId, keyReq.transactionID!!)
if (existing == null) { if (existing == null) {
Timber.e("## Received invalid accept request") Timber.e("## Received invalid accept request")
@ -303,7 +303,7 @@ internal class DefaultSasVerificationService(private val credentials: Credential
Timber.e("## Received invalid key request") Timber.e("## Received invalid key request")
return return
} }
val otherUserId = event.sender!! val otherUserId = event.senderId!!
val existing = getExistingTransaction(otherUserId, macReq.transactionID!!) val existing = getExistingTransaction(otherUserId, macReq.transactionID!!)
if (existing == null) { if (existing == null) {
Timber.e("## Received invalid accept request") Timber.e("## Received invalid accept request")

View File

@ -37,7 +37,7 @@ internal object EventMapper {
eventEntity.prevContent = ContentMapper.map(resolvedPrevContent) eventEntity.prevContent = ContentMapper.map(resolvedPrevContent)
eventEntity.stateKey = event.stateKey eventEntity.stateKey = event.stateKey
eventEntity.type = event.getClearType() eventEntity.type = event.getClearType()
eventEntity.sender = event.sender eventEntity.sender = event.senderId
eventEntity.originServerTs = event.originServerTs eventEntity.originServerTs = event.originServerTs
eventEntity.redacts = event.redacts eventEntity.redacts = event.redacts
eventEntity.age = event.unsignedData?.age ?: event.originServerTs eventEntity.age = event.unsignedData?.age ?: event.originServerTs
@ -63,7 +63,7 @@ internal object EventMapper {
content = ContentMapper.map(eventEntity.content), content = ContentMapper.map(eventEntity.content),
prevContent = ContentMapper.map(eventEntity.prevContent), prevContent = ContentMapper.map(eventEntity.prevContent),
originServerTs = eventEntity.originServerTs, originServerTs = eventEntity.originServerTs,
sender = eventEntity.sender, senderId = eventEntity.sender,
stateKey = eventEntity.stateKey, stateKey = eventEntity.stateKey,
roomId = eventEntity.roomId, roomId = eventEntity.roomId,
unsignedData = ud, unsignedData = ud,

View File

@ -227,7 +227,7 @@ internal class DefaultEventRelationsAggregationTask(private val monarchy: Monarc
sum.count = 1 sum.count = 1
sum.sourceEvents.add(reactionEventId) sum.sourceEvents.add(reactionEventId)
} }
sum.addedByMe = sum.addedByMe || (userId == event.sender) sum.addedByMe = sum.addedByMe || (userId == event.senderId)
eventSummary.reactionsSummary.add(sum) eventSummary.reactionsSummary.add(sum)
} else { } else {
//is this a known event (is possible? pagination?) //is this a known event (is possible? pagination?)
@ -249,7 +249,7 @@ internal class DefaultEventRelationsAggregationTask(private val monarchy: Monarc
sum.sourceEvents.add(reactionEventId) sum.sourceEvents.add(reactionEventId)
} }


sum.addedByMe = sum.addedByMe || (userId == event.sender) sum.addedByMe = sum.addedByMe || (userId == event.senderId)
} }


} }

View File

@ -54,7 +54,7 @@ internal class RoomMembers(private val realm: Realm,
} }
return EventEntity return EventEntity
.where(realm, roomId, EventType.STATE_ROOM_MEMBER) .where(realm, roomId, EventType.STATE_ROOM_MEMBER)
.contains(EventEntityFields.CONTENT, displayName) .contains(EventEntityFields.CONTENT, "\"displayname\":\"$displayName\"")
.distinct(EventEntityFields.STATE_KEY) .distinct(EventEntityFields.STATE_KEY)
.findAll() .findAll()
.size == 1 .size == 1

View File

@ -1,59 +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.matrix.android.internal.session.room.send

import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
import im.vector.matrix.android.internal.di.MoshiProvider

internal class EventFactory(private val credentials: Credentials) {

private val moshi = MoshiProvider.providesMoshi()

fun createTextEvent(roomId: String, msgType: String, text: String): Event {
val content = MessageTextContent(type = msgType, body = text)

return Event(
roomId = roomId,
originServerTs = dummyOriginServerTs(),
sender = credentials.userId,
eventId = dummyEventId(roomId),
type = EventType.MESSAGE,
content = toContent(content)
)
}

private fun dummyOriginServerTs(): Long {
return System.currentTimeMillis()
}

private fun dummyEventId(roomId: String): String {
return roomId + "-" + dummyOriginServerTs()
}

@Suppress("UNCHECKED_CAST")
private inline fun <reified T> toContent(data: T?): Content? {
val moshiAdapter = moshi.adapter(T::class.java)
val jsonValue = moshiAdapter.toJsonValue(data)
return jsonValue as? Content?
}


}

View File

@ -130,7 +130,7 @@ internal class LocalEchoEventFactory(private val credentials: Credentials, priva
return Event( return Event(
roomId = roomId, roomId = roomId,
originServerTs = dummyOriginServerTs(), originServerTs = dummyOriginServerTs(),
sender = credentials.userId, senderId = credentials.userId,
eventId = localId, eventId = localId,
type = EventType.REACTION, type = EventType.REACTION,
content = content.toContent(), content = content.toContent(),
@ -221,7 +221,7 @@ internal class LocalEchoEventFactory(private val credentials: Credentials, priva
return Event( return Event(
roomId = roomId, roomId = roomId,
originServerTs = dummyOriginServerTs(), originServerTs = dummyOriginServerTs(),
sender = credentials.userId, senderId = credentials.userId,
eventId = localID, eventId = localID,
type = EventType.MESSAGE, type = EventType.MESSAGE,
content = content.toContent(), content = content.toContent(),
@ -241,7 +241,7 @@ internal class LocalEchoEventFactory(private val credentials: Credentials, priva
//Fallbacks and event representation //Fallbacks and event representation
//TODO Add error/warning logs when any of this is null //TODO Add error/warning logs when any of this is null
val permalink = PermalinkFactory.createPermalink(eventReplied) ?: return null val permalink = PermalinkFactory.createPermalink(eventReplied) ?: return null
val userId = eventReplied.sender ?: return null val userId = eventReplied.senderId ?: return null
val userLink = PermalinkFactory.createPermalink(userId) ?: return null val userLink = PermalinkFactory.createPermalink(userId) ?: return null
// <mx-reply> // <mx-reply>
// <blockquote> // <blockquote>
@ -330,7 +330,7 @@ internal class LocalEchoEventFactory(private val credentials: Credentials, priva
return Event( return Event(
roomId = roomId, roomId = roomId,
originServerTs = dummyOriginServerTs(), originServerTs = dummyOriginServerTs(),
sender = credentials.userId, senderId = credentials.userId,
eventId = localID, eventId = localID,
type = EventType.REDACTION, type = EventType.REDACTION,
redacts = eventId, redacts = eventId,

View File

@ -26,6 +26,7 @@ import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.session.room.EventRelationExtractor import im.vector.matrix.android.internal.session.room.EventRelationExtractor
import im.vector.matrix.android.internal.session.room.membership.RoomMembers
import im.vector.matrix.android.internal.session.room.membership.SenderRoomMemberExtractor import im.vector.matrix.android.internal.session.room.membership.SenderRoomMemberExtractor
import io.realm.Realm import io.realm.Realm
import timber.log.Timber import timber.log.Timber
@ -49,7 +50,11 @@ internal class TimelineEventFactory(
val cacheKey = sender + eventEntity.localId val cacheKey = sender + eventEntity.localId
val senderData = senderCache.getOrPut(cacheKey) { val senderData = senderCache.getOrPut(cacheKey) {
val senderRoomMember = roomMemberExtractor.extractFrom(eventEntity, realm) val senderRoomMember = roomMemberExtractor.extractFrom(eventEntity, realm)
SenderData(senderRoomMember?.displayName, senderRoomMember?.avatarUrl) val isUniqueDisplayName = RoomMembers(realm, eventEntity.roomId).isUniqueDisplayName(senderRoomMember?.displayName)

SenderData(senderRoomMember?.displayName,
isUniqueDisplayName,
senderRoomMember?.avatarUrl)
} }
val event = eventEntity.asDomain() val event = eventEntity.asDomain()
if (event.getClearType() == EventType.ENCRYPTED) { if (event.getClearType() == EventType.ENCRYPTED) {
@ -62,6 +67,7 @@ internal class TimelineEventFactory(
eventEntity.localId, eventEntity.localId,
eventEntity.displayIndex, eventEntity.displayIndex,
senderData.senderName, senderData.senderName,
senderData.isUniqueDisplayName,
senderData.senderAvatar, senderData.senderAvatar,
eventEntity.sendState, eventEntity.sendState,
relations relations
@ -96,7 +102,7 @@ internal class TimelineEventFactory(


private data class SenderData( private data class SenderData(
val senderName: String?, val senderName: String?,
val isUniqueDisplayName: Boolean,
val senderAvatar: String? val senderAvatar: String?
) )

} }

View File

@ -257,7 +257,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {


try { try {
return Event(eventId = data["event_id"], return Event(eventId = data["event_id"],
sender = data["sender"], senderId = data["sender"],
roomId = data["room_id"], roomId = data["room_id"],
type = data.getValue("type"), type = data.getValue("type"),
// TODO content = data.getValue("content"), // TODO content = data.getValue("content"),

View File

@ -222,8 +222,8 @@ class RoomDetailFragment :
} }
//switch to expanded bar //switch to expanded bar
composerLayout.composerRelatedMessageTitle.apply { composerLayout.composerRelatedMessageTitle.apply {
text = event.senderName text = event.getDisambiguatedDisplayName()
setTextColor(ContextCompat.getColor(requireContext(), getColorFromUserId(event.root.sender))) setTextColor(ContextCompat.getColor(requireContext(), getColorFromUserId(event.root.senderId)))
} }


//TODO this is used at several places, find way to refactor? //TODO this is used at several places, find way to refactor?
@ -255,7 +255,7 @@ class RoomDetailFragment :
composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_reply)) composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_reply))
} }


AvatarRenderer.render(event.senderAvatar, event.root.sender AvatarRenderer.render(event.senderAvatar, event.root.senderId
?: "", event.senderName, composerLayout.composerRelatedMessageAvatar) ?: "", event.senderName, composerLayout.composerRelatedMessageAvatar)


composerLayout.composerEditText.setSelection(composerLayout.composerEditText.text.length) composerLayout.composerEditText.setSelection(composerLayout.composerEditText.text.length)

View File

@ -282,7 +282,7 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
val dateFormat = SimpleDateFormat("EEE, d MMM yyyy HH:mm", Locale.getDefault()) val dateFormat = SimpleDateFormat("EEE, d MMM yyyy HH:mm", Locale.getDefault())
_nonBlockingPopAlert.postValue(LiveEvent( _nonBlockingPopAlert.postValue(LiveEvent(
Pair(R.string.last_edited_info_message, listOf( Pair(R.string.last_edited_info_message, listOf(
lastReplace.senderName ?: "?", lastReplace.getDisambiguatedDisplayName(),
dateFormat.format(Date(lastReplace.root.originServerTs ?: 0))) dateFormat.format(Date(lastReplace.root.originServerTs ?: 0)))
)) ))
) )
@ -429,7 +429,7 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
private fun observeInvitationState() { private fun observeInvitationState() {
asyncSubscribe(RoomDetailViewState::asyncRoomSummary) { summary -> asyncSubscribe(RoomDetailViewState::asyncRoomSummary) { summary ->
if (summary.membership == Membership.INVITE) { if (summary.membership == Membership.INVITE) {
summary.lastMessage?.sender?.let { senderId -> summary.lastMessage?.senderId?.let { senderId ->
session.getUser(senderId) session.getUser(senderId)
}?.also { }?.also {
setState { copy(asyncInviter = Success(it)) } setState { copy(asyncInviter = Success(it)) }

View File

@ -238,7 +238,7 @@ class TimelineEventController(private val dateFormatter: TimelineDateFormatter,
val senderAvatar = mergedEvent.senderAvatar() val senderAvatar = mergedEvent.senderAvatar()
val senderName = mergedEvent.senderName() val senderName = mergedEvent.senderName()
MergedHeaderItem.Data( MergedHeaderItem.Data(
userId = mergedEvent.root.sender ?: "", userId = mergedEvent.root.senderId ?: "",
avatarUrl = senderAvatar, avatarUrl = senderAvatar,
memberName = senderName ?: "", memberName = senderName ?: "",
eventId = mergedEvent.localId eventId = mergedEvent.localId

View File

@ -90,7 +90,7 @@ class MessageActionsViewModel(initialState: MessageActionState) : VectorViewMode
} }
} }
MessageActionState( MessageActionState(
userId = event.root.sender ?: "", userId = event.root.senderId ?: "",
senderName = parcel.informationData.memberName?.toString() ?: "", senderName = parcel.informationData.memberName?.toString() ?: "",
messageBody = body, messageBody = body,
ts = dateFormat.format(Date(originTs ?: 0)), ts = dateFormat.format(Date(originTs ?: 0)),

View File

@ -131,7 +131,7 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes
} }
this.add(SimpleAction(ACTION_COPY_PERMALINK, R.string.permalink, R.drawable.ic_permalink, parcel.eventId)) this.add(SimpleAction(ACTION_COPY_PERMALINK, R.string.permalink, R.drawable.ic_permalink, parcel.eventId))


if (currentSession.sessionParams.credentials.userId != event.root.sender && event.root.getClearType() == EventType.MESSAGE) { if (currentSession.sessionParams.credentials.userId != event.root.senderId && event.root.getClearType() == EventType.MESSAGE) {
//not sent by me //not sent by me
this.add(SimpleAction(ACTION_FLAG, R.string.report_content, R.drawable.ic_flag, parcel.eventId)) this.add(SimpleAction(ACTION_FLAG, R.string.report_content, R.drawable.ic_flag, parcel.eventId))
} }
@ -179,7 +179,7 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment //Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
if (event.root.getClearType() != EventType.MESSAGE) return false if (event.root.getClearType() != EventType.MESSAGE) return false
//TODO if user is admin or moderator //TODO if user is admin or moderator
return event.root.sender == myUserId return event.root.senderId == myUserId
} }


private fun canViewReactions(event: TimelineEvent): Boolean { private fun canViewReactions(event: TimelineEvent): Boolean {
@ -194,7 +194,7 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes
if (event.root.getClearType() != EventType.MESSAGE) return false if (event.root.getClearType() != EventType.MESSAGE) return false
//TODO if user is admin or moderator //TODO if user is admin or moderator
val messageContent = event.root.content.toModel<MessageContent>() val messageContent = event.root.content.toModel<MessageContent>()
return event.root.sender == myUserId && ( return event.root.senderId == myUserId && (
messageContent?.type == MessageType.MSGTYPE_TEXT messageContent?.type == MessageType.MSGTYPE_TEXT
|| messageContent?.type == MessageType.MSGTYPE_EMOTE || messageContent?.type == MessageType.MSGTYPE_EMOTE
) )

View File

@ -49,8 +49,8 @@ class ViewReactionViewModel(private val session: Session,


sum.sourceEvents.mapNotNull { room.getTimeLineEvent(it) }.forEach { sum.sourceEvents.mapNotNull { room.getTimeLineEvent(it) }.forEach {
val localDate = it.root.localDateTime() val localDate = it.root.localDateTime()
results.add(ReactionInfo(it.root.eventId!!, sum.key, it.root.sender results.add(ReactionInfo(it.root.eventId!!, sum.key, it.root.senderId
?: "", it.senderName, timelineDateFormatter.formatMessageHour(localDate))) ?: "", it.getDisambiguatedDisplayName(), timelineDateFormatter.formatMessageHour(localDate)))
} }
} }
setState { setState {

View File

@ -37,7 +37,7 @@ class EncryptionItemFactory(private val stringProvider: StringProvider) {
val text = buildNoticeText(event.root, event.senderName) ?: return null val text = buildNoticeText(event.root, event.senderName) ?: return null
val informationData = MessageInformationData( val informationData = MessageInformationData(
eventId = event.root.eventId ?: "?", eventId = event.root.eventId ?: "?",
senderId = event.root.sender ?: "", senderId = event.root.senderId ?: "",
sendState = event.sendState, sendState = event.sendState,
avatarUrl = event.senderAvatar(), avatarUrl = event.senderAvatar(),
memberName = event.senderName(), memberName = event.senderName(),

View File

@ -32,7 +32,7 @@ class NoticeItemFactory(private val eventFormatter: NoticeEventFormatter) {
val formattedText = eventFormatter.format(event) ?: return null val formattedText = eventFormatter.format(event) ?: return null
val informationData = MessageInformationData( val informationData = MessageInformationData(
eventId = event.root.eventId ?: "?", eventId = event.root.eventId ?: "?",
senderId = event.root.sender ?: "", senderId = event.root.senderId ?: "",
sendState = event.sendState, sendState = event.sendState,
avatarUrl = event.senderAvatar(), avatarUrl = event.senderAvatar(),
memberName = event.senderName(), memberName = event.senderName(),

View File

@ -65,7 +65,7 @@ class TimelineItemFactory(private val messageItemFactory: MessageItemFactory,
if (TimelineDisplayableEvents.DEBUG_HIDDEN_EVENT) { if (TimelineDisplayableEvents.DEBUG_HIDDEN_EVENT) {
val informationData = MessageInformationData(eventId = event.root.eventId val informationData = MessageInformationData(eventId = event.root.eventId
?: "?", ?: "?",
senderId = event.root.sender ?: "", senderId = event.root.senderId ?: "",
sendState = event.sendState, sendState = event.sendState,
time = "", time = "",
avatarUrl = null, avatarUrl = null,

View File

@ -32,13 +32,13 @@ class NoticeEventFormatter(private val stringProvider: StringProvider) {


fun format(timelineEvent: TimelineEvent): CharSequence? { fun format(timelineEvent: TimelineEvent): CharSequence? {
return when (val type = timelineEvent.root.getClearType()) { return when (val type = timelineEvent.root.getClearType()) {
EventType.STATE_ROOM_NAME -> formatRoomNameEvent(timelineEvent.root, timelineEvent.senderName) EventType.STATE_ROOM_NAME -> formatRoomNameEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName())
EventType.STATE_ROOM_TOPIC -> formatRoomTopicEvent(timelineEvent.root, timelineEvent.senderName) EventType.STATE_ROOM_TOPIC -> formatRoomTopicEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName())
EventType.STATE_ROOM_MEMBER -> formatRoomMemberEvent(timelineEvent.root, timelineEvent.senderName()) EventType.STATE_ROOM_MEMBER -> formatRoomMemberEvent(timelineEvent.root, timelineEvent.senderName())
EventType.STATE_HISTORY_VISIBILITY -> formatRoomHistoryVisibilityEvent(timelineEvent.root, timelineEvent.senderName) EventType.STATE_HISTORY_VISIBILITY -> formatRoomHistoryVisibilityEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName())
EventType.CALL_INVITE, EventType.CALL_INVITE,
EventType.CALL_HANGUP, EventType.CALL_HANGUP,
EventType.CALL_ANSWER -> formatCallEvent(timelineEvent.root, timelineEvent.senderName) EventType.CALL_ANSWER -> formatCallEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName())
else -> { else -> {
Timber.v("Type $type not handled by this formatter") Timber.v("Type $type not handled by this formatter")
null null
@ -111,12 +111,12 @@ class NoticeEventFormatter(private val stringProvider: StringProvider) {
if (!TextUtils.equals(eventContent?.displayName, prevEventContent?.displayName)) { if (!TextUtils.equals(eventContent?.displayName, prevEventContent?.displayName)) {
val displayNameText = when { val displayNameText = when {
prevEventContent?.displayName.isNullOrEmpty() -> prevEventContent?.displayName.isNullOrEmpty() ->
stringProvider.getString(R.string.notice_display_name_set, event.sender, eventContent?.displayName) stringProvider.getString(R.string.notice_display_name_set, event.senderId, eventContent?.displayName)
eventContent?.displayName.isNullOrEmpty() -> eventContent?.displayName.isNullOrEmpty() ->
stringProvider.getString(R.string.notice_display_name_removed, event.sender, prevEventContent?.displayName) stringProvider.getString(R.string.notice_display_name_removed, event.senderId, prevEventContent?.displayName)
else -> else ->
stringProvider.getString(R.string.notice_display_name_changed_from, stringProvider.getString(R.string.notice_display_name_changed_from,
event.sender, prevEventContent?.displayName, eventContent?.displayName) event.senderId, prevEventContent?.displayName, eventContent?.displayName)
} }
displayText.append(displayNameText) displayText.append(displayNameText)
} }
@ -134,8 +134,8 @@ class NoticeEventFormatter(private val stringProvider: StringProvider) {
} }


private fun buildMembershipNotice(event: Event, senderName: String?, eventContent: RoomMember?, prevEventContent: RoomMember?): String? { private fun buildMembershipNotice(event: Event, senderName: String?, eventContent: RoomMember?, prevEventContent: RoomMember?): String? {
val senderDisplayName = senderName ?: event.sender val senderDisplayName = senderName ?: event.senderId
val targetDisplayName = eventContent?.displayName ?: event.sender val targetDisplayName = eventContent?.displayName ?: event.senderId
return when { return when {
Membership.INVITE == eventContent?.membership -> { Membership.INVITE == eventContent?.membership -> {
// TODO get userId // TODO get userId
@ -156,7 +156,7 @@ class NoticeEventFormatter(private val stringProvider: StringProvider) {
stringProvider.getString(R.string.notice_room_join, senderDisplayName) stringProvider.getString(R.string.notice_room_join, senderDisplayName)
Membership.LEAVE == eventContent?.membership -> Membership.LEAVE == eventContent?.membership ->
// 2 cases here: this member may have left voluntarily or they may have been "left" by someone else ie. kicked // 2 cases here: this member may have left voluntarily or they may have been "left" by someone else ie. kicked
return if (TextUtils.equals(event.sender, event.stateKey)) { return if (TextUtils.equals(event.senderId, event.stateKey)) {
if (prevEventContent?.membership == Membership.INVITE) { if (prevEventContent?.membership == Membership.INVITE) {
stringProvider.getString(R.string.notice_room_reject, senderDisplayName) stringProvider.getString(R.string.notice_room_reject, senderDisplayName)
} else { } else {

View File

@ -85,12 +85,11 @@ fun TimelineEvent.senderAvatar(): String? {


fun TimelineEvent.senderName(): String? { fun TimelineEvent.senderName(): String? {
// We might have no senderName when user leave, so we try to get it from prevContent // We might have no senderName when user leave, so we try to get it from prevContent
return senderName return when {
?: if (root.type == EventType.STATE_ROOM_MEMBER) { senderName != null -> getDisambiguatedDisplayName()
root.prevContent.toModel<RoomMember>()?.displayName root.type == EventType.STATE_ROOM_MEMBER -> root.prevContent.toModel<RoomMember>()?.displayName
} else { else -> null
null }
}
} }


fun TimelineEvent.canBeMerged(): Boolean { fun TimelineEvent.canBeMerged(): Boolean {

View File

@ -83,7 +83,6 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : BaseEventItem<H>() {
override fun bind(holder: H) { override fun bind(holder: H) {
super.bind(holder) super.bind(holder)
if (informationData.showInformation) { if (informationData.showInformation) {

holder.avatarImageView.layoutParams = holder.avatarImageView.layoutParams?.apply { holder.avatarImageView.layoutParams = holder.avatarImageView.layoutParams?.apply {
val size = dpToPx(avatarStyle.avatarSizeDP, holder.view.context) val size = dpToPx(avatarStyle.avatarSizeDP, holder.view.context)
height = size height = size

View File

@ -22,7 +22,6 @@ import im.vector.riotredesign.core.extensions.localDateTime
import im.vector.riotredesign.core.resources.ColorProvider import im.vector.riotredesign.core.resources.ColorProvider
import im.vector.riotredesign.features.home.getColorFromUserId import im.vector.riotredesign.features.home.getColorFromUserId
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderAvatar
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
import im.vector.riotredesign.features.home.room.detail.timeline.item.ReactionInfoData import im.vector.riotredesign.features.home.room.detail.timeline.item.ReactionInfoData
import me.gujun.android.span.span import me.gujun.android.span.span
@ -38,23 +37,23 @@ class MessageInformationDataFactory(private val timelineDateFormatter: TimelineD
val eventId = event.root.eventId!! val eventId = event.root.eventId!!


val date = event.root.localDateTime() val date = event.root.localDateTime()

val nextDate = nextEvent?.root?.localDateTime() val nextDate = nextEvent?.root?.localDateTime()
val addDaySeparator = date.toLocalDate() != nextDate?.toLocalDate() val addDaySeparator = date.toLocalDate() != nextDate?.toLocalDate()
val isNextMessageReceivedMoreThanOneHourAgo = nextDate?.isBefore(date.minusMinutes(60)) val isNextMessageReceivedMoreThanOneHourAgo = nextDate?.isBefore(date.minusMinutes(60))
?: false ?: false

val showInformation = val showInformation =
addDaySeparator addDaySeparator
|| event.senderAvatar != nextEvent?.senderAvatar || event.senderAvatar != nextEvent?.senderAvatar
|| event.senderName != nextEvent?.senderName || event.getDisambiguatedDisplayName() != nextEvent?.getDisambiguatedDisplayName()
|| (nextEvent?.root?.getClearType() != EventType.MESSAGE && nextEvent?.root?.getClearType() != EventType.ENCRYPTED) || (nextEvent?.root?.getClearType() != EventType.MESSAGE && nextEvent?.root?.getClearType() != EventType.ENCRYPTED)
|| isNextMessageReceivedMoreThanOneHourAgo || isNextMessageReceivedMoreThanOneHourAgo


val time = timelineDateFormatter.formatMessageHour(date) val time = timelineDateFormatter.formatMessageHour(date)
val avatarUrl = event.senderAvatar() val avatarUrl = event.senderAvatar
val memberName = event.senderName ?: event.root.sender ?: "" val memberName = event.getDisambiguatedDisplayName()
val formattedMemberName = span(memberName) { val formattedMemberName = span(memberName) {
textColor = colorProvider.getColor(getColorFromUserId(event.root.sender textColor = colorProvider.getColor(getColorFromUserId(event.root.senderId
?: "")) ?: ""))
} }


@ -62,7 +61,7 @@ class MessageInformationDataFactory(private val timelineDateFormatter: TimelineD


return MessageInformationData( return MessageInformationData(
eventId = eventId, eventId = eventId,
senderId = event.root.sender ?: "", senderId = event.root.senderId ?: "",
sendState = event.sendState, sendState = event.sendState,
time = time, time = time,
avatarUrl = avatarUrl, avatarUrl = avatarUrl,