Browse Source

upload avatar within the room creation

feature/jme/open-room-member-details-when-clicking-on-user-data
Florian Renaud 1 year ago
parent
commit
27f6f5cd3b
  1. 1
      features/createroom/impl/build.gradle.kts
  2. 21
      features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt
  3. 4
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt
  4. 21
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
  5. 12
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt

1
features/createroom/impl/build.gradle.kts

@ -47,6 +47,7 @@ dependencies { @@ -47,6 +47,7 @@ dependencies {
implementation(projects.libraries.uiStrings)
implementation(projects.features.userlist.api)
implementation(projects.libraries.mediapickers.api)
implementation(projects.libraries.mediaupload.api)
implementation(libs.coil.compose)
api(projects.features.createroom.api)

21
features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
package io.element.android.features.createroom.impl.configureroom
import android.net.Uri
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
@ -30,12 +31,16 @@ import io.element.android.features.createroom.impl.configureroom.avatar.AvatarAc @@ -30,12 +31,16 @@ import io.element.android.features.createroom.impl.configureroom.avatar.AvatarAc
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.execute
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters
import io.element.android.libraries.matrix.api.createroom.RoomPreset
import io.element.android.libraries.matrix.api.createroom.RoomVisibility
import io.element.android.libraries.mediapickers.api.PickerProvider
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
import io.element.android.libraries.mediaupload.api.MediaType
import io.element.android.libraries.mediaupload.api.MediaUploadInfo
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@ -45,6 +50,7 @@ class ConfigureRoomPresenter @Inject constructor( @@ -45,6 +50,7 @@ class ConfigureRoomPresenter @Inject constructor(
private val dataStore: CreateRoomDataStore,
private val matrixClient: MatrixClient,
private val mediaPickerProvider: PickerProvider,
private val mediaPreProcessor: MediaPreProcessor,
) : Presenter<ConfigureRoomState> {
@Composable
@ -112,9 +118,12 @@ class ConfigureRoomPresenter @Inject constructor( @@ -112,9 +118,12 @@ class ConfigureRoomPresenter @Inject constructor(
)
}
private fun CoroutineScope.createRoom(config: CreateRoomConfig, createRoomAction: MutableState<Async<RoomId>>) = launch {
private fun CoroutineScope.createRoom(
config: CreateRoomConfig,
createRoomAction: MutableState<Async<RoomId>>
) = launch {
val mxc = config.avatarUri?.let { uploadAvatar(it) }
suspend {
// TODO pre-process and upload the avatar before creating the room
val params = CreateRoomParameters(
name = config.roomName,
topic = config.topic,
@ -123,9 +132,15 @@ class ConfigureRoomPresenter @Inject constructor( @@ -123,9 +132,15 @@ class ConfigureRoomPresenter @Inject constructor(
visibility = if (config.privacy == RoomPrivacy.Public) RoomVisibility.PUBLIC else RoomVisibility.PRIVATE,
preset = if (config.privacy == RoomPrivacy.Public) RoomPreset.PUBLIC_CHAT else RoomPreset.PRIVATE_CHAT,
invite = config.invites.map { it.userId },
avatar = config.avatarUri?.toString(),
avatar = mxc,
)
matrixClient.createRoom(params).getOrThrow()
}.execute(createRoomAction)
}
private suspend fun uploadAvatar(avatarUri: Uri): String? {
val preprocessed = mediaPreProcessor.process(avatarUri, MediaType.Image).getOrThrow() as? MediaUploadInfo.Image
val byteArray = preprocessed?.file?.readBytes()
return byteArray?.let { matrixClient.uploadMedia(MimeTypes.Jpeg, it) }?.getOrThrow()
}
}

4
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt

@ -42,6 +42,7 @@ interface MatrixClient : Closeable { @@ -42,6 +42,7 @@ interface MatrixClient : Closeable {
suspend fun createRoom(createRoomParams: CreateRoomParameters): Result<RoomId>
suspend fun createDM(userId: UserId): Result<RoomId>
suspend fun getProfile(userId: UserId): Result<MatrixUser>
suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults>
fun startSync()
fun stopSync()
fun mediaResolver(): MediaResolver
@ -58,9 +59,10 @@ interface MatrixClient : Closeable { @@ -58,9 +59,10 @@ interface MatrixClient : Closeable {
height: Long
): Result<ByteArray>
suspend fun uploadMedia(mimeType: String, data: ByteArray): Result<String>
fun onSlidingSyncUpdate()
fun roomMembershipObserver(): RoomMembershipObserver
suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults>
}

21
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt

@ -280,6 +280,13 @@ class RustMatrixClient constructor( @@ -280,6 +280,13 @@ class RustMatrixClient constructor(
}
}
override suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults> =
withContext(dispatchers.io) {
runCatching {
client.searchUsers(searchTerm, limit.toULong()).let(UserSearchResultMapper::map)
}
}
override fun mediaResolver(): MediaResolver = mediaResolver
override fun sessionVerificationService(): SessionVerificationService = verificationService
@ -368,6 +375,13 @@ class RustMatrixClient constructor( @@ -368,6 +375,13 @@ class RustMatrixClient constructor(
}
}
@OptIn(ExperimentalUnsignedTypes::class)
override suspend fun uploadMedia(mimeType: String, data: ByteArray): Result<String> = withContext(dispatchers.io) {
runCatching {
client.uploadMedia(mimeType, data.toUByteArray().toList())
}
}
override fun onSlidingSyncUpdate() {
if (!verificationService.isReady.value) {
try {
@ -380,13 +394,6 @@ class RustMatrixClient constructor( @@ -380,13 +394,6 @@ class RustMatrixClient constructor(
override fun roomMembershipObserver(): RoomMembershipObserver = roomMembershipObserver
override suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults> =
withContext(dispatchers.io) {
runCatching {
client.searchUsers(searchTerm, limit.toULong()).let(UserSearchResultMapper::map)
}
}
private fun File.deleteSessionDirectory(userID: String): Boolean {
// Rust sanitises the user ID replacing invalid characters with an _
val sanitisedUserID = userID.replace(":", "_")

12
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt

@ -91,6 +91,10 @@ class FakeMatrixClient( @@ -91,6 +91,10 @@ class FakeMatrixClient(
return getProfileResults[userId] ?: Result.failure(IllegalStateException("No profile found for $userId"))
}
override suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults> {
return searchUserResults[searchTerm] ?: Result.failure(IllegalStateException("No response defined for $searchTerm"))
}
override fun startSync() = Unit
override fun stopSync() = Unit
@ -122,6 +126,10 @@ class FakeMatrixClient( @@ -122,6 +126,10 @@ class FakeMatrixClient(
return Result.success(ByteArray(0))
}
override suspend fun uploadMedia(mimeType: String, data: ByteArray): Result<String> {
return Result.success("")
}
override fun sessionVerificationService(): SessionVerificationService = sessionVerificationService
override fun pushersService(): PushersService = pushersService
@ -134,10 +142,6 @@ class FakeMatrixClient( @@ -134,10 +142,6 @@ class FakeMatrixClient(
return RoomMembershipObserver()
}
override suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults> {
return searchUserResults[searchTerm] ?: Result.failure(IllegalStateException("No response defined for $searchTerm"))
}
// Mocks
fun givenLogoutError(failure: Throwable?) {

Loading…
Cancel
Save