diff --git a/changelog.d/7555.bugfix b/changelog.d/7555.bugfix new file mode 100644 index 0000000000..064b21a9e5 --- /dev/null +++ b/changelog.d/7555.bugfix @@ -0,0 +1 @@ +Missing translations on "replyTo" messages diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index ab98f7e141..0e2b2bef94 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -3464,4 +3464,13 @@ Apply underline format Toggle full screen mode + + In reply to + sent a file. + sent an audio file. + sent a voice message. + sent an image. + sent a video. + sent a sticker. + created a poll. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt index 6ae585a273..1e32437727 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt @@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageStickerConte import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.model.message.asMessageAudioEvent import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent +import org.matrix.android.sdk.api.session.room.model.relation.isReply import org.matrix.android.sdk.api.session.room.model.relation.shouldRenderInThread import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.threads.ThreadDetails @@ -228,11 +229,14 @@ data class Event( return when { isReplyRenderedInThread() || isQuote() -> ContentUtils.extractUsefulTextFromReply(text) isFileMessage() -> "sent a file." + isVoiceMessage() -> "sent a voice message." isAudioMessage() -> "sent an audio file." isImageMessage() -> "sent an image." isVideoMessage() -> "sent a video." - isSticker() -> "sent a sticker" + isSticker() -> "sent a sticker." isPoll() -> getPollQuestion() ?: "created a poll." + isLiveLocation() -> "Live location." + isLocationMessage() -> "has shared their location." else -> text } } @@ -420,7 +424,7 @@ fun Event.getRelationContentForType(type: String): RelationDefaultContent? = getRelationContent()?.takeIf { it.type == type } fun Event.isReply(): Boolean { - return getRelationContent()?.inReplyTo?.eventId != null + return getRelationContent().isReply() } fun Event.isReplyRenderedInThread(): Boolean { @@ -443,7 +447,7 @@ fun Event.isInvitation(): Boolean = type == EventType.STATE_ROOM_MEMBER && content?.toModel()?.membership == Membership.INVITE fun Event.getPollContent(): MessagePollContent? { - return content.toModel() + return getClearContent().toModel() } fun Event.supportsNotification() = diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt index cd8acbcccc..93208be27b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt @@ -47,10 +47,9 @@ interface LocationSharingService { /** * Starts sharing live location in the room. * @param timeoutMillis timeout of the live in milliseconds - * @param description description of the live for text fallback * @return the result of the update of the live */ - suspend fun startLiveLocationShare(timeoutMillis: Long, description: String): UpdateLiveLocationShareResult + suspend fun startLiveLocationShare(timeoutMillis: Long): UpdateLiveLocationShareResult /** * Stops sharing live location in the room. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationDefaultContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationDefaultContent.kt index 5dcb1b4323..b9f9335dbd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationDefaultContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationDefaultContent.kt @@ -28,3 +28,5 @@ data class RelationDefaultContent( ) : RelationContent fun RelationDefaultContent.shouldRenderInThread(): Boolean = isFallingBack == false + +fun RelationDefaultContent?.isReply(): Boolean = this?.inReplyTo?.eventId != null diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt index 60312071d7..c36efa064f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt @@ -73,7 +73,7 @@ internal class DefaultLocationSharingService @AssistedInject constructor( return sendLiveLocationTask.execute(params) } - override suspend fun startLiveLocationShare(timeoutMillis: Long, description: String): UpdateLiveLocationShareResult { + override suspend fun startLiveLocationShare(timeoutMillis: Long): UpdateLiveLocationShareResult { // Ensure to stop any active live before starting a new one if (checkIfExistingActiveLive()) { val result = stopLiveLocationShare() @@ -84,7 +84,6 @@ internal class DefaultLocationSharingService @AssistedInject constructor( val params = StartLiveLocationShareTask.Params( roomId = roomId, timeoutMillis = timeoutMillis, - description = description ) return startLiveLocationShareTask.execute(params) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt index 79019e4765..781def1abe 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt @@ -30,7 +30,6 @@ internal interface StartLiveLocationShareTask : Task buildVerificationRequestMessageItem(messageContent, informationData, highlight, callback, attributes) is MessagePollContent -> buildPollItem(messageContent, informationData, highlight, callback, attributes) is MessageLocationContent -> buildLocationItem(messageContent, informationData, highlight, attributes) - is MessageBeaconInfoContent -> liveLocationShareMessageItemFactory.create(params.event, highlight, attributes) + is MessageBeaconInfoContent -> liveLocationShareMessageItemFactory.create(event, highlight, attributes) is MessageVoiceBroadcastInfoContent -> voiceBroadcastItemFactory.create(params, messageContent, highlight, attributes) else -> buildNotHandledMessageItem(messageContent, informationData, highlight, callback, attributes) } @@ -437,7 +440,14 @@ class MessageItemFactory @Inject constructor( attributes: AbsMessageItem.Attributes ): MessageTextItem? { // For compatibility reason we should display the body - return buildMessageTextItem(messageContent.body, false, informationData, highlight, callback, attributes) + return buildMessageTextItem( + messageContent.body, + false, + informationData, + highlight, + callback, + attributes, + ) } private fun buildImageMessageItem( @@ -540,7 +550,8 @@ class MessageItemFactory @Inject constructor( ): VectorEpoxyModel<*>? { val matrixFormattedBody = messageContent.matrixFormattedBody return if (matrixFormattedBody != null) { - buildFormattedTextItem(matrixFormattedBody, informationData, highlight, callback, attributes) + val replyToContent = messageContent.relatesTo?.inReplyTo + buildFormattedTextItem(matrixFormattedBody, informationData, highlight, callback, attributes, replyToContent) } else { buildMessageTextItem(messageContent.body, false, informationData, highlight, callback, attributes) } @@ -552,10 +563,21 @@ class MessageItemFactory @Inject constructor( highlight: Boolean, callback: TimelineEventController.Callback?, attributes: AbsMessageItem.Attributes, + replyToContent: ReplyToContent?, ): MessageTextItem? { - val compressed = htmlCompressor.compress(matrixFormattedBody) + val processedBody = replyToContent + ?.let { processBodyOfReplyToEventUseCase.execute(roomId, matrixFormattedBody, it) } + ?: matrixFormattedBody + val compressed = htmlCompressor.compress(processedBody) val renderedFormattedBody = htmlRenderer.get().render(compressed, pillsPostProcessor) as Spanned - return buildMessageTextItem(renderedFormattedBody, true, informationData, highlight, callback, attributes) + return buildMessageTextItem( + renderedFormattedBody, + true, + informationData, + highlight, + callback, + attributes, + ) } private fun buildMessageTextItem( diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/render/EventTextRenderer.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/render/EventTextRenderer.kt index 920f3e3b80..c46112f995 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/render/EventTextRenderer.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/render/EventTextRenderer.kt @@ -34,22 +34,22 @@ class EventTextRenderer @AssistedInject constructor( @Assisted private val roomId: String?, private val context: Context, private val avatarRenderer: AvatarRenderer, - private val sessionHolder: ActiveSessionHolder + private val activeSessionHolder: ActiveSessionHolder, ) { - /* ========================================================================================== - * Public api - * ========================================================================================== */ - @AssistedFactory interface Factory { fun create(roomId: String?): EventTextRenderer } /** - * @param text the text you want to render + * @param text the text to be rendered */ fun render(text: CharSequence): CharSequence { + return renderNotifyEveryone(text) + } + + private fun renderNotifyEveryone(text: CharSequence): CharSequence { return if (roomId != null && text.contains(MatrixItem.NOTIFY_EVERYONE)) { SpannableStringBuilder(text).apply { addNotifyEveryoneSpans(this, roomId) @@ -59,12 +59,8 @@ class EventTextRenderer @AssistedInject constructor( } } - /* ========================================================================================== - * Helper methods - * ========================================================================================== */ - private fun addNotifyEveryoneSpans(text: Spannable, roomId: String) { - val room: RoomSummary? = sessionHolder.getSafeActiveSession()?.roomService()?.getRoomSummary(roomId) + val room: RoomSummary? = activeSessionHolder.getSafeActiveSession()?.roomService()?.getRoomSummary(roomId) val matrixItem = MatrixItem.EveryoneInRoomItem( id = roomId, avatarUrl = room?.avatarUrl, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/render/ProcessBodyOfReplyToEventUseCase.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/render/ProcessBodyOfReplyToEventUseCase.kt new file mode 100644 index 0000000000..2197d89a2c --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/render/ProcessBodyOfReplyToEventUseCase.kt @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2022 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.app.features.home.room.detail.timeline.render + +import im.vector.app.R +import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.core.resources.StringProvider +import org.matrix.android.sdk.api.session.events.model.getPollQuestion +import org.matrix.android.sdk.api.session.events.model.isAudioMessage +import org.matrix.android.sdk.api.session.events.model.isFileMessage +import org.matrix.android.sdk.api.session.events.model.isImageMessage +import org.matrix.android.sdk.api.session.events.model.isLiveLocation +import org.matrix.android.sdk.api.session.events.model.isPoll +import org.matrix.android.sdk.api.session.events.model.isSticker +import org.matrix.android.sdk.api.session.events.model.isVideoMessage +import org.matrix.android.sdk.api.session.events.model.isVoiceMessage +import org.matrix.android.sdk.api.session.getRoom +import org.matrix.android.sdk.api.session.room.getTimelineEvent +import org.matrix.android.sdk.api.session.room.model.relation.ReplyToContent +import javax.inject.Inject + +private const val IN_REPLY_TO = "In reply to" +private const val BREAKING_LINE = "
" +private const val ENDING_BLOCK_QUOTE = "" + +class ProcessBodyOfReplyToEventUseCase @Inject constructor( + private val activeSessionHolder: ActiveSessionHolder, + private val stringProvider: StringProvider, +) { + + fun execute(roomId: String, matrixFormattedBody: String, replyToContent: ReplyToContent): String { + val repliedToEvent = replyToContent.eventId?.let { getEvent(it, roomId) } + val breakingLineIndex = matrixFormattedBody.indexOf(BREAKING_LINE) + val endOfBlockQuoteIndex = matrixFormattedBody.lastIndexOf(ENDING_BLOCK_QUOTE) + + val withTranslatedContent = if (repliedToEvent != null && breakingLineIndex != -1 && endOfBlockQuoteIndex != -1) { + val afterBreakingLineIndex = breakingLineIndex + BREAKING_LINE.length + when { + repliedToEvent.isFileMessage() -> { + matrixFormattedBody.replaceRange( + afterBreakingLineIndex, + endOfBlockQuoteIndex, + stringProvider.getString(R.string.message_reply_to_sender_sent_file) + ) + } + repliedToEvent.isVoiceMessage() -> { + matrixFormattedBody.replaceRange( + afterBreakingLineIndex, + endOfBlockQuoteIndex, + stringProvider.getString(R.string.message_reply_to_sender_sent_voice_message) + ) + } + repliedToEvent.isAudioMessage() -> { + matrixFormattedBody.replaceRange( + afterBreakingLineIndex, + endOfBlockQuoteIndex, + stringProvider.getString(R.string.message_reply_to_sender_sent_audio_file) + ) + } + repliedToEvent.isImageMessage() -> { + matrixFormattedBody.replaceRange( + afterBreakingLineIndex, + endOfBlockQuoteIndex, + stringProvider.getString(R.string.message_reply_to_sender_sent_image) + ) + } + repliedToEvent.isVideoMessage() -> { + matrixFormattedBody.replaceRange( + afterBreakingLineIndex, + endOfBlockQuoteIndex, + stringProvider.getString(R.string.message_reply_to_sender_sent_video) + ) + } + repliedToEvent.isSticker() -> { + matrixFormattedBody.replaceRange( + afterBreakingLineIndex, + endOfBlockQuoteIndex, + stringProvider.getString(R.string.message_reply_to_sender_sent_sticker) + ) + } + repliedToEvent.isPoll() -> { + matrixFormattedBody.replaceRange( + afterBreakingLineIndex, + endOfBlockQuoteIndex, + repliedToEvent.getPollQuestion() ?: stringProvider.getString(R.string.message_reply_to_sender_created_poll) + ) + } + repliedToEvent.isLiveLocation() -> { + matrixFormattedBody.replaceRange( + afterBreakingLineIndex, + endOfBlockQuoteIndex, + stringProvider.getString(R.string.live_location_description) + ) + } + else -> matrixFormattedBody + } + } else { + matrixFormattedBody + } + + return withTranslatedContent.replace( + IN_REPLY_TO, + stringProvider.getString(R.string.message_reply_to_prefix) + ) + } + + private fun getEvent(eventId: String, roomId: String) = + activeSessionHolder.getSafeActiveSession() + ?.getRoom(roomId) + ?.getTimelineEvent(eventId) + ?.root +} diff --git a/vector/src/main/java/im/vector/app/features/location/live/tracking/LocationSharingAndroidService.kt b/vector/src/main/java/im/vector/app/features/location/live/tracking/LocationSharingAndroidService.kt index 545f98f01e..ccab23a83b 100644 --- a/vector/src/main/java/im/vector/app/features/location/live/tracking/LocationSharingAndroidService.kt +++ b/vector/src/main/java/im/vector/app/features/location/live/tracking/LocationSharingAndroidService.kt @@ -21,7 +21,6 @@ import android.os.IBinder import android.os.Parcelable import androidx.core.app.NotificationManagerCompat import dagger.hilt.android.AndroidEntryPoint -import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.services.VectorAndroidService import im.vector.app.features.location.LocationData @@ -125,10 +124,7 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca val updateLiveResult = session .getRoom(roomArgs.roomId) ?.locationSharingService() - ?.startLiveLocationShare( - timeoutMillis = roomArgs.durationMillis, - description = getString(R.string.live_location_description) - ) + ?.startLiveLocationShare(roomArgs.durationMillis) updateLiveResult ?.let { result -> diff --git a/vector/src/test/java/im/vector/app/features/home/room/detail/timeline/render/ProcessBodyOfReplyToEventUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/home/room/detail/timeline/render/ProcessBodyOfReplyToEventUseCaseTest.kt new file mode 100644 index 0000000000..f612861511 --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/home/room/detail/timeline/render/ProcessBodyOfReplyToEventUseCaseTest.kt @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2022 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.app.features.home.room.detail.timeline.render + +import android.annotation.StringRes +import im.vector.app.R +import im.vector.app.test.fakes.FakeActiveSessionHolder +import im.vector.app.test.fakes.FakeStringProvider +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.unmockkAll +import org.amshove.kluent.shouldBeEqualTo +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.events.model.getPollQuestion +import org.matrix.android.sdk.api.session.events.model.isAudioMessage +import org.matrix.android.sdk.api.session.events.model.isFileMessage +import org.matrix.android.sdk.api.session.events.model.isImageMessage +import org.matrix.android.sdk.api.session.events.model.isLiveLocation +import org.matrix.android.sdk.api.session.events.model.isPoll +import org.matrix.android.sdk.api.session.events.model.isSticker +import org.matrix.android.sdk.api.session.events.model.isVideoMessage +import org.matrix.android.sdk.api.session.events.model.isVoiceMessage +import org.matrix.android.sdk.api.session.room.model.relation.ReplyToContent +import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent + +private const val A_ROOM_ID = "room-id" +private const val AN_EVENT_ID = "event-id" +private const val A_REPLY_TO_EVENT_MATRIX_FORMATTED_BODY = + "" + + "
" + + "In reply to " + + "@user:matrix.org" + + "
" + + "Message content" + + "
" + + "
" + + "Reply text" +private const val A_NEW_PREFIX = "new-prefix" +private const val A_NEW_CONTENT = "new-content" +private const val PREFIX_PROCESSED_ONLY_REPLY_TO_EVENT_MATRIX_FORMATTED_BODY = + "" + + "
" + + "$A_NEW_PREFIX " + + "@user:matrix.org" + + "
" + + "Message content" + + "
" + + "
" + + "Reply text" +private const val FULLY_PROCESSED_REPLY_TO_EVENT_MATRIX_FORMATTED_BODY = + "" + + "
" + + "$A_NEW_PREFIX " + + "@user:matrix.org" + + "
" + + A_NEW_CONTENT + + "
" + + "
" + + "Reply text" + +class ProcessBodyOfReplyToEventUseCaseTest { + + private val fakeActiveSessionHolder = FakeActiveSessionHolder() + private val fakeStringProvider = FakeStringProvider() + private val fakeReplyToContent = ReplyToContent(eventId = AN_EVENT_ID) + private val fakeRepliedEvent = givenARepliedEvent() + + private val processBodyOfReplyToEventUseCase = ProcessBodyOfReplyToEventUseCase( + activeSessionHolder = fakeActiveSessionHolder.instance, + stringProvider = fakeStringProvider.instance, + ) + + @Before + fun setup() { + givenNewPrefix() + mockkStatic("org.matrix.android.sdk.api.session.events.model.EventKt") + } + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun `given a replied event of type file message when process the formatted body then content is replaced by correct string`() { + // Given + givenTypeOfRepliedEvent(isFileMessage = true) + givenNewContentForId(R.string.message_reply_to_sender_sent_file) + + executeAndAssertResult() + } + + @Test + fun `given a replied event of type voice message when process the formatted body then content is replaced by correct string`() { + // Given + givenTypeOfRepliedEvent(isVoiceMessage = true) + givenNewContentForId(R.string.message_reply_to_sender_sent_voice_message) + + executeAndAssertResult() + } + + @Test + fun `given a replied event of type audio message when process the formatted body then content is replaced by correct string`() { + // Given + givenTypeOfRepliedEvent(isAudioMessage = true) + givenNewContentForId(R.string.message_reply_to_sender_sent_audio_file) + + executeAndAssertResult() + } + + @Test + fun `given a replied event of type image message when process the formatted body then content is replaced by correct string`() { + // Given + givenTypeOfRepliedEvent(isImageMessage = true) + givenNewContentForId(R.string.message_reply_to_sender_sent_image) + + executeAndAssertResult() + } + + @Test + fun `given a replied event of type video message when process the formatted body then content is replaced by correct string`() { + // Given + givenTypeOfRepliedEvent(isVideoMessage = true) + givenNewContentForId(R.string.message_reply_to_sender_sent_video) + + executeAndAssertResult() + } + + @Test + fun `given a replied event of type sticker message when process the formatted body then content is replaced by correct string`() { + // Given + givenTypeOfRepliedEvent(isStickerMessage = true) + givenNewContentForId(R.string.message_reply_to_sender_sent_sticker) + + executeAndAssertResult() + } + + @Test + fun `given a replied event of type poll message with null question when process the formatted body then content is replaced by correct string`() { + // Given + givenTypeOfRepliedEvent(isPollMessage = true) + givenNewContentForId(R.string.message_reply_to_sender_created_poll) + every { fakeRepliedEvent.getPollQuestion() } returns null + + executeAndAssertResult() + } + + @Test + fun `given a replied event of type poll message with existing question when process the formatted body then content is replaced by correct string`() { + // Given + givenTypeOfRepliedEvent(isPollMessage = true) + givenNewContentForId(R.string.message_reply_to_sender_created_poll) + every { fakeRepliedEvent.getPollQuestion() } returns A_NEW_CONTENT + + executeAndAssertResult() + } + + @Test + fun `given a replied event of type live location message when process the formatted body then content is replaced by correct string`() { + // Given + givenTypeOfRepliedEvent(isLiveLocationMessage = true) + givenNewContentForId(R.string.live_location_description) + + executeAndAssertResult() + } + + @Test + fun `given a replied event of type not handled when process the formatted body only prefix is replaced by correct string`() { + // Given + givenTypeOfRepliedEvent() + + // When + val result = processBodyOfReplyToEventUseCase.execute( + roomId = A_ROOM_ID, + matrixFormattedBody = A_REPLY_TO_EVENT_MATRIX_FORMATTED_BODY, + replyToContent = fakeReplyToContent, + ) + + // Then + result shouldBeEqualTo PREFIX_PROCESSED_ONLY_REPLY_TO_EVENT_MATRIX_FORMATTED_BODY + } + + @Test + fun `given no replied event found when process the formatted body then only prefix is replaced by correct string`() { + // Given + givenARepliedEvent(timelineEvent = null) + + // When + val result = processBodyOfReplyToEventUseCase.execute( + roomId = A_ROOM_ID, + matrixFormattedBody = A_REPLY_TO_EVENT_MATRIX_FORMATTED_BODY, + replyToContent = fakeReplyToContent, + ) + + // Then + result shouldBeEqualTo PREFIX_PROCESSED_ONLY_REPLY_TO_EVENT_MATRIX_FORMATTED_BODY + } + + private fun executeAndAssertResult() { + // When + val result = processBodyOfReplyToEventUseCase.execute( + roomId = A_ROOM_ID, + matrixFormattedBody = A_REPLY_TO_EVENT_MATRIX_FORMATTED_BODY, + replyToContent = fakeReplyToContent, + ) + + // Then + result shouldBeEqualTo FULLY_PROCESSED_REPLY_TO_EVENT_MATRIX_FORMATTED_BODY + } + + private fun givenARepliedEvent(timelineEvent: TimelineEvent? = mockk()): Event { + val event = mockk() + timelineEvent?.let { every { it.root } returns event } + fakeActiveSessionHolder + .fakeSession + .roomService() + .getRoom(A_ROOM_ID) + .timelineService() + .givenTimelineEvent(timelineEvent) + return event + } + + private fun givenTypeOfRepliedEvent( + isFileMessage: Boolean = false, + isVoiceMessage: Boolean = false, + isAudioMessage: Boolean = false, + isImageMessage: Boolean = false, + isVideoMessage: Boolean = false, + isStickerMessage: Boolean = false, + isPollMessage: Boolean = false, + isLiveLocationMessage: Boolean = false, + ) { + every { fakeRepliedEvent.isFileMessage() } returns isFileMessage + every { fakeRepliedEvent.isVoiceMessage() } returns isVoiceMessage + every { fakeRepliedEvent.isAudioMessage() } returns isAudioMessage + every { fakeRepliedEvent.isImageMessage() } returns isImageMessage + every { fakeRepliedEvent.isVideoMessage() } returns isVideoMessage + every { fakeRepliedEvent.isSticker() } returns isStickerMessage + every { fakeRepliedEvent.isPoll() } returns isPollMessage + every { fakeRepliedEvent.isLiveLocation() } returns isLiveLocationMessage + } + + private fun givenNewPrefix() { + fakeStringProvider.given(R.string.message_reply_to_prefix, A_NEW_PREFIX) + } + + private fun givenNewContentForId(@StringRes resId: Int) { + fakeStringProvider.given(resId, A_NEW_CONTENT) + } +} diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeTimelineService.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeTimelineService.kt index 56f38724b1..a5fac5f1a1 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeTimelineService.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeTimelineService.kt @@ -23,7 +23,7 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineService class FakeTimelineService : TimelineService by mockk() { - fun givenTimelineEvent(event: TimelineEvent) { + fun givenTimelineEvent(event: TimelineEvent?) { every { getTimelineEvent(any()) } returns event } }