Merge pull request #5508 from element-hq/feature/bma/noUriInComposeState

Replace Uri by String in States that are used in Composable function.
This commit is contained in:
Benoit Marty
2025-10-13 15:32:39 +02:00
committed by GitHub
16 changed files with 78 additions and 87 deletions

View File

@@ -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<AsyncAction<RoomId>>
) = 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,

View File

@@ -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,

View File

@@ -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<MatrixUser> = persistentListOf(),
val roomVisibility: RoomVisibilityState = RoomVisibilityState.Private,
)

View File

@@ -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())
}
}

View File

@@ -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)

View File

@@ -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?,

View File

@@ -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<AvatarAction>,
val saveButtonEnabled: Boolean,
val saveAction: AsyncAction<Unit>,

View File

@@ -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<EditUserProfi
override val values: Sequence<EditUserProfileState>
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",

View File

@@ -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)),

View File

@@ -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<Uri?>(null) }
val roomAvatarUri = room.avatarUrl()
var roomAvatarUriEdited by rememberSaveable { mutableStateOf<String?>(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
}
}

View File

@@ -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<AvatarAction>,
val saveButtonEnabled: Boolean,

View File

@@ -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<RoomDetailsEd
aRoomDetailsEditState(),
aRoomDetailsEditState(roomTopic = ""),
aRoomDetailsEditState(roomRawName = ""),
aRoomDetailsEditState(roomAvatarUrl = "example://uri".toUri()),
aRoomDetailsEditState(roomAvatarUrl = "example://uri"),
aRoomDetailsEditState(canChangeName = true, canChangeTopic = false, canChangeAvatar = true, saveButtonEnabled = false),
aRoomDetailsEditState(canChangeName = false, canChangeTopic = true, canChangeAvatar = false, saveButtonEnabled = false),
aRoomDetailsEditState(saveAction = AsyncAction.Loading),
@@ -37,7 +35,7 @@ fun aRoomDetailsEditState(
canChangeName: Boolean = true,
roomTopic: String = "a room topic that is quite long so should wrap onto multiple lines",
canChangeTopic: Boolean = true,
roomAvatarUrl: Uri? = null,
roomAvatarUrl: String? = null,
canChangeAvatar: Boolean = true,
avatarActions: List<AvatarAction> = emptyList(),
saveButtonEnabled: Boolean = true,

View File

@@ -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)),

View File

@@ -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<Uri?> {
override val values: Sequence<Uri?>
open class EditableAvatarViewUriProvider : PreviewParameterProvider<String?> {
override val values: Sequence<String?>
get() = sequenceOf(
null,
"mxc://matrix.org/123456".toUri(),
"https://example.com/avatar.jpg".toUri(),
"mxc://matrix.org/123456",
"https://example.com/avatar.jpg",
)
}

View File

@@ -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())
}
}

View File

@@ -62,6 +62,7 @@ class KonsistClassNameTest {
.withAllParentsOf(PreviewParameterProvider::class)
.withoutName(
"AspectRatioProvider",
"EditableAvatarViewUriProvider",
"LoginModeViewErrorProvider",
"OverlapRatioProvider",
"TextFileContentProvider",