From 5f5d1a21ba43f33281adece53661f8bb5df36703 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 10 Oct 2025 14:13:32 +0200 Subject: [PATCH] Replace Uri by String in State that are used in Composable function. --- .../configureroom/ConfigureRoomPresenter.kt | 3 ++- .../impl/configureroom/ConfigureRoomView.kt | 3 +-- .../impl/configureroom/CreateRoomConfig.kt | 6 +---- .../configureroom/CreateRoomConfigStore.kt | 2 +- .../ConfigureRoomPresenterTest.kt | 13 +++++----- .../editprofile/EditUserProfilePresenter.kt | 24 +++++++++++-------- .../user/editprofile/EditUserProfileState.kt | 6 +---- .../EditUserProfileStateProvider.kt | 6 ++--- .../EditUserProfilePresenterTest.kt | 20 +++++++++------- .../impl/edit/RoomDetailsEditPresenter.kt | 20 ++++++++-------- .../impl/edit/RoomDetailsEditState.kt | 6 +---- .../impl/edit/RoomDetailsEditStateProvider.kt | 6 ++--- .../impl/edit/RoomDetailsEditPresenterTest.kt | 22 +++++++++-------- .../ui/components/EditableAvatarView.kt | 20 +++++++--------- .../matrix/ui/components/UnsavedAvatar.kt | 7 +++--- 15 files changed, 77 insertions(+), 87 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index 29b9842abe..ae28b95bba 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -17,6 +17,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.core.net.toUri import dev.zacsweers.metro.Inject import im.vector.app.features.analytics.plan.CreatedRoom import io.element.android.libraries.architecture.AsyncAction @@ -157,7 +158,7 @@ class ConfigureRoomPresenter( createRoomAction: MutableState> ) = launch { suspend { - val avatarUrl = config.avatarUri?.let { uploadAvatar(it) } + val avatarUrl = config.avatarUri?.let { uploadAvatar(it.toUri()) } val params = if (config.roomVisibility is RoomVisibilityState.Public) { CreateRoomParameters( name = config.roomName, diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index 8c56607a22..cd8c1e7674 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -7,7 +7,6 @@ package io.element.android.features.createroom.impl.configureroom -import android.net.Uri import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -192,7 +191,7 @@ private fun ConfigureRoomToolbar( @Composable private fun RoomNameWithAvatar( - avatarUri: Uri?, + avatarUri: String?, roomName: String, onAvatarClick: () -> Unit, onChangeRoomName: (String) -> Unit, diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/CreateRoomConfig.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/CreateRoomConfig.kt index f157dda2d5..797000b21e 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/CreateRoomConfig.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/CreateRoomConfig.kt @@ -7,18 +7,14 @@ package io.element.android.features.createroom.impl.configureroom -import android.net.Uri -import androidx.compose.runtime.Immutable import io.element.android.libraries.matrix.api.user.MatrixUser import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -// Annotate with @Immutable since `Uri` is unstable -@Immutable data class CreateRoomConfig( val roomName: String? = null, val topic: String? = null, - val avatarUri: Uri? = null, + val avatarUri: String? = null, val invites: ImmutableList = persistentListOf(), val roomVisibility: RoomVisibilityState = RoomVisibilityState.Private, ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/CreateRoomConfigStore.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/CreateRoomConfigStore.kt index f2701216c3..671141bf5a 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/CreateRoomConfigStore.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/CreateRoomConfigStore.kt @@ -62,7 +62,7 @@ class CreateRoomConfigStore( fun setAvatarUri(uri: Uri?, cached: Boolean = false) { cachedAvatarUri = uri.takeIf { cached } createRoomConfigFlow.getAndUpdate { config -> - config.copy(avatarUri = uri) + config.copy(avatarUri = uri?.toString()) } } 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 635a2b330e..08dd1e904f 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 @@ -8,6 +8,7 @@ package io.element.android.features.startchat.impl.configureroom import android.net.Uri +import androidx.core.net.toUri import app.cash.turbine.TurbineTestContext import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.CreatedRoom @@ -155,15 +156,15 @@ class ConfigureRoomPresenterTest { // Pick avatar pickerProvider.givenResult(null) // From gallery - val uriFromGallery = Uri.parse(AN_URI_FROM_GALLERY) - pickerProvider.givenResult(uriFromGallery) + val uriFromGallery = AN_URI_FROM_GALLERY + pickerProvider.givenResult(uriFromGallery.toUri()) newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) newState = awaitItem() expectedConfig = expectedConfig.copy(avatarUri = uriFromGallery) assertThat(newState.config).isEqualTo(expectedConfig) // From camera - val uriFromCamera = Uri.parse(AN_URI_FROM_CAMERA) - pickerProvider.givenResult(uriFromCamera) + val uriFromCamera = AN_URI_FROM_CAMERA + pickerProvider.givenResult(uriFromCamera.toUri()) assertThat(newState.cameraPermissionState.permissionGranted).isFalse() newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto)) newState = awaitItem() @@ -175,8 +176,8 @@ class ConfigureRoomPresenterTest { expectedConfig = expectedConfig.copy(avatarUri = uriFromCamera) assertThat(newState.config).isEqualTo(expectedConfig) // Do it again, no permission is requested - val uriFromCamera2 = Uri.parse(AN_URI_FROM_CAMERA_2) - pickerProvider.givenResult(uriFromCamera2) + val uriFromCamera2 = AN_URI_FROM_CAMERA_2 + pickerProvider.givenResult(uriFromCamera2.toUri()) newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto)) newState = awaitItem() expectedConfig = expectedConfig.copy(avatarUri = uriFromCamera2) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt index 0cd0144986..c894aae05d 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt @@ -62,21 +62,21 @@ class EditUserProfilePresenter( @Composable override fun present(): EditUserProfileState { val cameraPermissionState = cameraPermissionPresenter.present() - var userAvatarUri by rememberSaveable { mutableStateOf(matrixUser.avatarUrl?.toUri()) } + var userAvatarUri by rememberSaveable { mutableStateOf(matrixUser.avatarUrl) } var userDisplayName by rememberSaveable { mutableStateOf(matrixUser.displayName) } val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker( onResult = { uri -> if (uri != null) { - temporaryUriDeleter.delete(userAvatarUri) - userAvatarUri = uri + temporaryUriDeleter.delete(userAvatarUri?.toUri()) + userAvatarUri = uri.toString() } } ) val galleryImagePicker = mediaPickerProvider.registerGalleryImagePicker( onResult = { uri -> if (uri != null) { - temporaryUriDeleter.delete(userAvatarUri) - userAvatarUri = uri + temporaryUriDeleter.delete(userAvatarUri?.toUri()) + userAvatarUri = uri.toString() } } ) @@ -102,7 +102,12 @@ class EditUserProfilePresenter( val localCoroutineScope = rememberCoroutineScope() fun handleEvents(event: EditUserProfileEvents) { when (event) { - is EditUserProfileEvents.Save -> localCoroutineScope.saveChanges(userDisplayName, userAvatarUri, matrixUser, saveAction) + is EditUserProfileEvents.Save -> localCoroutineScope.saveChanges( + name = userDisplayName, + avatarUri = userAvatarUri?.toUri(), + currentUser = matrixUser, + action = saveAction, + ) is EditUserProfileEvents.HandleAvatarAction -> { when (event.action) { AvatarAction.ChoosePhoto -> galleryImagePicker.launch() @@ -113,7 +118,7 @@ class EditUserProfilePresenter( cameraPermissionState.eventSink(PermissionsEvents.RequestPermissions) } AvatarAction.Remove -> { - temporaryUriDeleter.delete(userAvatarUri) + temporaryUriDeleter.delete(userAvatarUri?.toUri()) userAvatarUri = null } } @@ -145,9 +150,8 @@ class EditUserProfilePresenter( private fun hasDisplayNameChanged(name: String?, currentUser: MatrixUser) = name?.trim() != currentUser.displayName?.trim() - private fun hasAvatarUrlChanged(avatarUri: Uri?, currentUser: MatrixUser) = - // Need to call `toUri()?.toString()` to make the test pass (we mockk Uri) - avatarUri?.toString()?.trim() != currentUser.avatarUrl?.toUri()?.toString()?.trim() + private fun hasAvatarUrlChanged(avatarUri: String?, currentUser: MatrixUser) = + avatarUri?.trim() != currentUser.avatarUrl?.trim() private fun CoroutineScope.saveChanges( name: String?, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileState.kt index a2d5d7a653..c2bdf11a63 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileState.kt @@ -7,20 +7,16 @@ package io.element.android.features.preferences.impl.user.editprofile -import android.net.Uri -import androidx.compose.runtime.Immutable import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.media.AvatarAction import io.element.android.libraries.permissions.api.PermissionsState import kotlinx.collections.immutable.ImmutableList -// Annotate with @Immutable since `Uri` is unstable -@Immutable data class EditUserProfileState( val userId: UserId, val displayName: String, - val userAvatarUrl: Uri?, + val userAvatarUrl: String?, val avatarActions: ImmutableList, val saveButtonEnabled: Boolean, val saveAction: AsyncAction, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileStateProvider.kt index 0e4c1b4f2d..6af3c185c2 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileStateProvider.kt @@ -7,9 +7,7 @@ package io.element.android.features.preferences.impl.user.editprofile -import android.net.Uri import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import androidx.core.net.toUri import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.permissions.api.aPermissionsState @@ -19,13 +17,13 @@ open class EditUserProfileStateProvider : PreviewParameterProvider get() = sequenceOf( aEditUserProfileState(), - aEditUserProfileState(userAvatarUrl = "example://uri".toUri()), + aEditUserProfileState(userAvatarUrl = "example://uri"), // Add other states here ) } fun aEditUserProfileState( - userAvatarUrl: Uri? = null, + userAvatarUrl: String? = null, ) = EditUserProfileState( userId = UserId("@john.doe:matrix.org"), displayName = "John Doe", 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 947be54785..237951af19 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 @@ -66,7 +66,9 @@ class EditUserProfilePresenterTest { mockkStatic(Uri::class) every { Uri.parse(AN_AVATAR_URL) } returns userAvatarUri + every { userAvatarUri.toString() } returns AN_AVATAR_URL every { Uri.parse(ANOTHER_AVATAR_URL) } returns anotherAvatarUri + every { anotherAvatarUri.toString() } returns ANOTHER_AVATAR_URL } @After @@ -102,7 +104,7 @@ class EditUserProfilePresenterTest { val initialState = awaitItem() assertThat(initialState.userId).isEqualTo(user.userId) assertThat(initialState.displayName).isEqualTo(user.displayName) - assertThat(initialState.userAvatarUrl).isEqualTo(userAvatarUri) + assertThat(initialState.userAvatarUrl).isEqualTo(AN_AVATAR_URL) assertThat(initialState.avatarActions).containsExactly( AvatarAction.ChoosePhoto, AvatarAction.TakePhoto, @@ -127,16 +129,16 @@ class EditUserProfilePresenterTest { }.test { val initialState = awaitItem() assertThat(initialState.displayName).isEqualTo("Name") - assertThat(initialState.userAvatarUrl).isEqualTo(userAvatarUri) + assertThat(initialState.userAvatarUrl).isEqualTo(AN_AVATAR_URL) initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name II")) awaitItem().apply { assertThat(displayName).isEqualTo("Name II") - assertThat(userAvatarUrl).isEqualTo(userAvatarUri) + assertThat(userAvatarUrl).isEqualTo(AN_AVATAR_URL) } initialState.eventSink(EditUserProfileEvents.UpdateDisplayName("Name III")) awaitItem().apply { assertThat(displayName).isEqualTo("Name III") - assertThat(userAvatarUrl).isEqualTo(userAvatarUri) + assertThat(userAvatarUrl).isEqualTo(AN_AVATAR_URL) } initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.Remove)) awaitItem().apply { @@ -160,10 +162,10 @@ class EditUserProfilePresenterTest { presenter.present() }.test { val initialState = awaitItem() - assertThat(initialState.userAvatarUrl).isEqualTo(userAvatarUri) + assertThat(initialState.userAvatarUrl).isEqualTo(AN_AVATAR_URL) initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) awaitItem().apply { - assertThat(userAvatarUrl).isEqualTo(anotherAvatarUri) + assertThat(userAvatarUrl).isEqualTo(ANOTHER_AVATAR_URL) } } } @@ -185,7 +187,7 @@ class EditUserProfilePresenterTest { presenter.present() }.test { val initialState = awaitItem() - assertThat(initialState.userAvatarUrl).isEqualTo(userAvatarUri) + assertThat(initialState.userAvatarUrl).isEqualTo(AN_AVATAR_URL) assertThat(initialState.cameraPermissionState.permissionGranted).isFalse() initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.TakePhoto)) val stateWithAskingPermission = awaitItem() @@ -194,12 +196,12 @@ class EditUserProfilePresenterTest { val stateWithPermission = awaitItem() assertThat(stateWithPermission.cameraPermissionState.permissionGranted).isTrue() val stateWithNewAvatar = awaitItem() - assertThat(stateWithNewAvatar.userAvatarUrl).isEqualTo(anotherAvatarUri) + assertThat(stateWithNewAvatar.userAvatarUrl).isEqualTo(ANOTHER_AVATAR_URL) // Do it again, no permission is requested fakePickerProvider.givenResult(userAvatarUri) stateWithNewAvatar.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.TakePhoto)) val stateWithNewAvatar2 = awaitItem() - assertThat(stateWithNewAvatar2.userAvatarUrl).isEqualTo(userAvatarUri) + assertThat(stateWithNewAvatar2.userAvatarUrl).isEqualTo(AN_AVATAR_URL) deleteCallback.assertions().isCalledExactly(2).withSequence( listOf(value(userAvatarUri)), listOf(value(anotherAvatarUri)), diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt index 2520d0d29c..6e318d5f45 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt @@ -61,11 +61,11 @@ class RoomDetailsEditPresenter( val cameraPermissionState = cameraPermissionPresenter.present() val roomSyncUpdateFlow = room.syncUpdateFlow.collectAsState() - val roomAvatarUri = room.avatarUrl()?.toUri() - var roomAvatarUriEdited by rememberSaveable { mutableStateOf(null) } + val roomAvatarUri = room.avatarUrl() + var roomAvatarUriEdited by rememberSaveable { mutableStateOf(null) } LaunchedEffect(roomAvatarUri) { // Every time the roomAvatar change (from sync), we can set the new avatar. - temporaryUriDeleter.delete(roomAvatarUriEdited) + temporaryUriDeleter.delete(roomAvatarUriEdited?.toUri()) roomAvatarUriEdited = roomAvatarUri } @@ -107,16 +107,16 @@ class RoomDetailsEditPresenter( val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker( onResult = { uri -> if (uri != null) { - temporaryUriDeleter.delete(roomAvatarUriEdited) - roomAvatarUriEdited = uri + temporaryUriDeleter.delete(roomAvatarUriEdited?.toUri()) + roomAvatarUriEdited = uri.toString() } } ) val galleryImagePicker = mediaPickerProvider.registerGalleryImagePicker( onResult = { uri -> if (uri != null) { - temporaryUriDeleter.delete(roomAvatarUriEdited) - roomAvatarUriEdited = uri + temporaryUriDeleter.delete(roomAvatarUriEdited?.toUri()) + roomAvatarUriEdited = uri.toString() } } ) @@ -147,8 +147,8 @@ class RoomDetailsEditPresenter( newNameTrimmed = roomRawNameEdited.trim(), currentTopicTrimmed = roomTopicTrimmed, newTopicTrimmed = roomTopicEdited.trim(), - currentAvatar = roomAvatarUri, - newAvatarUri = roomAvatarUriEdited, + currentAvatar = roomAvatarUri?.toUri(), + newAvatarUri = roomAvatarUriEdited?.toUri(), action = saveAction, ) is RoomDetailsEditEvents.HandleAvatarAction -> { @@ -161,7 +161,7 @@ class RoomDetailsEditPresenter( cameraPermissionState.eventSink(PermissionsEvents.RequestPermissions) } AvatarAction.Remove -> { - temporaryUriDeleter.delete(roomAvatarUriEdited) + temporaryUriDeleter.delete(roomAvatarUriEdited?.toUri()) roomAvatarUriEdited = null } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditState.kt index d9a51e8e06..6f44190461 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditState.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditState.kt @@ -7,16 +7,12 @@ package io.element.android.features.roomdetails.impl.edit -import android.net.Uri -import androidx.compose.runtime.Immutable import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.ui.media.AvatarAction import io.element.android.libraries.permissions.api.PermissionsState import kotlinx.collections.immutable.ImmutableList -// Annotate with @Immutable since `Uri` is unstable -@Immutable data class RoomDetailsEditState( val roomId: RoomId, /** The raw room name (i.e. the room name from the state event `m.room.name`), not the display name. */ @@ -24,7 +20,7 @@ data class RoomDetailsEditState( val canChangeName: Boolean, val roomTopic: String, val canChangeTopic: Boolean, - val roomAvatarUrl: Uri?, + val roomAvatarUrl: String?, val canChangeAvatar: Boolean, val avatarActions: ImmutableList, val saveButtonEnabled: Boolean, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditStateProvider.kt index 7ae59febc0..f1dcc2463d 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditStateProvider.kt @@ -7,9 +7,7 @@ package io.element.android.features.roomdetails.impl.edit -import android.net.Uri import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import androidx.core.net.toUri import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.ui.media.AvatarAction @@ -23,7 +21,7 @@ open class RoomDetailsEditStateProvider : PreviewParameterProvider = emptyList(), saveButtonEnabled: Boolean = true, diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenterTest.kt index 996cbcfece..ab80113870 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenterTest.kt @@ -66,7 +66,9 @@ class RoomDetailsEditPresenterTest { mockkStatic(Uri::class) every { Uri.parse(AN_AVATAR_URL) } returns roomAvatarUri + every { roomAvatarUri.toString() } returns AN_AVATAR_URL every { Uri.parse(ANOTHER_AVATAR_URL) } returns anotherAvatarUri + every { anotherAvatarUri.toString() } returns ANOTHER_AVATAR_URL } @After @@ -107,7 +109,7 @@ class RoomDetailsEditPresenterTest { val initialState = awaitFirstItem() assertThat(initialState.roomId).isEqualTo(room.roomId) assertThat(initialState.roomRawName).isEqualTo(A_ROOM_RAW_NAME) - assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri) + assertThat(initialState.roomAvatarUrl).isEqualTo(AN_AVATAR_URL) assertThat(initialState.roomTopic).isEqualTo(room.info().topic.orEmpty()) assertThat(initialState.avatarActions).containsExactly( AvatarAction.ChoosePhoto, @@ -233,24 +235,24 @@ class RoomDetailsEditPresenterTest { val initialState = awaitFirstItem() assertThat(initialState.roomTopic).isEqualTo("My topic") assertThat(initialState.roomRawName).isEqualTo("Name") - assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri) + assertThat(initialState.roomAvatarUrl).isEqualTo(AN_AVATAR_URL) initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("Name II")) awaitItem().apply { assertThat(roomTopic).isEqualTo("My topic") assertThat(roomRawName).isEqualTo("Name II") - assertThat(roomAvatarUrl).isEqualTo(roomAvatarUri) + assertThat(roomAvatarUrl).isEqualTo(AN_AVATAR_URL) } initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("Name III")) awaitItem().apply { assertThat(roomTopic).isEqualTo("My topic") assertThat(roomRawName).isEqualTo("Name III") - assertThat(roomAvatarUrl).isEqualTo(roomAvatarUri) + assertThat(roomAvatarUrl).isEqualTo(AN_AVATAR_URL) } initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("Another topic")) awaitItem().apply { assertThat(roomTopic).isEqualTo("Another topic") assertThat(roomRawName).isEqualTo("Name III") - assertThat(roomAvatarUrl).isEqualTo(roomAvatarUri) + assertThat(roomAvatarUrl).isEqualTo(AN_AVATAR_URL) } initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove)) awaitItem().apply { @@ -277,10 +279,10 @@ class RoomDetailsEditPresenterTest { ) presenter.test { val initialState = awaitFirstItem() - assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri) + assertThat(initialState.roomAvatarUrl).isEqualTo(AN_AVATAR_URL) initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) awaitItem().apply { - assertThat(roomAvatarUrl).isEqualTo(anotherAvatarUri) + assertThat(roomAvatarUrl).isEqualTo(anotherAvatarUri.toString()) } } } @@ -303,7 +305,7 @@ class RoomDetailsEditPresenterTest { ) presenter.test { val initialState = awaitFirstItem() - assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri) + assertThat(initialState.roomAvatarUrl).isEqualTo(AN_AVATAR_URL) assertThat(initialState.cameraPermissionState.permissionGranted).isFalse() initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.TakePhoto)) val stateWithAskingPermission = awaitItem() @@ -312,12 +314,12 @@ class RoomDetailsEditPresenterTest { val stateWithPermission = awaitItem() assertThat(stateWithPermission.cameraPermissionState.permissionGranted).isTrue() val stateWithNewAvatar = awaitItem() - assertThat(stateWithNewAvatar.roomAvatarUrl).isEqualTo(anotherAvatarUri) + assertThat(stateWithNewAvatar.roomAvatarUrl).isEqualTo(anotherAvatarUri.toString()) // Do it again, no permission is requested fakePickerProvider.givenResult(roomAvatarUri) stateWithNewAvatar.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.TakePhoto)) val stateWithNewAvatar2 = awaitItem() - assertThat(stateWithNewAvatar2.roomAvatarUrl).isEqualTo(roomAvatarUri) + assertThat(stateWithNewAvatar2.roomAvatarUrl).isEqualTo(AN_AVATAR_URL) deleteCallback.assertions().isCalledExactly(3).withSequence( listOf(value(null)), listOf(value(roomAvatarUri)), diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt index ad06985ab7..156b488c60 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt @@ -7,7 +7,6 @@ package io.element.android.libraries.matrix.ui.components -import android.net.Uri import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource @@ -28,7 +27,6 @@ import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp -import androidx.core.net.toUri import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.avatar.Avatar @@ -47,7 +45,7 @@ import io.element.android.libraries.ui.strings.CommonStrings fun EditableAvatarView( matrixId: String, displayName: String?, - avatarUrl: Uri?, + avatarUrl: String?, avatarSize: AvatarSize, avatarType: AvatarType, onAvatarClick: () -> Unit, @@ -71,13 +69,13 @@ fun EditableAvatarView( contentDescription = a11yAvatar }, ) { - when (avatarUrl?.scheme) { - null, "mxc" -> { + when { + avatarUrl == null || avatarUrl.startsWith("mxc://") -> { Avatar( avatarData = AvatarData( id = matrixId, name = displayName, - url = avatarUrl?.toString(), + url = avatarUrl, size = avatarSize, ), avatarType = avatarType, @@ -114,7 +112,7 @@ fun EditableAvatarView( @PreviewsDayNight @Composable internal fun EditableAvatarViewPreview( - @PreviewParameter(EditableAvatarViewUriProvider::class) uri: Uri? + @PreviewParameter(EditableAvatarViewUriProvider::class) uri: String? ) = ElementPreview( drawableFallbackForImages = CommonDrawables.sample_avatar, ) { @@ -128,11 +126,11 @@ internal fun EditableAvatarViewPreview( ) } -open class EditableAvatarViewUriProvider : PreviewParameterProvider { - override val values: Sequence +open class EditableAvatarViewUriProvider : PreviewParameterProvider { + override val values: Sequence get() = sequenceOf( null, - "mxc://matrix.org/123456".toUri(), - "https://example.com/avatar.jpg".toUri(), + "mxc://matrix.org/123456", + "https://example.com/avatar.jpg", ) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt index 1e041a69fe..58dba0183c 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt @@ -7,7 +7,6 @@ package io.element.android.libraries.matrix.ui.components -import android.net.Uri import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -43,7 +42,7 @@ import io.element.android.libraries.designsystem.theme.temporaryColorBgSpecial */ @Composable fun UnsavedAvatar( - avatarUri: Uri?, + avatarUri: String?, avatarSize: AvatarSize, avatarType: AvatarType, modifier: Modifier = Modifier, @@ -86,8 +85,8 @@ internal fun UnsavedAvatarPreview() = ElementPreview { horizontalArrangement = Arrangement.spacedBy(8.dp), ) { UnsavedAvatar(null, AvatarSize.EditRoomDetails, AvatarType.User) - UnsavedAvatar(Uri.EMPTY, AvatarSize.EditRoomDetails, AvatarType.User) + UnsavedAvatar("", AvatarSize.EditRoomDetails, AvatarType.User) UnsavedAvatar(null, AvatarSize.EditRoomDetails, AvatarType.Space()) - UnsavedAvatar(Uri.EMPTY, AvatarSize.EditRoomDetails, AvatarType.Space()) + UnsavedAvatar("", AvatarSize.EditRoomDetails, AvatarType.Space()) } }