upload avatar within the room creation
This commit is contained in:
parent
4a444c24c4
commit
5a0665a27a
5 changed files with 44 additions and 15 deletions
|
|
@ -47,6 +47,7 @@ dependencies {
|
||||||
implementation(projects.libraries.uiStrings)
|
implementation(projects.libraries.uiStrings)
|
||||||
implementation(projects.features.userlist.api)
|
implementation(projects.features.userlist.api)
|
||||||
implementation(projects.libraries.mediapickers.api)
|
implementation(projects.libraries.mediapickers.api)
|
||||||
|
implementation(projects.libraries.mediaupload.api)
|
||||||
implementation(libs.coil.compose)
|
implementation(libs.coil.compose)
|
||||||
api(projects.features.createroom.api)
|
api(projects.features.createroom.api)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package io.element.android.features.createroom.impl.configureroom
|
package io.element.android.features.createroom.impl.configureroom
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
|
|
@ -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.Async
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.execute
|
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.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
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.CreateRoomParameters
|
||||||
import io.element.android.libraries.matrix.api.createroom.RoomPreset
|
import io.element.android.libraries.matrix.api.createroom.RoomPreset
|
||||||
import io.element.android.libraries.matrix.api.createroom.RoomVisibility
|
import io.element.android.libraries.matrix.api.createroom.RoomVisibility
|
||||||
import io.element.android.libraries.mediapickers.api.PickerProvider
|
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.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -45,6 +50,7 @@ class ConfigureRoomPresenter @Inject constructor(
|
||||||
private val dataStore: CreateRoomDataStore,
|
private val dataStore: CreateRoomDataStore,
|
||||||
private val matrixClient: MatrixClient,
|
private val matrixClient: MatrixClient,
|
||||||
private val mediaPickerProvider: PickerProvider,
|
private val mediaPickerProvider: PickerProvider,
|
||||||
|
private val mediaPreProcessor: MediaPreProcessor,
|
||||||
) : Presenter<ConfigureRoomState> {
|
) : Presenter<ConfigureRoomState> {
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -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 {
|
suspend {
|
||||||
// TODO pre-process and upload the avatar before creating the room
|
|
||||||
val params = CreateRoomParameters(
|
val params = CreateRoomParameters(
|
||||||
name = config.roomName,
|
name = config.roomName,
|
||||||
topic = config.topic,
|
topic = config.topic,
|
||||||
|
|
@ -123,9 +132,15 @@ class ConfigureRoomPresenter @Inject constructor(
|
||||||
visibility = if (config.privacy == RoomPrivacy.Public) RoomVisibility.PUBLIC else RoomVisibility.PRIVATE,
|
visibility = if (config.privacy == RoomPrivacy.Public) RoomVisibility.PUBLIC else RoomVisibility.PRIVATE,
|
||||||
preset = if (config.privacy == RoomPrivacy.Public) RoomPreset.PUBLIC_CHAT else RoomPreset.PRIVATE_CHAT,
|
preset = if (config.privacy == RoomPrivacy.Public) RoomPreset.PUBLIC_CHAT else RoomPreset.PRIVATE_CHAT,
|
||||||
invite = config.invites.map { it.userId },
|
invite = config.invites.map { it.userId },
|
||||||
avatar = config.avatarUri?.toString(),
|
avatar = mxc,
|
||||||
)
|
)
|
||||||
matrixClient.createRoom(params).getOrThrow()
|
matrixClient.createRoom(params).getOrThrow()
|
||||||
}.execute(createRoomAction)
|
}.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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ interface MatrixClient : Closeable {
|
||||||
suspend fun createRoom(createRoomParams: CreateRoomParameters): Result<RoomId>
|
suspend fun createRoom(createRoomParams: CreateRoomParameters): Result<RoomId>
|
||||||
suspend fun createDM(userId: UserId): Result<RoomId>
|
suspend fun createDM(userId: UserId): Result<RoomId>
|
||||||
suspend fun getProfile(userId: UserId): Result<MatrixUser>
|
suspend fun getProfile(userId: UserId): Result<MatrixUser>
|
||||||
|
suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults>
|
||||||
fun startSync()
|
fun startSync()
|
||||||
fun stopSync()
|
fun stopSync()
|
||||||
fun mediaResolver(): MediaResolver
|
fun mediaResolver(): MediaResolver
|
||||||
|
|
@ -58,9 +59,10 @@ interface MatrixClient : Closeable {
|
||||||
height: Long
|
height: Long
|
||||||
): Result<ByteArray>
|
): Result<ByteArray>
|
||||||
|
|
||||||
|
suspend fun uploadMedia(mimeType: String, data: ByteArray): Result<String>
|
||||||
|
|
||||||
fun onSlidingSyncUpdate()
|
fun onSlidingSyncUpdate()
|
||||||
|
|
||||||
fun roomMembershipObserver(): RoomMembershipObserver
|
fun roomMembershipObserver(): RoomMembershipObserver
|
||||||
|
|
||||||
suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults>
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 mediaResolver(): MediaResolver = mediaResolver
|
||||||
|
|
||||||
override fun sessionVerificationService(): SessionVerificationService = verificationService
|
override fun sessionVerificationService(): SessionVerificationService = verificationService
|
||||||
|
|
@ -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() {
|
override fun onSlidingSyncUpdate() {
|
||||||
if (!verificationService.isReady.value) {
|
if (!verificationService.isReady.value) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -380,13 +394,6 @@ class RustMatrixClient constructor(
|
||||||
|
|
||||||
override fun roomMembershipObserver(): RoomMembershipObserver = roomMembershipObserver
|
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 {
|
private fun File.deleteSessionDirectory(userID: String): Boolean {
|
||||||
// Rust sanitises the user ID replacing invalid characters with an _
|
// Rust sanitises the user ID replacing invalid characters with an _
|
||||||
val sanitisedUserID = userID.replace(":", "_")
|
val sanitisedUserID = userID.replace(":", "_")
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,10 @@ class FakeMatrixClient(
|
||||||
return getProfileResults[userId] ?: Result.failure(IllegalStateException("No profile found for $userId"))
|
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 startSync() = Unit
|
||||||
|
|
||||||
override fun stopSync() = Unit
|
override fun stopSync() = Unit
|
||||||
|
|
@ -122,6 +126,10 @@ class FakeMatrixClient(
|
||||||
return Result.success(ByteArray(0))
|
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 sessionVerificationService(): SessionVerificationService = sessionVerificationService
|
||||||
|
|
||||||
override fun pushersService(): PushersService = pushersService
|
override fun pushersService(): PushersService = pushersService
|
||||||
|
|
@ -134,10 +142,6 @@ class FakeMatrixClient(
|
||||||
return RoomMembershipObserver()
|
return RoomMembershipObserver()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults> {
|
|
||||||
return searchUserResults[searchTerm] ?: Result.failure(IllegalStateException("No response defined for $searchTerm"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mocks
|
// Mocks
|
||||||
|
|
||||||
fun givenLogoutError(failure: Throwable?) {
|
fun givenLogoutError(failure: Throwable?) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue