Merge pull request #5844 from element-hq/feature/fga/room_edit_details

Change : room details edit
This commit is contained in:
ganfra
2025-12-03 17:43:59 +01:00
committed by GitHub
106 changed files with 632 additions and 171 deletions

View File

@@ -59,6 +59,7 @@ dependencies {
implementation(projects.features.roommembermoderation.api)
implementation(projects.features.rolesandpermissions.api)
implementation(projects.features.securityandprivacy.api)
implementation(projects.features.roomdetailsedit.api)
implementation(projects.features.invitepeople.api)
testCommonDependencies(libs, true)
@@ -73,6 +74,7 @@ dependencies {
testImplementation(projects.features.call.test)
testImplementation(projects.features.rolesandpermissions.test)
testImplementation(projects.features.securityandprivacy.test)
testImplementation(projects.features.roomdetailsedit.test)
testImplementation(projects.features.knockrequests.test)
testImplementation(projects.features.messages.test)
testImplementation(projects.features.poll.test)

View File

@@ -35,11 +35,11 @@ import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRoles
import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType
import io.element.android.features.rolesandpermissions.api.RolesAndPermissionsEntryPoint
import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint
import io.element.android.features.roomdetails.impl.edit.RoomDetailsEditNode
import io.element.android.features.roomdetails.impl.invite.RoomInviteMembersNode
import io.element.android.features.roomdetails.impl.members.RoomMemberListNode
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsNode
import io.element.android.features.roomdetails.impl.notificationsettings.RoomNotificationSettingsNode
import io.element.android.features.roomdetailsedit.api.RoomDetailsEditEntryPoint
import io.element.android.features.securityandprivacy.api.SecurityAndPrivacyEntryPoint
import io.element.android.features.userprofile.shared.UserProfileNodeHelper
import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint
@@ -85,6 +85,7 @@ class RoomDetailsFlowNode(
private val changeRoomMemberRolesEntryPoint: ChangeRoomMemberRolesEntryPoint,
private val rolesAndPermissionsEntryPoint: RolesAndPermissionsEntryPoint,
private val securityAndPrivacyEntryPoint: SecurityAndPrivacyEntryPoint,
private val roomDetailsEditEntryPoint: RoomDetailsEditEntryPoint,
) : BaseFlowNode<RoomDetailsFlowNode.NavTarget>(
backstack = BackStack(
initialElement = plugins.filterIsInstance<RoomDetailsEntryPoint.Params>().first().initialElement.toNavTarget(),
@@ -256,7 +257,7 @@ class RoomDetailsFlowNode(
}
NavTarget.RoomDetailsEdit -> {
createNode<RoomDetailsEditNode>(buildContext)
roomDetailsEditEntryPoint.createNode(this, buildContext)
}
NavTarget.InviteMembers -> {

View File

@@ -20,6 +20,7 @@ import io.element.android.features.messages.test.FakeMessagesEntryPoint
import io.element.android.features.poll.test.history.FakePollHistoryEntryPoint
import io.element.android.features.reportroom.test.FakeReportRoomEntryPoint
import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint
import io.element.android.features.roomdetailsedit.test.FakeRoomDetailsEditEntryPoint
import io.element.android.features.securityandprivacy.test.FakeSecurityAndPrivacyEntryPoint
import io.element.android.features.verifysession.test.FakeOutgoingVerificationEntryPoint
import io.element.android.libraries.matrix.api.core.EventId
@@ -63,6 +64,7 @@ class DefaultRoomDetailsEntryPointTest {
changeRoomMemberRolesEntryPoint = FakeChangeRoomMemberRolesEntryPoint(),
rolesAndPermissionsEntryPoint = FakeRolesAndPermissionsEntryPoint(),
securityAndPrivacyEntryPoint = FakeSecurityAndPrivacyEntryPoint(),
roomDetailsEditEntryPoint = FakeRoomDetailsEditEntryPoint(),
)
}
val callback = object : RoomDetailsEntryPoint.Callback {

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2022-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
plugins {
id("io.element.android-library")
id("kotlin-parcelize")
}
android {
namespace = "io.element.android.features.roomdetailsedit.api"
}
dependencies {
implementation(projects.libraries.architecture)
implementation(projects.libraries.matrix.api)
}

View File

@@ -0,0 +1,13 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.roomdetailsedit.api
import io.element.android.libraries.architecture.SimpleFeatureEntryPoint
fun interface RoomDetailsEditEntryPoint : SimpleFeatureEntryPoint

View File

@@ -0,0 +1,57 @@
import extension.setupDependencyInjection
import extension.testCommonDependencies
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
plugins {
id("io.element.android-compose-library")
id("kotlin-parcelize")
}
android {
namespace = "io.element.android.features.roomdetailsedit.impl"
testOptions {
unitTests {
isIncludeAndroidResources = true
}
}
}
setupDependencyInjection()
dependencies {
implementation(projects.libraries.core)
implementation(projects.libraries.architecture)
implementation(projects.libraries.matrix.api)
implementation(projects.libraries.matrixui)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.uiStrings)
implementation(projects.libraries.androidutils)
implementation(projects.libraries.mediapickers.api)
implementation(projects.libraries.mediaupload.api)
implementation(projects.libraries.mediaviewer.api)
implementation(projects.libraries.featureflag.api)
implementation(projects.libraries.permissions.api)
implementation(projects.libraries.preferences.api)
implementation(projects.services.analytics.api)
implementation(projects.libraries.testtags)
api(projects.features.roomdetailsedit.api)
api(projects.services.apperror.api)
implementation(libs.coil.compose)
testCommonDependencies(libs, true)
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.libraries.mediaupload.test)
testImplementation(projects.libraries.mediapickers.test)
testImplementation(projects.libraries.mediaviewer.test)
testImplementation(projects.libraries.permissions.test)
testImplementation(projects.libraries.preferences.test)
testImplementation(projects.libraries.featureflag.test)
testImplementation(projects.services.analytics.test)
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.roomdetailsedit.impl
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.features.roomdetailsedit.api.RoomDetailsEditEntryPoint
import io.element.android.libraries.architecture.createNode
@ContributesBinding(AppScope::class)
class DefaultRoomDetailsEditEntryPoint : RoomDetailsEditEntryPoint {
override fun createNode(parentNode: Node, buildContext: BuildContext): Node {
return parentNode.createNode<RoomDetailsEditNode>(buildContext)
}
}

View File

@@ -6,7 +6,7 @@
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.roomdetails.impl.edit
package io.element.android.features.roomdetailsedit.impl
import io.element.android.libraries.matrix.ui.media.AvatarAction

View File

@@ -6,7 +6,7 @@
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.roomdetails.impl.edit
package io.element.android.features.roomdetailsedit.impl
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier

View File

@@ -6,8 +6,9 @@
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.roomdetails.impl.edit
package io.element.android.features.roomdetailsedit.impl
import android.Manifest
import android.net.Uri
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -32,9 +33,6 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.room.StateEventType
import io.element.android.libraries.matrix.api.room.powerlevels.canSendState
import io.element.android.libraries.matrix.ui.media.AvatarAction
import io.element.android.libraries.matrix.ui.room.avatarUrl
import io.element.android.libraries.matrix.ui.room.rawName
import io.element.android.libraries.matrix.ui.room.topic
import io.element.android.libraries.mediapickers.api.PickerProvider
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfigProvider
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
@@ -54,15 +52,15 @@ class RoomDetailsEditPresenter(
permissionsPresenterFactory: PermissionsPresenter.Factory,
private val mediaOptimizationConfigProvider: MediaOptimizationConfigProvider,
) : Presenter<RoomDetailsEditState> {
private val cameraPermissionPresenter = permissionsPresenterFactory.create(android.Manifest.permission.CAMERA)
private val cameraPermissionPresenter = permissionsPresenterFactory.create(Manifest.permission.CAMERA)
private var pendingPermissionRequest = false
@Composable
override fun present(): RoomDetailsEditState {
val cameraPermissionState = cameraPermissionPresenter.present()
val roomSyncUpdateFlow = room.syncUpdateFlow.collectAsState()
val roomAvatarUri = room.avatarUrl()
val roomInfo by room.roomInfoFlow.collectAsState()
val roomAvatarUri = roomInfo.avatarUrl
var roomAvatarUriEdited by rememberSaveable { mutableStateOf<String?>(null) }
LaunchedEffect(roomAvatarUri) {
// Every time the roomAvatar change (from sync), we can set the new avatar.
@@ -70,13 +68,13 @@ class RoomDetailsEditPresenter(
roomAvatarUriEdited = roomAvatarUri
}
val roomRawNameTrimmed = room.rawName().orEmpty().trim()
val roomRawNameTrimmed = roomInfo.rawName.orEmpty().trim()
var roomRawNameEdited by rememberSaveable { mutableStateOf("") }
LaunchedEffect(roomRawNameTrimmed) {
// Every time the rawName change (from sync), we can set the new name.
roomRawNameEdited = roomRawNameTrimmed
}
val roomTopicTrimmed = room.topic().orEmpty().trim()
val roomTopicTrimmed = roomInfo.topic.orEmpty().trim()
var roomTopicEdited by rememberSaveable { mutableStateOf("") }
LaunchedEffect(roomTopicTrimmed) {
// Every time the topic change (from sync), we can set the new topic.
@@ -192,6 +190,7 @@ class RoomDetailsEditPresenter(
saveButtonEnabled = saveButtonEnabled,
saveAction = saveAction.value,
cameraPermissionState = cameraPermissionState,
isSpace = roomInfo.isSpace,
eventSink = ::handleEvent,
)
}

View File

@@ -6,7 +6,7 @@
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.roomdetails.impl.edit
package io.element.android.features.roomdetailsedit.impl
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.api.core.RoomId
@@ -27,5 +27,6 @@ data class RoomDetailsEditState(
val saveButtonEnabled: Boolean,
val saveAction: AsyncAction<Unit>,
val cameraPermissionState: PermissionsState,
val isSpace: Boolean,
val eventSink: (RoomDetailsEditEvents) -> Unit
)

View File

@@ -6,7 +6,7 @@
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.roomdetails.impl.edit
package io.element.android.features.roomdetailsedit.impl
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.architecture.AsyncAction
@@ -23,6 +23,7 @@ open class RoomDetailsEditStateProvider : PreviewParameterProvider<RoomDetailsEd
aRoomDetailsEditState(roomTopic = ""),
aRoomDetailsEditState(roomRawName = ""),
aRoomDetailsEditState(roomAvatarUrl = "example://uri"),
aRoomDetailsEditState(roomAvatarUrl = "example://uri", isSpace = true, roomTopic = ""),
aRoomDetailsEditState(canChangeName = true, canChangeTopic = false, canChangeAvatar = true, saveButtonEnabled = false),
aRoomDetailsEditState(canChangeName = false, canChangeTopic = true, canChangeAvatar = false, saveButtonEnabled = false),
aRoomDetailsEditState(saveAction = AsyncAction.Loading),
@@ -43,6 +44,7 @@ fun aRoomDetailsEditState(
saveButtonEnabled: Boolean = true,
saveAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
cameraPermissionState: PermissionsState = aPermissionsState(showDialog = false),
isSpace: Boolean = false,
eventSink: (RoomDetailsEditEvents) -> Unit = {},
) = RoomDetailsEditState(
roomId = roomId,
@@ -56,5 +58,6 @@ fun aRoomDetailsEditState(
saveButtonEnabled = saveButtonEnabled,
saveAction = saveAction,
cameraPermissionState = cameraPermissionState,
isSpace = isSpace,
eventSink = eventSink,
)

View File

@@ -8,7 +8,7 @@
@file:OptIn(ExperimentalMaterial3Api::class)
package io.element.android.features.roomdetails.impl.edit
package io.element.android.features.roomdetailsedit.impl
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Column
@@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
@@ -31,7 +30,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.features.roomdetails.impl.R
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.designsystem.components.async.AsyncActionView
import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults
@@ -97,7 +95,6 @@ fun RoomDetailsEditView(
modifier = Modifier
.padding(padding)
.padding(horizontal = 16.dp)
.navigationBarsPadding()
.imePadding()
.verticalScroll(rememberScrollState())
) {
@@ -108,14 +105,18 @@ fun RoomDetailsEditView(
displayName = state.roomRawName,
avatarUrl = state.roomAvatarUrl,
avatarSize = AvatarSize.EditRoomDetails,
avatarType = AvatarType.Room(),
avatarType = if (state.isSpace) {
AvatarType.Space()
} else {
AvatarType.Room()
},
onAvatarClick = ::onAvatarClick,
modifier = Modifier.fillMaxWidth(),
)
Spacer(modifier = Modifier.height(60.dp))
Spacer(modifier = Modifier.height(32.dp))
TextField(
label = stringResource(id = R.string.screen_room_details_room_name_label),
label = stringResource(id = CommonStrings.common_name),
value = state.roomRawName,
placeholder = stringResource(CommonStrings.common_room_name_placeholder),
singleLine = true,
@@ -123,12 +124,16 @@ fun RoomDetailsEditView(
onValueChange = { state.eventSink(RoomDetailsEditEvents.UpdateRoomName(it)) },
)
Spacer(modifier = Modifier.height(28.dp))
Spacer(modifier = Modifier.height(32.dp))
TextField(
label = stringResource(CommonStrings.common_topic),
value = state.roomTopic,
placeholder = stringResource(CommonStrings.common_topic_placeholder),
placeholder = if (state.isSpace) {
stringResource(CommonStrings.common_space_topic_placeholder)
} else {
stringResource(CommonStrings.common_topic_placeholder)
},
maxLines = 10,
readOnly = !state.canChangeTopic,
onValueChange = { state.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(it)) },

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Рэдагаваць пакой"</string>
<string name="screen_room_details_edition_error">"Адбылася невядомая памылка, і інфармацыю нельга было змяніць."</string>
<string name="screen_room_details_edition_error_title">"Немагчыма абнавіць пакой"</string>
<string name="screen_room_details_updating_room">"Ідзе абнаўленне пакоя…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Редактиране на стаята"</string>
<string name="screen_room_details_edition_error">"Възникна неизвестна грешка и информацията не можа да бъде променена."</string>
<string name="screen_room_details_edition_error_title">"Не може да се обнови стаята"</string>
<string name="screen_room_details_updating_room">"Обновяване на стаята…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Upravit podrobnosti"</string>
<string name="screen_room_details_edition_error">"Došlo k neznámé chybě a informace nebylo možné změnit."</string>
<string name="screen_room_details_edition_error_title">"Nelze aktualizovat místnost"</string>
<string name="screen_room_details_updating_room">"Aktualizace místnosti…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Ystafell Golygu"</string>
<string name="screen_room_details_edition_error">"Roedd gwall anhysbys ac nid oedd modd newid y manylion."</string>
<string name="screen_room_details_edition_error_title">"Methu diweddaru\'r ystafell"</string>
<string name="screen_room_details_updating_room">"Wrthi\'n diweddaru ystafell…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Rediger rum"</string>
<string name="screen_room_details_edition_error">"Der opstod en ukendt fejl, og oplysningerne kunne ikke ændres."</string>
<string name="screen_room_details_edition_error_title">"Rummet kunne ikke opdateres"</string>
<string name="screen_room_details_updating_room">"Opdaterer rum…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Chat bearbeiten"</string>
<string name="screen_room_details_edition_error">"Es ist ein unbekannter Fehler aufgetreten und die Informationen konnten nicht geändert werden."</string>
<string name="screen_room_details_edition_error_title">"Chat kann nicht aktualisiert werden"</string>
<string name="screen_room_details_updating_room">"Chat wird aktualisiert…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Επεξεργασία Αίθουσας"</string>
<string name="screen_room_details_edition_error">"Υπήρξε ένα άγνωστο σφάλμα και οι πληροφορίες δεν μπορούσαν να αλλάξουν."</string>
<string name="screen_room_details_edition_error_title">"Αδυναμία ενημέρωσης αίθουσας"</string>
<string name="screen_room_details_updating_room">"Ενημέρωση αίθουσας…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Editar sala"</string>
<string name="screen_room_details_edition_error">"Se ha producido un error desconocido y no se ha podido cambiar la información."</string>
<string name="screen_room_details_edition_error_title">"No se puede actualizar la sala"</string>
<string name="screen_room_details_updating_room">"Actualizando la sala…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Muuda üksikasju"</string>
<string name="screen_room_details_edition_error">"Tekkis tundmatu viga ja andmed jäid muutmata."</string>
<string name="screen_room_details_edition_error_title">"Jututoa andmete muutmine ei õnnestu"</string>
<string name="screen_room_details_updating_room">"Uuendame jututuba…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Editatu gela"</string>
<string name="screen_room_details_edition_error">"Errore ezezaguna gertatu da eta ezin izan da informazioa aldatu."</string>
<string name="screen_room_details_edition_error_title">"Ezin da gela eguneratu"</string>
<string name="screen_room_details_updating_room">"Gela eguneratzen…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"ویرایش اتاق"</string>
<string name="screen_room_details_edition_error">"خطایی ناشناخته رخ داد و اطّلاعات قابل تغییر نبودند."</string>
<string name="screen_room_details_edition_error_title">"ناتوان در به‌روز رسانی اتاق"</string>
<string name="screen_room_details_updating_room">"به‌روز کردن اتاق…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Muokkaa tietoja"</string>
<string name="screen_room_details_edition_error">"Tuntematon virhe tapahtui, eikä tietoja voitu muuttaa."</string>
<string name="screen_room_details_edition_error_title">"Huoneen muokkaaminen ei onnistunut"</string>
<string name="screen_room_details_updating_room">"Muokataan huonetta…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Modifier les détails"</string>
<string name="screen_room_details_edition_error">"Une erreur inconnue sest produite et les informations nont pas pu être modifiées."</string>
<string name="screen_room_details_edition_error_title">"Impossible de mettre à jour le salon"</string>
<string name="screen_room_details_updating_room">"Mise à jour du salon…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Részletek szerkesztése"</string>
<string name="screen_room_details_edition_error">"Ismeretlen hiba történt, és az információkat nem lehetett megváltoztatni."</string>
<string name="screen_room_details_edition_error_title">"Nem sikerült frissíteni a szobát"</string>
<string name="screen_room_details_updating_room">"Szoba frissítése…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Sunting Ruangan"</string>
<string name="screen_room_details_edition_error">"Terjadi kesalahan yang tidak diketahui dan informasinya tidak dapat diubah."</string>
<string name="screen_room_details_edition_error_title">"Tidak dapat memperbarui ruangan"</string>
<string name="screen_room_details_updating_room">"Memperbarui ruangan…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Modifica dettagli"</string>
<string name="screen_room_details_edition_error">"Si è verificato un errore sconosciuto e non è stato possibile modificare le informazioni."</string>
<string name="screen_room_details_edition_error_title">"Impossibile aggiornare la stanza"</string>
<string name="screen_room_details_updating_room">"Aggiornamento della stanza…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"ოთახის რედაქტირება"</string>
<string name="screen_room_details_edition_error">"უცნობი შეცდომა მოხდა. ინფორმაციის შეცვლა ვერ მოხერხდა."</string>
<string name="screen_room_details_edition_error_title">"ოთახის განახლება შეუძლებელია"</string>
<string name="screen_room_details_updating_room">"ოთახის განახლება…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"방 편집"</string>
<string name="screen_room_details_edition_error">"알 수 없는 오류가 발생하여 정보를 변경할 수 없습니다."</string>
<string name="screen_room_details_edition_error_title">"방을 업데이트할 수 없습니다."</string>
<string name="screen_room_details_updating_room">"방 업데이트 중…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Redaguoti kambarį"</string>
<string name="screen_room_details_edition_error">"Įvyko nežinoma klaida ir informacijos pakeisti nepavyko."</string>
<string name="screen_room_details_edition_error_title">"Nepavyko atnaujinti kambario"</string>
<string name="screen_room_details_updating_room">"Atnaujinamas kambarys…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Rediger rom"</string>
<string name="screen_room_details_edition_error">"Det oppstod en ukjent feil, og informasjonen kunne ikke endres."</string>
<string name="screen_room_details_edition_error_title">"Kan ikke oppdatere rommet"</string>
<string name="screen_room_details_updating_room">"Oppdaterer rommet …"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Kamer bewerken"</string>
<string name="screen_room_details_edition_error">"Er is een onbekende fout opgetreden en de informatie kon niet worden gewijzigd."</string>
<string name="screen_room_details_edition_error_title">"Kan kamer niet bijwerken"</string>
<string name="screen_room_details_updating_room">"Kamer bijwerken…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Edytuj pokój"</string>
<string name="screen_room_details_edition_error">"Wystąpił nieznany błąd i nie można było zmienić informacji."</string>
<string name="screen_room_details_edition_error_title">"Nie można zaktualizować pokoju"</string>
<string name="screen_room_details_updating_room">"Aktualizuję pokój…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Editar detalhes"</string>
<string name="screen_room_details_edition_error">"Ocorreu um erro desconhecido e as informações não puderam ser alteradas."</string>
<string name="screen_room_details_edition_error_title">"Não foi possível atualizar a sala"</string>
<string name="screen_room_details_updating_room">"Atualizando a sala…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Editar sala"</string>
<string name="screen_room_details_edition_error">"Ocorreu um erro desconhecido e não foi possível alterar a informação."</string>
<string name="screen_room_details_edition_error_title">"Não foi possível atualizar a sala"</string>
<string name="screen_room_details_updating_room">"A atualizar sala…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Editați camera"</string>
<string name="screen_room_details_edition_error">"A apărut o eroare la actualizarea detaliilor camerei"</string>
<string name="screen_room_details_edition_error_title">"Nu s-a putut actualiza camera"</string>
<string name="screen_room_details_updating_room">"Se actualizează camera…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Редактировать комнату"</string>
<string name="screen_room_details_edition_error">"Произошла неизвестная ошибка и информацию не удалось изменить."</string>
<string name="screen_room_details_edition_error_title">"Не удалось обновить комнату"</string>
<string name="screen_room_details_updating_room">"Обновление комнаты…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Upraviť podrobnosti"</string>
<string name="screen_room_details_edition_error">"Vyskytla sa neznáma chyba a informácie nebolo možné zmeniť."</string>
<string name="screen_room_details_edition_error_title">"Nepodarilo sa aktualizovať miestnosť"</string>
<string name="screen_room_details_updating_room">"Aktualizácia miestnosti…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Redigera rummet"</string>
<string name="screen_room_details_edition_error">"Ett okänt fel uppstod och informationen kunde inte ändras."</string>
<string name="screen_room_details_edition_error_title">"Kunde inte uppdatera rummet"</string>
<string name="screen_room_details_updating_room">"Uppdaterar rummet …"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Odayı Düzenle"</string>
<string name="screen_room_details_edition_error">"Bilinmeyen bir hata oluştu ve bilgiler değiştirilemedi."</string>
<string name="screen_room_details_edition_error_title">"Oda güncellenemiyor"</string>
<string name="screen_room_details_updating_room">"Oda güncelleniyor…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Редагувати кімнату"</string>
<string name="screen_room_details_edition_error">"Сталася невідома помилка, й інформацію не вдалося змінити."</string>
<string name="screen_room_details_edition_error_title">"Не вдалося оновити кімнату"</string>
<string name="screen_room_details_updating_room">"Оновлення кімнати…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"کمرے میں ترمیم کریں"</string>
<string name="screen_room_details_edition_error">"ایک نامعلوم خلل تھا اور معلومات تبدیل نہیں ہوسکی۔"</string>
<string name="screen_room_details_edition_error_title">"کمرے کی تجدید کرنے سے قاصر"</string>
<string name="screen_room_details_updating_room">"کمرے کی تجدید کر رہا ہے…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Xonani tahrirlash"</string>
<string name="screen_room_details_edition_error">"Nomaʼlum xatolik yuz berdi va maʼlumotni oʻzgartirib boʻlmadi."</string>
<string name="screen_room_details_edition_error_title">"Xonani yangilab bolmadi"</string>
<string name="screen_room_details_updating_room">"Xona yangilanmoqda…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"編輯詳細資訊"</string>
<string name="screen_room_details_edition_error">"發生未知錯誤,無法變更資訊。"</string>
<string name="screen_room_details_edition_error_title">"無法更新聊天室"</string>
<string name="screen_room_details_updating_room">"正在更新聊天室…"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"编辑聊天室"</string>
<string name="screen_room_details_edition_error">"出现未知错误,无法更改信息。"</string>
<string name="screen_room_details_edition_error_title">"无法更新聊天室"</string>
<string name="screen_room_details_updating_room">"正在更新聊天室……"</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_details_edit_room_title">"Edit details"</string>
<string name="screen_room_details_edition_error">"There was an unknown error and the information couldn\'t be changed."</string>
<string name="screen_room_details_edition_error_title">"Unable to update room"</string>
<string name="screen_room_details_updating_room">"Updating room…"</string>
</resources>

View File

@@ -1,25 +1,28 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.roomdetails.impl.edit
package io.element.android.features.roomdetailsedit.impl
import android.net.Uri
import app.cash.turbine.ReceiveTurbine
import com.google.common.truth.Truth.assertThat
import io.element.android.features.roomdetails.impl.aJoinedRoom
import io.element.android.libraries.androidutils.file.TemporaryUriDeleter
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.room.StateEventType
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
import io.element.android.libraries.matrix.test.A_ROOM_NAME
import io.element.android.libraries.matrix.test.A_ROOM_RAW_NAME
import io.element.android.libraries.matrix.test.A_ROOM_TOPIC
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.libraries.matrix.test.room.aRoomInfo
import io.element.android.libraries.matrix.ui.media.AvatarAction
import io.element.android.libraries.mediapickers.test.FakePickerProvider
import io.element.android.libraries.mediaupload.api.MediaUploadInfo
@@ -124,8 +127,8 @@ class RoomDetailsEditPresenterTest {
@Test
fun `present - sets canChangeName if user has permission`() = runTest {
val room = aJoinedRoom(
avatarUrl = AN_AVATAR_URL,
val room = FakeJoinedRoom(
FakeBaseRoom(
canSendStateResult = { _, stateEventType ->
when (stateEventType) {
StateEventType.ROOM_NAME -> Result.success(true)
@@ -135,6 +138,7 @@ class RoomDetailsEditPresenterTest {
}
},
)
)
val deleteCallback = lambdaRecorder<Uri?, Unit> {}
val presenter = createRoomDetailsEditPresenter(
room = room,
@@ -769,6 +773,34 @@ class RoomDetailsEditPresenterTest {
)
}
private fun aJoinedRoom(
avatarUrl: String? = AN_AVATAR_URL,
displayName: String = A_ROOM_NAME,
rawName: String = displayName,
topic: String? = A_ROOM_TOPIC,
setNameResult: (String) -> Result<Unit> = { Result.success(Unit) },
setTopicResult: (String) -> Result<Unit> = { Result.success(Unit) },
updateAvatarResult: (String, ByteArray) -> Result<Unit> = { _, _ -> Result.success(Unit) },
removeAvatarResult: () -> Result<Unit> = { Result.success(Unit) },
canSendStateResult: (UserId, StateEventType) -> Result<Boolean>,
): JoinedRoom {
return FakeJoinedRoom(
baseRoom = FakeBaseRoom(
canSendStateResult = canSendStateResult,
initialRoomInfo = aRoomInfo(
name = displayName,
topic = topic,
avatarUrl = avatarUrl,
rawName = rawName
)
),
setNameResult = setNameResult,
setTopicResult = setTopicResult,
updateAvatarResult = updateAvatarResult,
removeAvatarResult = removeAvatarResult,
)
}
companion object {
private const val ANOTHER_AVATAR_URL = "example://camera/foo.jpg"
}

View File

@@ -1,12 +1,11 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.roomdetails.impl.edit
package io.element.android.features.roomdetailsedit.impl
import androidx.activity.ComponentActivity
import androidx.annotation.StringRes

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
plugins {
id("io.element.android-library")
}
android {
namespace = "io.element.android.features.roomdetetailsedit.test"
}
dependencies {
implementation(projects.features.roomdetailsedit.api)
implementation(projects.libraries.architecture)
implementation(projects.libraries.matrix.api)
implementation(projects.tests.testutils)
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.roomdetailsedit.test
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import io.element.android.features.roomdetailsedit.api.RoomDetailsEditEntryPoint
import io.element.android.tests.testutils.lambda.lambdaError
class FakeRoomDetailsEditEntryPoint : RoomDetailsEditEntryPoint {
override fun createNode(parentNode: Node, buildContext: BuildContext): Node {
lambdaError()
}
}

View File

@@ -8,20 +8,25 @@
package io.element.android.libraries.matrix.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
@@ -51,14 +56,15 @@ fun EditableAvatarView(
avatarType: AvatarType,
onAvatarClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
val a11yAvatar = stringResource(CommonStrings.a11y_avatar)
val editIconRadius = 15.dp
val parentHeight = avatarSize.dp
val parentWidth = avatarSize.dp + editIconRadius / 2f
Box(
modifier = Modifier
modifier = modifier
.wrapContentSize()
.size(height = parentHeight, width = parentWidth)
.clickable(
interactionSource = remember { MutableInteractionSource() },
onClickLabel = stringResource(CommonStrings.a11y_edit_avatar),
@@ -69,6 +75,24 @@ fun EditableAvatarView(
.clearAndSetSemantics {
contentDescription = a11yAvatar
},
) {
Box(
modifier = Modifier
.graphicsLayer {
compositingStrategy = CompositingStrategy.Offscreen
}
.drawWithContent {
drawContent()
drawCircle(
color = Color.Black,
center = Offset(
x = parentWidth.toPx() - editIconRadius.toPx(),
y = size.height - editIconRadius.toPx(),
),
radius = (editIconRadius + 4.dp).toPx(),
blendMode = BlendMode.Clear,
)
}
) {
when {
avatarUrl == null || avatarUrl.startsWith("mxc://") -> {
@@ -90,25 +114,19 @@ fun EditableAvatarView(
)
}
}
Box(
}
Icon(
modifier = Modifier
.align(Alignment.BottomEnd)
.clip(CircleShape)
.background(ElementTheme.colors.iconPrimary)
.size(24.dp),
contentAlignment = Alignment.Center,
) {
Icon(
modifier = Modifier.size(16.dp),
imageVector = CompoundIcons.EditSolid(),
.size(editIconRadius * 2)
.border(1.dp, ElementTheme.colors.borderInteractiveSecondary, CircleShape)
.padding(6.dp),
imageVector = CompoundIcons.Edit(),
contentDescription = null,
tint = ElementTheme.colors.iconOnSolidPrimary,
tint = ElementTheme.colors.iconPrimary,
)
}
}
}
}
@PreviewsDayNight
@Composable
@@ -119,9 +137,9 @@ internal fun EditableAvatarViewPreview(
) {
EditableAvatarView(
matrixId = "id",
displayName = "A room",
displayName = "Room",
avatarUrl = uri,
avatarSize = AvatarSize.EditRoomDetails,
avatarSize = AvatarSize.RoomDetailsHeader,
avatarType = AvatarType.User,
onAvatarClick = {},
)

View File

@@ -111,21 +111,3 @@ fun BaseRoom.isOwnUserAdmin(): Boolean {
val role = roomInfo.roleOf(sessionId)
return role == RoomMember.Role.Admin || role is RoomMember.Role.Owner
}
@Composable
fun BaseRoom.rawName(): String? {
val roomInfo by roomInfoFlow.collectAsState()
return roomInfo.rawName
}
@Composable
fun BaseRoom.topic(): String? {
val roomInfo by roomInfoFlow.collectAsState()
return roomInfo.topic
}
@Composable
fun BaseRoom.avatarUrl(): String? {
val roomInfo by roomInfoFlow.collectAsState()
return roomInfo.avatarUrl
}

View File

@@ -251,6 +251,7 @@ Reason: %1$s."</string>
<string name="common_message_removed">"Message removed"</string>
<string name="common_modern">"Modern"</string>
<string name="common_mute">"Mute"</string>
<string name="common_name">"Name"</string>
<string name="common_name_and_id">"%1$s (%2$s)"</string>
<string name="common_no_results">"No results"</string>
<string name="common_no_room_name">"No room name"</string>
@@ -325,6 +326,7 @@ Reason: %1$s."</string>
<string name="common_something_went_wrong">"Something went wrong"</string>
<string name="common_something_went_wrong_message">"We encountered an issue. Please try again."</string>
<string name="common_space">"Space"</string>
<string name="common_space_topic_placeholder">"What is this space about?"</string>
<plurals name="common_spaces">
<item quantity="one">"%1$d Space"</item>
<item quantity="other">"%1$d Spaces"</item>

Some files were not shown because too many files have changed in this diff Show More