diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 3efb2d8dd4c..dbbf81b44bc 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - - \ No newline at end of file + diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt index 91fcc2593e2..4183e225314 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt @@ -177,8 +177,8 @@ class DefaultActiveCallManager( suspend fun incomingCallTimedOut(displayMissedCallNotification: Boolean) = mutex.withLock { Timber.tag(tag).d("Incoming call timed out") - val previousActiveCall = activeCall.value ?: return - val notificationData = (previousActiveCall.callState as? CallState.Ringing)?.notificationData ?: return + val previousActiveCall = activeCall.value ?: return@withLock + val notificationData = (previousActiveCall.callState as? CallState.Ringing)?.notificationData ?: return@withLock activeCall.value = null if (activeWakeLock?.isHeld == true) { Timber.tag(tag).d("Releasing partial wakelock after timeout") @@ -196,11 +196,11 @@ class DefaultActiveCallManager( Timber.tag(tag).d("Hung up call: $callType") val currentActiveCall = activeCall.value ?: run { Timber.tag(tag).w("No active call, ignoring hang up") - return + return@withLock } if (currentActiveCall.callType != callType) { Timber.tag(tag).w("Call type $callType does not match the active call type, ignoring") - return + return@withLock } if (currentActiveCall.callState is CallState.Ringing) { // Decline the call diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomPresenterTest.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomPresenterTest.kt index c8a6c2bd8a9..6de15904d7f 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomPresenterTest.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomPresenterTest.kt @@ -51,15 +51,10 @@ import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.test -import io.mockk.every import io.mockk.mockk -import io.mockk.mockkStatic -import io.mockk.unmockkAll import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest -import org.junit.After -import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -76,17 +71,6 @@ class ConfigureRoomPresenterTest { @get:Rule val warmUpRule = WarmUpRule() - @Before - fun setup() { - mockkStatic(File::readBytes) - every { any().readBytes() } returns byteArrayOf() - } - - @After - fun tearDown() { - unmockkAll() - } - @Test fun `present - initial state`() = runTest { val presenter = createConfigureRoomPresenter() @@ -261,20 +245,25 @@ class ConfigureRoomPresenterTest { val initialState = initialState() dataStore.setAvatarUri(Uri.parse(AN_URI_FROM_GALLERY)) skipItems(1) - mediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(mockk(), mockk(), mockk()))) - matrixClient.givenUploadMediaResult(Result.failure(AN_EXCEPTION)) - - initialState.eventSink(ConfigureRoomEvents.CreateRoom) - assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) - val stateAfterCreateRoom = awaitItem() - assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Failure::class.java) - assertThat(analyticsService.capturedEvents.filterIsInstance()).isEmpty() - - matrixClient.givenUploadMediaResult(Result.success(AN_AVATAR_URL)) - stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom) - assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java) - assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) - assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Success::class.java) + val file = File.createTempFile("test", "jpg") + try { + mediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(file, mockk(), mockk()))) + matrixClient.givenUploadMediaResult(Result.failure(AN_EXCEPTION)) + + initialState.eventSink(ConfigureRoomEvents.CreateRoom) + assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) + val stateAfterCreateRoom = awaitItem() + assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Failure::class.java) + assertThat(analyticsService.capturedEvents.filterIsInstance()).isEmpty() + + matrixClient.givenUploadMediaResult(Result.success(AN_AVATAR_URL)) + stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom) + assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java) + assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java) + assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Success::class.java) + } finally { + file.delete() + } } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index a953c6e3491..5bafaf3e88e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -394,9 +394,8 @@ class TimelinePresenter( newMostRecentItemId != prevMostRecentItemIdValue if (hasNewEvent) { - val newMostRecentEvent = newMostRecentItem // Scroll to bottom if the new event is from me, even if sent from another device - val fromMe = newMostRecentEvent?.isMine == true + val fromMe = newMostRecentItem.isMine newEventState.value = if (fromMe) { NewEventState.FromMe } else { diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt index d69101cbba2..0602709ec82 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt @@ -56,8 +56,6 @@ class EditUserProfilePresenterTest { private val userAvatarUri: Uri = mockk() private val anotherAvatarUri: Uri = mockk() - private val fakeFileContents = ByteArray(2) - @Before fun setup() { fakePickerProvider = FakePickerProvider() @@ -397,7 +395,7 @@ class EditUserProfilePresenterTest { fun `present - save processes and sets avatar when processor returns successfully`() = runTest { val matrixClient = FakeMatrixClient() val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) - givenPickerReturnsFile() + val tmpFile = givenPickerReturnsFile() val presenter = createEditUserProfilePresenter( matrixClient = matrixClient, matrixUser = user, @@ -405,12 +403,16 @@ class EditUserProfilePresenterTest { deleteLambda = { assertThat(it).isEqualTo(userAvatarUri) } ), ) - presenter.test { - val initialState = awaitItem() - initialState.eventSink(EditUserProfileEvent.HandleAvatarAction(AvatarAction.ChoosePhoto)) - initialState.eventSink(EditUserProfileEvent.Save) - consumeItemsUntilPredicate { matrixClient.uploadAvatarCalled } - assertThat(matrixClient.uploadAvatarCalled).isTrue() + try { + presenter.test { + val initialState = awaitItem() + initialState.eventSink(EditUserProfileEvent.HandleAvatarAction(AvatarAction.ChoosePhoto)) + initialState.eventSink(EditUserProfileEvent.Save) + consumeItemsUntilPredicate { matrixClient.uploadAvatarCalled } + assertThat(matrixClient.uploadAvatarCalled).isTrue() + } + } finally { + tmpFile.delete() } } @@ -457,30 +459,38 @@ class EditUserProfilePresenterTest { @Test fun `present - sets save action to failure if setting avatar fails`() = runTest { - givenPickerReturnsFile() + val tmpFile = givenPickerReturnsFile() val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) val matrixClient = FakeMatrixClient().apply { givenUploadAvatarResult(Result.failure(RuntimeException("!"))) } - saveAndAssertFailure(user, matrixClient, EditUserProfileEvent.HandleAvatarAction(AvatarAction.ChoosePhoto)) + try { + saveAndAssertFailure(user, matrixClient, EditUserProfileEvent.HandleAvatarAction(AvatarAction.ChoosePhoto)) + } finally { + tmpFile.delete() + } } @Test fun `present - CloseDialog resets save action state`() = runTest { - givenPickerReturnsFile() + val tmpFile = givenPickerReturnsFile() val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL) val matrixClient = FakeMatrixClient().apply { givenSetDisplayNameResult(Result.failure(RuntimeException("!"))) } val presenter = createEditUserProfilePresenter(matrixUser = user, matrixClient = matrixClient) - presenter.test { - val initialState = awaitItem() - initialState.eventSink(EditUserProfileEvent.UpdateDisplayName("foo")) - initialState.eventSink(EditUserProfileEvent.Save) - skipItems(2) - assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java) - initialState.eventSink(EditUserProfileEvent.CloseDialog) - assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Uninitialized::class.java) + try { + presenter.test { + val initialState = awaitItem() + initialState.eventSink(EditUserProfileEvent.UpdateDisplayName("foo")) + initialState.eventSink(EditUserProfileEvent.Save) + skipItems(2) + assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java) + initialState.eventSink(EditUserProfileEvent.CloseDialog) + assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Uninitialized::class.java) + } + } finally { + tmpFile.delete() } } @@ -502,20 +512,18 @@ class EditUserProfilePresenterTest { } } - private fun givenPickerReturnsFile() { - mockkStatic(File::readBytes) - val processedFile: File = mockk { - every { readBytes() } returns fakeFileContents - } + private fun givenPickerReturnsFile(): File { + val file = File.createTempFile("test", "jpg") fakePickerProvider.givenResult(anotherAvatarUri) fakeMediaPreProcessor.givenResult( Result.success( MediaUploadInfo.AnyFile( - file = processedFile, + file = file, fileInfo = mockk(), ) ) ) + return file } companion object { diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt index f82c57889fc..41e136a53e5 100755 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt @@ -323,7 +323,7 @@ class DefaultBugReporterTest { while (part != null) { part.headers["Content-Disposition"]?.let { contentDisposition -> regex.find(contentDisposition)?.groupValues?.get(1)?.let { name -> - foundValues.put(name, part!!.body.readUtf8()) + foundValues.put(name, part.body.readUtf8()) } } part = multipartReader.nextPart() diff --git a/features/roomdetailsedit/impl/src/test/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditPresenterTest.kt b/features/roomdetailsedit/impl/src/test/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditPresenterTest.kt index 9c7f840af25..72ba466ffae 100644 --- a/features/roomdetailsedit/impl/src/test/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditPresenterTest.kt +++ b/features/roomdetailsedit/impl/src/test/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditPresenterTest.kt @@ -35,6 +35,7 @@ import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.fake.FakeTemporaryUriDeleter import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.matching import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.test import io.mockk.every @@ -528,22 +529,29 @@ class RoomDetailsEditPresenterTest { avatarUrl = AN_AVATAR_URL, updateAvatarResult = updateAvatarResult, ) - givenPickerReturnsFile() + val tmpFile = givenPickerReturnsFile() val deleteCallback = lambdaRecorder {} val presenter = createRoomDetailsEditPresenter( room = room, temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), ) - presenter.test { - val initialState = awaitItem() - initialState.eventSink(RoomDetailsEditEvent.HandleAvatarAction(AvatarAction.ChoosePhoto)) - initialState.eventSink(RoomDetailsEditEvent.Save) - skipItems(4) - updateAvatarResult.assertions().isCalledOnce().with(value(MimeTypes.Jpeg), value(fakeFileContents)) - deleteCallback.assertions().isCalledExactly(2).withSequence( - listOf(value(null)), - listOf(value(roomAvatarUri)), - ) + try { + presenter.test { + val initialState = awaitItem() + initialState.eventSink(RoomDetailsEditEvent.HandleAvatarAction(AvatarAction.ChoosePhoto)) + initialState.eventSink(RoomDetailsEditEvent.Save) + skipItems(4) + updateAvatarResult.assertions().isCalledOnce().with( + value(MimeTypes.Jpeg), + matching { it.contentEquals(fakeFileContents) } + ) + deleteCallback.assertions().isCalledExactly(2).withSequence( + listOf(value(null)), + listOf(value(roomAvatarUri)), + ) + } + } finally { + tmpFile.delete() } } @@ -605,19 +613,23 @@ class RoomDetailsEditPresenterTest { @Test fun `present - sets save action to failure if setting avatar fails`() = runTest { - givenPickerReturnsFile() + val tmpFile = givenPickerReturnsFile() val room = aJoinedRoom( topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL, updateAvatarResult = { _, _ -> Result.failure(RuntimeException("!")) }, ) - saveAndAssertFailure(room, RoomDetailsEditEvent.HandleAvatarAction(AvatarAction.ChoosePhoto), deleteCallbackNumberOfInvocation = 2) + try { + saveAndAssertFailure(room, RoomDetailsEditEvent.HandleAvatarAction(AvatarAction.ChoosePhoto), deleteCallbackNumberOfInvocation = 2) + } finally { + tmpFile.delete() + } } @Test fun `present - CancelSaveChanges resets save action state`() = runTest { - givenPickerReturnsFile() + val tmpFile = givenPickerReturnsFile() val room = aJoinedRoom( topic = "My topic", displayName = "Name", @@ -629,14 +641,18 @@ class RoomDetailsEditPresenterTest { room = room, temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback), ) - presenter.test { - val initialState = awaitItem() - initialState.eventSink(RoomDetailsEditEvent.UpdateRoomTopic("foo")) - initialState.eventSink(RoomDetailsEditEvent.Save) - skipItems(3) - assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java) - initialState.eventSink(RoomDetailsEditEvent.CloseDialog) - assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Uninitialized::class.java) + try { + presenter.test { + val initialState = awaitItem() + initialState.eventSink(RoomDetailsEditEvent.UpdateRoomTopic("foo")) + initialState.eventSink(RoomDetailsEditEvent.Save) + skipItems(3) + assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java) + initialState.eventSink(RoomDetailsEditEvent.CloseDialog) + assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Uninitialized::class.java) + } + } finally { + tmpFile.delete() } } @@ -736,20 +752,19 @@ class RoomDetailsEditPresenterTest { } } - private fun givenPickerReturnsFile() { - mockkStatic(File::readBytes) - val processedFile: File = mockk { - every { readBytes() } returns fakeFileContents - } + private fun givenPickerReturnsFile(): File { + val tmpFile = File.createTempFile("test", "jpg") + tmpFile.writeBytes(fakeFileContents) fakePickerProvider.givenResult(anotherAvatarUri) fakeMediaPreProcessor.givenResult( Result.success( MediaUploadInfo.AnyFile( - file = processedFile, + file = tmpFile, fileInfo = mockk(), ) ) ) + return tmpFile } private fun aJoinedRoom( diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1183f3687ff..db06b5bd574 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,9 +5,9 @@ # Project android_gradle_plugin = "8.13.2" # When updateing this, please also update the version in the file ./idea/kotlinc.xml -kotlin = "2.2.20" +kotlin = "2.3.0" kotlinpoet = "2.2.0" -ksp = "2.2.20-2.0.4" +ksp = "2.3.4" firebaseAppDistribution = "5.2.0" # AndroidX @@ -62,7 +62,7 @@ detekt = "1.23.8" # See https://github.com/pinterest/ktlint/releases/ ktlint = "1.8.0" androidx-test-ext-junit = "1.3.0" -kover = "0.9.2" +kover = "0.9.4" [libraries] # Project diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheUpdater.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheUpdater.kt index fce510f69c7..fb3c610a018 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheUpdater.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheUpdater.kt @@ -28,7 +28,7 @@ class DiffCacheUpdater( private val cacheInvalidator: DiffCacheInvalidator = DefaultDiffCacheInvalidator(), private val areItemsTheSame: (oldItem: ListItem?, newItem: ListItem?) -> Boolean, ) { - private val lock = Object() + private val lock = Any() private var prevOriginalList: List = emptyList() private val listUpdateCallback = object : ListUpdateCallback { diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/text/LinkifyHelper.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/text/LinkifyHelper.kt index 916f365b282..b95dacc5a8d 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/text/LinkifyHelper.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/text/LinkifyHelper.kt @@ -30,6 +30,7 @@ object LinkifyHelper { @LinkifyCompat.LinkifyMask linkifyMask: Int = Linkify.WEB_URLS or Linkify.PHONE_NUMBERS or Linkify.EMAIL_ADDRESSES, ): CharSequence { // Convert the text to a Spannable to be able to add URL spans, return the original text if it's not possible (in tests, i.e.) + @Suppress("USELESS_ELVIS") val spannable = text.toSpannable() ?: return text // Get all URL spans, as they will be removed by LinkifyCompat.addLinks diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt index 6115dd2345d..4451de6276f 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt @@ -158,7 +158,7 @@ class FakeTimeline( imageInfo: ImageInfo, body: String?, formattedBody: String?, - inReplyToEventId: EventId??, + inReplyToEventId: EventId?, ) -> Result = { _, _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } @@ -169,7 +169,7 @@ class FakeTimeline( imageInfo: ImageInfo, caption: String?, formattedCaption: String?, - inReplyToEventId: EventId??, + inReplyToEventId: EventId?, ): Result = simulateLongTask { sendImageLambda( file, @@ -187,7 +187,7 @@ class FakeTimeline( videoInfo: VideoInfo, body: String?, formattedBody: String?, - inReplyToEventId: EventId??, + inReplyToEventId: EventId?, ) -> Result = { _, _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } @@ -198,7 +198,7 @@ class FakeTimeline( videoInfo: VideoInfo, caption: String?, formattedCaption: String?, - inReplyToEventId: EventId??, + inReplyToEventId: EventId?, ): Result = simulateLongTask { sendVideoLambda( file, @@ -215,7 +215,7 @@ class FakeTimeline( audioInfo: AudioInfo, caption: String?, formattedCaption: String?, - inReplyToEventId: EventId??, + inReplyToEventId: EventId?, ) -> Result = { _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } @@ -225,7 +225,7 @@ class FakeTimeline( audioInfo: AudioInfo, caption: String?, formattedCaption: String?, - inReplyToEventId: EventId??, + inReplyToEventId: EventId?, ): Result = simulateLongTask { sendAudioLambda( file, @@ -241,7 +241,7 @@ class FakeTimeline( fileInfo: FileInfo, caption: String?, formattedCaption: String?, - inReplyToEventId: EventId??, + inReplyToEventId: EventId?, ) -> Result = { _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } @@ -251,7 +251,7 @@ class FakeTimeline( fileInfo: FileInfo, caption: String?, formattedCaption: String?, - inReplyToEventId: EventId??, + inReplyToEventId: EventId?, ): Result = simulateLongTask { sendFileLambda( file, @@ -266,7 +266,7 @@ class FakeTimeline( file: File, audioInfo: AudioInfo, waveform: List, - inReplyToEventId: EventId??, + inReplyToEventId: EventId?, ) -> Result = { _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } @@ -275,7 +275,7 @@ class FakeTimeline( file: File, audioInfo: AudioInfo, waveform: List, - inReplyToEventId: EventId??, + inReplyToEventId: EventId?, ): Result = simulateLongTask { sendVoiceMessageLambda( file, @@ -291,7 +291,7 @@ class FakeTimeline( description: String?, zoomLevel: Int?, assetType: AssetType?, - inReplyToEventId: EventId??, + inReplyToEventId: EventId?, ) -> Result = { _, _, _, _, _, _ -> lambdaError() } @@ -302,7 +302,7 @@ class FakeTimeline( description: String?, zoomLevel: Int?, assetType: AssetType?, - inReplyToEventId: EventId??, + inReplyToEventId: EventId?, ): Result = simulateLongTask { sendLocationLambda( body,