Extract to sub classes

This commit is contained in:
Benoit Marty
2023-05-16 17:18:19 +02:00
committed by Benoit Marty
parent bbe4b7d26a
commit be478cadaa
8 changed files with 490 additions and 198 deletions

View File

@@ -37,6 +37,7 @@ dependencies {
anvil(projects.anvilcodegen)
implementation(projects.anvilannotations)
implementation(projects.libraries.core)
implementation(projects.libraries.architecture)
implementation(projects.libraries.matrix.api)
implementation(projects.libraries.uiStrings)

View File

@@ -24,6 +24,7 @@ import androidx.compose.ui.text.withStyle
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
import io.element.android.libraries.eventformatter.impl.mode.RenderingMode
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType
@@ -32,11 +33,9 @@ import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParse
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
import io.element.android.libraries.matrix.api.timeline.item.event.MessageType
import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.OtherState
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
@@ -49,7 +48,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.UnknownConten
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
import io.element.android.services.toolbox.api.strings.StringProvider
import timber.log.Timber
import javax.inject.Inject
import io.element.android.libraries.ui.strings.R as StringR
@@ -57,6 +55,9 @@ import io.element.android.libraries.ui.strings.R as StringR
class DefaultRoomLastMessageFormatter @Inject constructor(
private val sp: StringProvider,
private val matrixClient: MatrixClient,
private val roomMembershipContentFormatter: RoomMembershipContentFormatter,
private val profileChangeContentFormatter: ProfileChangeContentFormatter,
private val stateContentFormatter: StateContentFormatter,
) : RoomLastMessageFormatter {
override fun format(event: EventTimelineItem, isDmRoom: Boolean): CharSequence? {
@@ -84,13 +85,13 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
}
}
is RoomMembershipContent -> {
processRoomMembershipChange(content, senderDisplayName, isOutgoing)
roomMembershipContentFormatter.format(content, senderDisplayName, isOutgoing)
}
is ProfileChangeContent -> {
processProfileChangeContent(content, senderDisplayName, isOutgoing)
profileChangeContentFormatter.format(content, senderDisplayName, isOutgoing)
}
is StateContent -> {
processRoomStateChange(content, senderDisplayName, isOutgoing)
stateContentFormatter.format(content, senderDisplayName, isOutgoing, RenderingMode.RoomList)
}
is FailedToParseMessageLikeContent, is FailedToParseStateContent, is UnknownContent -> {
prefixIfNeeded(sp.getString(StringR.string.common_unsupported_event), senderDisplayName, isDmRoom)
@@ -131,186 +132,6 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
return prefixIfNeeded(internalMessage, senderDisplayName, isDmRoom)
}
private fun processRoomMembershipChange(membershipContent: RoomMembershipContent, senderDisplayName: String, senderIsYou: Boolean): CharSequence? {
val userId = membershipContent.userId
val memberIsYou = userId == matrixClient.sessionId
return when (val change = membershipContent.change) {
MembershipChange.JOINED -> if (memberIsYou) {
sp.getString(R.string.state_event_room_join_by_you)
} else {
sp.getString(R.string.state_event_room_join, userId.value)
}
MembershipChange.LEFT -> if (memberIsYou) {
sp.getString(R.string.state_event_room_leave_by_you)
} else {
sp.getString(R.string.state_event_room_leave, userId.value)
}
MembershipChange.BANNED, MembershipChange.KICKED_AND_BANNED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_ban_by_you, userId.value)
} else {
sp.getString(R.string.state_event_room_ban, senderDisplayName, userId.value)
}
MembershipChange.UNBANNED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_unban_by_you, userId.value)
} else {
sp.getString(R.string.state_event_room_unban, senderDisplayName, userId.value)
}
MembershipChange.KICKED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_remove_by_you, userId.value)
} else {
sp.getString(R.string.state_event_room_remove, senderDisplayName, userId.value)
}
MembershipChange.INVITED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_invite_by_you, userId.value)
} else if (memberIsYou) {
sp.getString(R.string.state_event_room_invite_you, senderDisplayName)
} else {
sp.getString(R.string.state_event_room_invite, senderDisplayName, userId.value)
}
MembershipChange.INVITATION_ACCEPTED -> if (memberIsYou) {
sp.getString(R.string.state_event_room_invite_accepted_by_you)
} else {
sp.getString(R.string.state_event_room_invite_accepted, userId.value)
}
MembershipChange.INVITATION_REJECTED -> if (memberIsYou) {
sp.getString(R.string.state_event_room_reject_by_you)
} else {
sp.getString(R.string.state_event_room_reject, userId.value)
}
MembershipChange.INVITATION_REVOKED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_third_party_revoked_invite_by_you, userId.value)
} else {
sp.getString(R.string.state_event_room_third_party_revoked_invite, senderDisplayName, userId.value)
}
MembershipChange.KNOCKED -> if (memberIsYou) {
sp.getString(R.string.state_event_room_knock_by_you)
} else {
sp.getString(R.string.state_event_room_knock, userId.value)
}
MembershipChange.KNOCK_ACCEPTED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_knock_accepted_by_you, userId.value)
} else {
sp.getString(R.string.state_event_room_knock_accepted, senderDisplayName, userId.value)
}
MembershipChange.KNOCK_RETRACTED -> if (memberIsYou) {
sp.getString(R.string.state_event_room_knock_retracted_by_you)
} else {
sp.getString(R.string.state_event_room_knock_retracted, userId.value)
}
MembershipChange.KNOCK_DENIED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_knock_denied_by_you, userId.value)
} else if (memberIsYou) {
sp.getString(R.string.state_event_room_knock_denied_you, senderDisplayName)
} else {
sp.getString(R.string.state_event_room_knock_denied, senderDisplayName, userId.value)
}
else -> {
Timber.v("Filtering timeline item for room membership: $membershipContent")
null
}
}
}
private fun processRoomStateChange(stateContent: StateContent, senderDisplayName: String, senderIsYou: Boolean): CharSequence? {
return when (val content = stateContent.content) {
is OtherState.RoomAvatar -> {
val hasAvatarUrl = content.url != null
when {
senderIsYou && hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_changed_by_you)
senderIsYou && !hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_removed_by_you)
!senderIsYou && hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_changed, senderDisplayName)
else -> sp.getString(R.string.state_event_room_avatar_removed, senderDisplayName)
}
}
is OtherState.RoomCreate -> {
if (senderIsYou) {
sp.getString(R.string.state_event_room_created_by_you)
} else {
sp.getString(R.string.state_event_room_created, senderDisplayName)
}
}
is OtherState.RoomEncryption -> sp.getString(StringR.string.common_encryption_enabled)
is OtherState.RoomName -> {
val hasRoomName = content.name != null
when {
senderIsYou && hasRoomName -> sp.getString(R.string.state_event_room_name_changed_by_you, content.name)
senderIsYou && !hasRoomName -> sp.getString(R.string.state_event_room_name_removed_by_you)
!senderIsYou && hasRoomName -> sp.getString(R.string.state_event_room_name_changed, senderDisplayName, content.name)
else -> sp.getString(R.string.state_event_room_name_removed, senderDisplayName)
}
}
is OtherState.RoomThirdPartyInvite -> {
if (content.displayName == null) {
Timber.e("RoomThirdPartyInvite undisplayable due to missing name")
return null
}
if (senderIsYou) {
sp.getString(R.string.state_event_room_third_party_invite_by_you, content.displayName)
} else {
sp.getString(R.string.state_event_room_third_party_invite, senderDisplayName, content.displayName)
}
}
is OtherState.RoomTopic -> {
val hasRoomTopic = content.topic != null
when {
senderIsYou && hasRoomTopic -> sp.getString(R.string.state_event_room_topic_changed_by_you, content.topic)
senderIsYou && !hasRoomTopic -> sp.getString(R.string.state_event_room_topic_removed_by_you)
!senderIsYou && hasRoomTopic -> sp.getString(R.string.state_event_room_topic_changed, senderDisplayName, content.topic)
else -> sp.getString(R.string.state_event_room_topic_removed, senderDisplayName)
}
}
else -> {
Timber.v("Filtering timeline item for room state change: $content")
null
}
}
}
private fun processProfileChangeContent(
profileChangeContent: ProfileChangeContent,
senderDisplayName: String,
senderIsYou: Boolean
): String? = profileChangeContent.run {
val displayNameChanged = displayName != prevDisplayName
val avatarChanged = avatarUrl != prevAvatarUrl
return when {
avatarChanged && displayNameChanged -> {
val message = processProfileChangeContent(profileChangeContent.copy(avatarUrl = null, prevAvatarUrl = null), senderDisplayName, senderIsYou)
val avatarChangedToo = sp.getString(R.string.state_event_avatar_changed_too)
"$message\n$avatarChangedToo"
}
displayNameChanged -> {
if (displayName != null && prevDisplayName != null) {
if (senderIsYou) {
sp.getString(R.string.state_event_display_name_changed_from_by_you, prevDisplayName, displayName)
} else {
sp.getString(R.string.state_event_display_name_changed_from, senderDisplayName, prevDisplayName, displayName)
}
} else if (displayName != null) {
if (senderIsYou) {
sp.getString(R.string.state_event_display_name_set_by_you, displayName)
} else {
sp.getString(R.string.state_event_display_name_set, senderDisplayName, displayName)
}
} else {
if (senderIsYou) {
sp.getString(R.string.state_event_display_name_removed_by_you, prevDisplayName)
} else {
sp.getString(R.string.state_event_display_name_removed, senderDisplayName, prevDisplayName)
}
}
}
avatarChanged -> {
if (senderIsYou) {
sp.getString(R.string.state_event_avatar_url_changed_by_you)
} else {
sp.getString(R.string.state_event_avatar_url_changed, senderDisplayName)
}
}
else -> null
}
}
private fun prefixIfNeeded(message: String, senderDisplayName: String, isDmRoom: Boolean): CharSequence = if (isDmRoom) {
message
} else {

View File

@@ -17,26 +17,62 @@
package io.element.android.libraries.eventformatter.impl
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
import io.element.android.libraries.eventformatter.api.TimelineEventFormatter
import io.element.android.libraries.eventformatter.impl.mode.RenderingMode
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent
import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
import io.element.android.libraries.ui.strings.R
import io.element.android.services.toolbox.api.strings.StringProvider
import javax.inject.Inject
/**
* For now use the same formatter than for the room list using [RoomLastMessageFormatter].
* We will change this if we want to have a different rendering in the timeline.
*/
@ContributesBinding(SessionScope::class)
class DefaultTimelineEventFormatter @Inject constructor(
private val roomLastMessageFormatter: RoomLastMessageFormatter,
private val sp: StringProvider,
private val matrixClient: MatrixClient,
private val buildMeta: BuildMeta,
private val roomMembershipContentFormatter: RoomMembershipContentFormatter,
private val profileChangeContentFormatter: ProfileChangeContentFormatter,
private val stateContentFormatter: StateContentFormatter,
) : TimelineEventFormatter {
override fun format(event: EventTimelineItem): CharSequence? {
return roomLastMessageFormatter.format(
event,
/* We do not want to distinguish DM and room here */
isDmRoom = false,
)
val isOutgoing = event.sender == matrixClient.sessionId
val senderDisplayName = (event.senderProfile as? ProfileTimelineDetails.Ready)?.displayName ?: event.sender.value
return when (val content = event.content) {
is RoomMembershipContent -> {
roomMembershipContentFormatter.format(content, senderDisplayName, isOutgoing)
}
is ProfileChangeContent -> {
profileChangeContentFormatter.format(content, senderDisplayName, isOutgoing)
}
is StateContent -> {
stateContentFormatter.format(content, senderDisplayName, isOutgoing, RenderingMode.Timeline)
}
RedactedContent,
is StickerContent,
is UnableToDecryptContent,
is MessageContent,
is FailedToParseMessageLikeContent,
is FailedToParseStateContent,
is UnknownContent -> {
if (buildMeta.isDebuggable) {
error("You should not use this formatter for this event: $event")
}
sp.getString(R.string.common_unsupported_event)
}
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2023 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 io.element.android.libraries.eventformatter.impl
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
import io.element.android.services.toolbox.api.strings.StringProvider
import javax.inject.Inject
class ProfileChangeContentFormatter @Inject constructor(
private val sp: StringProvider,
) {
fun format(
profileChangeContent: ProfileChangeContent,
senderDisplayName: String,
senderIsYou: Boolean,
): String? = profileChangeContent.run {
val displayNameChanged = displayName != prevDisplayName
val avatarChanged = avatarUrl != prevAvatarUrl
return when {
avatarChanged && displayNameChanged -> {
val message = format(profileChangeContent.copy(avatarUrl = null, prevAvatarUrl = null), senderDisplayName, senderIsYou)
val avatarChangedToo = sp.getString(R.string.state_event_avatar_changed_too)
"$message\n$avatarChangedToo"
}
displayNameChanged -> {
if (displayName != null && prevDisplayName != null) {
if (senderIsYou) {
sp.getString(R.string.state_event_display_name_changed_from_by_you, prevDisplayName, displayName)
} else {
sp.getString(R.string.state_event_display_name_changed_from, senderDisplayName, prevDisplayName, displayName)
}
} else if (displayName != null) {
if (senderIsYou) {
sp.getString(R.string.state_event_display_name_set_by_you, displayName)
} else {
sp.getString(R.string.state_event_display_name_set, senderDisplayName, displayName)
}
} else {
if (senderIsYou) {
sp.getString(R.string.state_event_display_name_removed_by_you, prevDisplayName)
} else {
sp.getString(R.string.state_event_display_name_removed, senderDisplayName, prevDisplayName)
}
}
}
avatarChanged -> {
if (senderIsYou) {
sp.getString(R.string.state_event_avatar_url_changed_by_you)
} else {
sp.getString(R.string.state_event_avatar_url_changed, senderDisplayName)
}
}
else -> null
}
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 2023 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 io.element.android.libraries.eventformatter.impl
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
import io.element.android.services.toolbox.api.strings.StringProvider
import timber.log.Timber
import javax.inject.Inject
class RoomMembershipContentFormatter @Inject constructor(
private val matrixClient: MatrixClient,
private val sp: StringProvider,
) {
fun format(
membershipContent: RoomMembershipContent,
senderDisplayName: String,
senderIsYou: Boolean,
): CharSequence? {
val userId = membershipContent.userId
val memberIsYou = userId == matrixClient.sessionId
return when (val change = membershipContent.change) {
MembershipChange.JOINED -> if (memberIsYou) {
sp.getString(R.string.state_event_room_join_by_you)
} else {
sp.getString(R.string.state_event_room_join, userId.value)
}
MembershipChange.LEFT -> if (memberIsYou) {
sp.getString(R.string.state_event_room_leave_by_you)
} else {
sp.getString(R.string.state_event_room_leave, userId.value)
}
MembershipChange.BANNED, MembershipChange.KICKED_AND_BANNED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_ban_by_you, userId.value)
} else {
sp.getString(R.string.state_event_room_ban, senderDisplayName, userId.value)
}
MembershipChange.UNBANNED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_unban_by_you, userId.value)
} else {
sp.getString(R.string.state_event_room_unban, senderDisplayName, userId.value)
}
MembershipChange.KICKED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_remove_by_you, userId.value)
} else {
sp.getString(R.string.state_event_room_remove, senderDisplayName, userId.value)
}
MembershipChange.INVITED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_invite_by_you, userId.value)
} else if (memberIsYou) {
sp.getString(R.string.state_event_room_invite_you, senderDisplayName)
} else {
sp.getString(R.string.state_event_room_invite, senderDisplayName, userId.value)
}
MembershipChange.INVITATION_ACCEPTED -> if (memberIsYou) {
sp.getString(R.string.state_event_room_invite_accepted_by_you)
} else {
sp.getString(R.string.state_event_room_invite_accepted, userId.value)
}
MembershipChange.INVITATION_REJECTED -> if (memberIsYou) {
sp.getString(R.string.state_event_room_reject_by_you)
} else {
sp.getString(R.string.state_event_room_reject, userId.value)
}
MembershipChange.INVITATION_REVOKED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_third_party_revoked_invite_by_you, userId.value)
} else {
sp.getString(R.string.state_event_room_third_party_revoked_invite, senderDisplayName, userId.value)
}
MembershipChange.KNOCKED -> if (memberIsYou) {
sp.getString(R.string.state_event_room_knock_by_you)
} else {
sp.getString(R.string.state_event_room_knock, userId.value)
}
MembershipChange.KNOCK_ACCEPTED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_knock_accepted_by_you, userId.value)
} else {
sp.getString(R.string.state_event_room_knock_accepted, senderDisplayName, userId.value)
}
MembershipChange.KNOCK_RETRACTED -> if (memberIsYou) {
sp.getString(R.string.state_event_room_knock_retracted_by_you)
} else {
sp.getString(R.string.state_event_room_knock_retracted, userId.value)
}
MembershipChange.KNOCK_DENIED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_knock_denied_by_you, userId.value)
} else if (memberIsYou) {
sp.getString(R.string.state_event_room_knock_denied_you, senderDisplayName)
} else {
sp.getString(R.string.state_event_room_knock_denied, senderDisplayName, userId.value)
}
else -> {
Timber.v("Filtering timeline item for room membership: $membershipContent")
null
}
}
}
}

View File

@@ -0,0 +1,219 @@
/*
* Copyright (c) 2023 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 io.element.android.libraries.eventformatter.impl
import io.element.android.libraries.eventformatter.impl.mode.RenderingMode
import io.element.android.libraries.matrix.api.timeline.item.event.OtherState
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
import io.element.android.services.toolbox.api.strings.StringProvider
import timber.log.Timber
import javax.inject.Inject
class StateContentFormatter @Inject constructor(
private val sp: StringProvider,
) {
fun format(
stateContent: StateContent,
senderDisplayName: String,
senderIsYou: Boolean,
renderingMode: RenderingMode,
): CharSequence? {
return when (val content = stateContent.content) {
is OtherState.RoomAvatar -> {
val hasAvatarUrl = content.url != null
when {
senderIsYou && hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_changed_by_you)
senderIsYou && !hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_removed_by_you)
!senderIsYou && hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_changed, senderDisplayName)
else -> sp.getString(R.string.state_event_room_avatar_removed, senderDisplayName)
}
}
is OtherState.RoomCreate -> {
if (senderIsYou) {
sp.getString(R.string.state_event_room_created_by_you)
} else {
sp.getString(R.string.state_event_room_created, senderDisplayName)
}
}
is OtherState.RoomEncryption -> sp.getString(io.element.android.libraries.ui.strings.R.string.common_encryption_enabled)
is OtherState.RoomName -> {
val hasRoomName = content.name != null
when {
senderIsYou && hasRoomName -> sp.getString(R.string.state_event_room_name_changed_by_you, content.name)
senderIsYou && !hasRoomName -> sp.getString(R.string.state_event_room_name_removed_by_you)
!senderIsYou && hasRoomName -> sp.getString(R.string.state_event_room_name_changed, senderDisplayName, content.name)
else -> sp.getString(R.string.state_event_room_name_removed, senderDisplayName)
}
}
is OtherState.RoomThirdPartyInvite -> {
if (content.displayName == null) {
Timber.e("RoomThirdPartyInvite undisplayable due to missing name")
return null
}
if (senderIsYou) {
sp.getString(R.string.state_event_room_third_party_invite_by_you, content.displayName)
} else {
sp.getString(R.string.state_event_room_third_party_invite, senderDisplayName, content.displayName)
}
}
is OtherState.RoomTopic -> {
val hasRoomTopic = content.topic != null
when {
senderIsYou && hasRoomTopic -> sp.getString(R.string.state_event_room_topic_changed_by_you, content.topic)
senderIsYou && !hasRoomTopic -> sp.getString(R.string.state_event_room_topic_removed_by_you)
!senderIsYou && hasRoomTopic -> sp.getString(R.string.state_event_room_topic_changed, senderDisplayName, content.topic)
else -> sp.getString(R.string.state_event_room_topic_removed, senderDisplayName)
}
}
is OtherState.Custom -> when (renderingMode) {
RenderingMode.RoomList -> {
Timber.v("Filtering timeline item for room state change: $content")
null
}
RenderingMode.Timeline -> {
"Custom event ${content.eventType}"
}
}
OtherState.PolicyRuleRoom -> when (renderingMode) {
RenderingMode.RoomList -> {
Timber.v("Filtering timeline item for room state change: $content")
null
}
RenderingMode.Timeline -> {
"PolicyRuleRoom"
}
}
OtherState.PolicyRuleServer -> when (renderingMode) {
RenderingMode.RoomList -> {
Timber.v("Filtering timeline item for room state change: $content")
null
}
RenderingMode.Timeline -> {
"PolicyRuleServer"
}
}
OtherState.PolicyRuleUser -> when (renderingMode) {
RenderingMode.RoomList -> {
Timber.v("Filtering timeline item for room state change: $content")
null
}
RenderingMode.Timeline -> {
"PolicyRuleUser"
}
}
OtherState.RoomAliases -> when (renderingMode) {
RenderingMode.RoomList -> {
Timber.v("Filtering timeline item for room state change: $content")
null
}
RenderingMode.Timeline -> {
"RoomAliases"
}
}
OtherState.RoomCanonicalAlias -> when (renderingMode) {
RenderingMode.RoomList -> {
Timber.v("Filtering timeline item for room state change: $content")
null
}
RenderingMode.Timeline -> {
"RoomCanonicalAlias"
}
}
OtherState.RoomGuestAccess -> when (renderingMode) {
RenderingMode.RoomList -> {
Timber.v("Filtering timeline item for room state change: $content")
null
}
RenderingMode.Timeline -> {
"RoomGuestAccess"
}
}
OtherState.RoomHistoryVisibility -> when (renderingMode) {
RenderingMode.RoomList -> {
Timber.v("Filtering timeline item for room state change: $content")
null
}
RenderingMode.Timeline -> {
"RoomHistoryVisibility"
}
}
OtherState.RoomJoinRules -> when (renderingMode) {
RenderingMode.RoomList -> {
Timber.v("Filtering timeline item for room state change: $content")
null
}
RenderingMode.Timeline -> {
"RoomJoinRules"
}
}
OtherState.RoomPinnedEvents -> when (renderingMode) {
RenderingMode.RoomList -> {
Timber.v("Filtering timeline item for room state change: $content")
null
}
RenderingMode.Timeline -> {
"RoomPinnedEvents"
}
}
OtherState.RoomPowerLevels -> when (renderingMode) {
RenderingMode.RoomList -> {
Timber.v("Filtering timeline item for room state change: $content")
null
}
RenderingMode.Timeline -> {
"RoomPowerLevels"
}
}
OtherState.RoomServerAcl -> when (renderingMode) {
RenderingMode.RoomList -> {
Timber.v("Filtering timeline item for room state change: $content")
null
}
RenderingMode.Timeline -> {
"RoomServerAcl"
}
}
OtherState.RoomTombstone -> when (renderingMode) {
RenderingMode.RoomList -> {
Timber.v("Filtering timeline item for room state change: $content")
null
}
RenderingMode.Timeline -> {
"RoomTombstone"
}
}
OtherState.SpaceChild -> when (renderingMode) {
RenderingMode.RoomList -> {
Timber.v("Filtering timeline item for room state change: $content")
null
}
RenderingMode.Timeline -> {
"SpaceChild"
}
}
OtherState.SpaceParent -> when (renderingMode) {
RenderingMode.RoomList -> {
Timber.v("Filtering timeline item for room state change: $content")
null
}
RenderingMode.Timeline -> {
"SpaceParent"
}
}
}
}
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 2023 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 io.element.android.libraries.eventformatter.impl.mode
enum class RenderingMode {
RoomList,
Timeline,
}

View File

@@ -32,6 +32,9 @@ import io.element.android.libraries.dateformatter.impl.DefaultLastMessageTimesta
import io.element.android.libraries.dateformatter.impl.LocalDateTimeProvider
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
import io.element.android.libraries.eventformatter.impl.DefaultRoomLastMessageFormatter
import io.element.android.libraries.eventformatter.impl.ProfileChangeContentFormatter
import io.element.android.libraries.eventformatter.impl.RoomMembershipContentFormatter
import io.element.android.libraries.eventformatter.impl.StateContentFormatter
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
@@ -53,10 +56,17 @@ class RoomListScreen(
private val dateTimeProvider = LocalDateTimeProvider(clock, timeZone)
private val dateFormatters = DateFormatters(locale, clock, timeZone)
private val sessionVerificationService = matrixClient.sessionVerificationService()
private val stringProvider = AndroidStringProvider(context.resources)
private val presenter = RoomListPresenter(
client = matrixClient,
lastMessageTimestampFormatter = DefaultLastMessageTimestampFormatter(dateTimeProvider, dateFormatters),
roomLastMessageFormatter = DefaultRoomLastMessageFormatter(AndroidStringProvider(context.resources), matrixClient),
roomLastMessageFormatter = DefaultRoomLastMessageFormatter(
sp = stringProvider,
matrixClient = matrixClient,
roomMembershipContentFormatter = RoomMembershipContentFormatter(matrixClient, stringProvider),
profileChangeContentFormatter = ProfileChangeContentFormatter(stringProvider),
stateContentFormatter = StateContentFormatter(stringProvider),
),
sessionVerificationService = sessionVerificationService,
networkMonitor = NetworkMonitorImpl(context),
snackbarDispatcher = SnackbarDispatcher(),