mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-10-31 08:15:02 +00:00
Add an extension for room details sections (#7781)
* Add an extension for room details sections Provide a no-op rendering hook for build-specific room details sections and shared room audio labels. Keep the default room details behavior unchanged. * Cover the default room details extension Verify that the no-op extension renders no content and leaves surrounding UI unchanged through recomposition. Cover its constructor and both generated render paths. * Refine the room details extension and link the enterprise picker Expose ColumnScope to contributed room details sections and use room-details string keys. Pin the enterprise submodule to the reviewed picker implementation so integrated builds include it behind the default-off flag. * Make the room details extension a functional interface Declare the single composable rendering contract as a fun interface to satisfy the Sonar check while preserving its named dependency injection binding. * Update localazy strings for the audio section in room details * Bump enterprise commit --------- Co-authored-by: Jorge Martín <jorgem@element.io>
This commit is contained in:
+1
-1
Submodule enterprise updated: 6b422f4c8e...bcffe42d5c
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("io.element.android-library")
|
||||
id("io.element.android-compose-library")
|
||||
id("kotlin-parcelize")
|
||||
}
|
||||
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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.roomdetails.api
|
||||
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
/** Optional room-details sections contributed by a build-specific implementation. */
|
||||
fun interface RoomDetailsExtension {
|
||||
@Composable
|
||||
fun ColumnScope.Render(modifier: Modifier)
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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.roomdetails.impl
|
||||
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.features.roomdetails.api.RoomDetailsExtension
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
|
||||
@ContributesBinding(RoomScope::class)
|
||||
class NoopRoomDetailsExtension : RoomDetailsExtension {
|
||||
@Composable
|
||||
override fun ColumnScope.Render(modifier: Modifier) = Unit
|
||||
}
|
||||
+3
@@ -24,6 +24,7 @@ import dev.zacsweers.metro.AssistedInject
|
||||
import im.vector.app.features.analytics.plan.MobileScreen
|
||||
import io.element.android.annotations.ContributesNode
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomRenderer
|
||||
import io.element.android.features.roomdetails.api.RoomDetailsExtension
|
||||
import io.element.android.libraries.androidutils.system.startSharePlainTextIntent
|
||||
import io.element.android.libraries.architecture.appyx.launchMolecule
|
||||
import io.element.android.libraries.architecture.callback
|
||||
@@ -46,6 +47,7 @@ class RoomDetailsNode(
|
||||
private val room: BaseRoom,
|
||||
private val analyticsService: AnalyticsService,
|
||||
private val leaveRoomRenderer: LeaveRoomRenderer,
|
||||
private val roomDetailsExtension: RoomDetailsExtension,
|
||||
) : Node(buildContext, plugins = plugins), RoomDetailsNavigator {
|
||||
interface Callback : Plugin {
|
||||
fun navigateBack()
|
||||
@@ -120,6 +122,7 @@ class RoomDetailsNode(
|
||||
|
||||
RoomDetailsView(
|
||||
state = state,
|
||||
additionalSections = { with(roomDetailsExtension) { Render(Modifier) } },
|
||||
modifier = modifier,
|
||||
goBack = ::navigateUp,
|
||||
onActionClick = ::onActionClick,
|
||||
|
||||
+4
@@ -14,6 +14,7 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
@@ -123,6 +124,7 @@ fun RoomDetailsView(
|
||||
onProfileClick: (UserId) -> Unit,
|
||||
onReportRoomClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
additionalSections: @Composable ColumnScope.() -> Unit = {},
|
||||
leaveRoomView: @Composable () -> Unit,
|
||||
) {
|
||||
val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage)
|
||||
@@ -198,6 +200,8 @@ fun RoomDetailsView(
|
||||
)
|
||||
}
|
||||
|
||||
additionalSections()
|
||||
|
||||
PreferenceCategory {
|
||||
if (state.hasNewContent) {
|
||||
ListItem(
|
||||
|
||||
@@ -42,6 +42,9 @@
|
||||
<string name="screen_room_change_role_unsaved_changes_description">"You have unsaved changes."</string>
|
||||
<string name="screen_room_change_role_unsaved_changes_title">"Save changes?"</string>
|
||||
<string name="screen_room_details_add_topic_title">"Add topic"</string>
|
||||
<string name="screen_room_details_audio_push_to_talk">"Push to talk"</string>
|
||||
<string name="screen_room_details_audio_title">"Room Audio"</string>
|
||||
<string name="screen_room_details_audio_video_calls">"Video calls"</string>
|
||||
<string name="screen_room_details_badge_encrypted">"Encrypted"</string>
|
||||
<string name="screen_room_details_badge_not_encrypted">"Not encrypted"</string>
|
||||
<string name="screen_room_details_badge_public">"Public room"</string>
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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.
|
||||
*/
|
||||
|
||||
@file:OptIn(ExperimentalTestApi::class)
|
||||
|
||||
package io.element.android.features.roomdetails.impl
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.text.BasicText
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.test.ExperimentalTestApi
|
||||
import androidx.compose.ui.test.assertCountEquals
|
||||
import androidx.compose.ui.test.onChildren
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.onRoot
|
||||
import androidx.compose.ui.test.v2.runAndroidComposeUiTest
|
||||
import io.element.android.tests.testutils.robolectric.RobolectricTest
|
||||
import org.junit.Test
|
||||
|
||||
class NoopRoomDetailsExtensionTest : RobolectricTest() {
|
||||
@Test
|
||||
fun `default extension renders no additional content`() = runAndroidComposeUiTest<ComponentActivity> {
|
||||
val extension = NoopRoomDetailsExtension()
|
||||
setContent { Column { with(extension) { Render(Modifier) } } }
|
||||
onRoot().onChildren().assertCountEquals(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `default extension leaves surrounding content unchanged during updates`() = runAndroidComposeUiTest<ComponentActivity> {
|
||||
val extension = NoopRoomDetailsExtension()
|
||||
val text = mutableStateOf("Before")
|
||||
setContent {
|
||||
Column {
|
||||
BasicText(text.value)
|
||||
with(extension) { Render(Modifier) }
|
||||
}
|
||||
}
|
||||
onNodeWithText("Before").assertExists()
|
||||
onRoot().onChildren().assertCountEquals(1)
|
||||
runOnIdle { text.value = "After" }
|
||||
onNodeWithText("After").assertExists()
|
||||
onRoot().onChildren().assertCountEquals(1)
|
||||
}
|
||||
}
|
||||
+12
@@ -11,16 +11,20 @@
|
||||
package io.element.android.features.roomdetails.impl
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.test.AndroidComposeUiTest
|
||||
import androidx.compose.ui.test.ExperimentalTestApi
|
||||
import androidx.compose.ui.test.onAllNodesWithText
|
||||
import androidx.compose.ui.test.onLast
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.v2.runAndroidComposeUiTest
|
||||
import io.element.android.features.roomdetails.impl.members.aRoomMember
|
||||
import io.element.android.features.userprofile.shared.aUserProfileState
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.notification.CallIntent
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
@@ -41,6 +45,12 @@ import org.junit.Test
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
class RoomDetailsViewTest : RobolectricTest() {
|
||||
@Test
|
||||
fun `extension section is rendered`() = runAndroidComposeUiTest {
|
||||
setRoomDetailView(additionalSections = { Text("Extra section") })
|
||||
onNodeWithText("Extra section").assertExists()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `click on back invokes expected callback`() = runAndroidComposeUiTest {
|
||||
ensureCalledOnce { callback ->
|
||||
@@ -381,6 +391,7 @@ private fun AndroidComposeUiTest<ComponentActivity>.setRoomDetailView(
|
||||
onSecurityAndPrivacyClick: () -> Unit = EnsureNeverCalled(),
|
||||
onProfileClick: (UserId) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onReportRoomClick: () -> Unit = EnsureNeverCalled(),
|
||||
additionalSections: @Composable ColumnScope.() -> Unit = {},
|
||||
) {
|
||||
setContent {
|
||||
RoomDetailsView(
|
||||
@@ -402,6 +413,7 @@ private fun AndroidComposeUiTest<ComponentActivity>.setRoomDetailView(
|
||||
onProfileClick = onProfileClick,
|
||||
onReportRoomClick = onReportRoomClick,
|
||||
leaveRoomView = {},
|
||||
additionalSections = additionalSections,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user