diff --git a/features/knockrequests/api/build.gradle.kts b/features/knockrequests/api/build.gradle.kts index 90bcb6e568..c3eca7567c 100644 --- a/features/knockrequests/api/build.gradle.kts +++ b/features/knockrequests/api/build.gradle.kts @@ -6,7 +6,7 @@ */ plugins { - id("io.element.android-library") + id("io.element.android-compose-library") } android { diff --git a/features/knockrequests/api/src/main/kotlin/io/element/android/features/knockrequests/api/banner/KnockRequestsBannerRenderer.kt b/features/knockrequests/api/src/main/kotlin/io/element/android/features/knockrequests/api/banner/KnockRequestsBannerRenderer.kt new file mode 100644 index 0000000000..86483aee70 --- /dev/null +++ b/features/knockrequests/api/src/main/kotlin/io/element/android/features/knockrequests/api/banner/KnockRequestsBannerRenderer.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.knockrequests.api.banner + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +interface KnockRequestsBannerRenderer { + @Composable + fun View(modifier: Modifier, onViewRequestsClick: () -> Unit) +} diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/KnockRequest.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/KnockRequest.kt index d9df9a5bc2..1014d13e58 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/KnockRequest.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/KnockRequest.kt @@ -29,3 +29,17 @@ fun KnockRequest.getAvatarData(size: AvatarSize) = AvatarData( fun KnockRequest.getBestName(): String { return displayName?.takeIf { it.isNotEmpty() } ?: userId.value } + +fun aKnockRequest( + userId: UserId = UserId("@jacob_ross:example.com"), + displayName: String? = "Jacob Ross", + avatarUrl: String? = null, + reason: String? = "Hi, I would like to get access to this room please.", + formattedDate: String = "20 Nov 2024", +) = KnockRequest( + userId = userId, + displayName = displayName, + avatarUrl = avatarUrl, + reason = reason, + formattedDate = formattedDate, +) diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/DefaultKnockRequestsBannerRenderer.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/DefaultKnockRequestsBannerRenderer.kt new file mode 100644 index 0000000000..9fce2f2173 --- /dev/null +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/DefaultKnockRequestsBannerRenderer.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.knockrequests.impl.banner + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.knockrequests.api.banner.KnockRequestsBannerRenderer +import io.element.android.libraries.di.RoomScope +import javax.inject.Inject + +@ContributesBinding(RoomScope::class) +class DefaultKnockRequestsBannerRenderer @Inject constructor( + private val presenter: KnockRequestsBannerPresenter, +) : KnockRequestsBannerRenderer { + @Composable + override fun View(modifier: Modifier, onViewRequestsClick: () -> Unit) { + val state = presenter.present() + KnockRequestsBannerView( + state = state, + onViewRequestsClick = onViewRequestsClick, + ) + } +} diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerEvents.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerEvents.kt new file mode 100644 index 0000000000..14239d93ef --- /dev/null +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerEvents.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.knockrequests.impl.banner + +sealed interface KnockRequestsBannerEvents { + data object AcceptSingleRequest : KnockRequestsBannerEvents + data object Dismiss : KnockRequestsBannerEvents +} diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerPresenter.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerPresenter.kt new file mode 100644 index 0000000000..a61236bbb0 --- /dev/null +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerPresenter.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.knockrequests.impl.banner + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.architecture.Presenter +import kotlinx.collections.immutable.persistentListOf +import javax.inject.Inject + +class KnockRequestsBannerPresenter @Inject constructor() : Presenter { + @Composable + override fun present(): KnockRequestsBannerState { + var shouldShowBanner by remember { mutableStateOf(false) } + + fun handleEvents(event: KnockRequestsBannerEvents) { + when (event) { + is KnockRequestsBannerEvents.AcceptSingleRequest -> Unit + is KnockRequestsBannerEvents.Dismiss -> { + shouldShowBanner = false + } + } + } + + return KnockRequestsBannerState( + knockRequests = persistentListOf(), + acceptAction = AsyncAction.Uninitialized, + canAccept = false, + isVisible = shouldShowBanner, + eventSink = ::handleEvents, + ) + } +} diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerState.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerState.kt new file mode 100644 index 0000000000..e65cd2fb26 --- /dev/null +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerState.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.knockrequests.impl.banner + +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.pluralStringResource +import androidx.compose.ui.res.stringResource +import io.element.android.features.knockrequests.impl.KnockRequest +import io.element.android.features.knockrequests.impl.R +import io.element.android.features.knockrequests.impl.getBestName +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.core.extensions.firstIfSingle +import kotlinx.collections.immutable.ImmutableList + +data class KnockRequestsBannerState( + val isVisible: Boolean, + val knockRequests: ImmutableList, + val acceptAction: AsyncAction, + val canAccept: Boolean, + val eventSink: (KnockRequestsBannerEvents) -> Unit, +) { + val subtitle = knockRequests.firstIfSingle()?.userId?.value + val reason = knockRequests.firstIfSingle()?.reason + + @Composable + fun formattedTitle(): String { + return when (knockRequests.size) { + 0 -> "" + 1 -> stringResource(R.string.screen_room_single_knock_request_title, knockRequests.first().getBestName()) + else -> { + val firstRequest = knockRequests.first() + val otherRequestsCount = knockRequests.size - 1 + pluralStringResource( + id = R.plurals.screen_room_multiple_knock_requests_title, + count = otherRequestsCount, + firstRequest.getBestName(), + otherRequestsCount + ) + } + } + } +} diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerStateProvider.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerStateProvider.kt new file mode 100644 index 0000000000..87295d94ee --- /dev/null +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerStateProvider.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.knockrequests.impl.banner + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.knockrequests.impl.KnockRequest +import io.element.android.features.knockrequests.impl.aKnockRequest +import io.element.android.libraries.architecture.AsyncAction +import kotlinx.collections.immutable.toImmutableList + +class KnockRequestsBannerStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aKnockRequestsBannerState(), + aKnockRequestsBannerState( + knockRequests = listOf( + aKnockRequest( + reason = "A very long reason that should probably be truncated, " + + "but could be also expanded so you can see it over the lines, wow," + + "very amazing reason, I know, right, I'm so good at writing reasons." + ) + ) + ), + aKnockRequestsBannerState( + knockRequests = listOf( + aKnockRequest(), + aKnockRequest(displayName = "Alice") + ) + ), + aKnockRequestsBannerState( + knockRequests = listOf( + aKnockRequest(), + aKnockRequest(displayName = "Alice"), + aKnockRequest(displayName = "Bob"), + aKnockRequest(displayName = "Charlie") + ) + ), + aKnockRequestsBannerState( + canAccept = false + ), + aKnockRequestsBannerState( + acceptAction = AsyncAction.Loading + ), + aKnockRequestsBannerState( + acceptAction = AsyncAction.Failure(Throwable("Failed to accept knock")) + ), + ) +} + +fun aKnockRequestsBannerState( + knockRequests: List = listOf(aKnockRequest()), + acceptAction: AsyncAction = AsyncAction.Uninitialized, + canAccept: Boolean = true, + isVisible: Boolean = true, + eventSink: (KnockRequestsBannerEvents) -> Unit = {} +) = KnockRequestsBannerState( + knockRequests = knockRequests.toImmutableList(), + acceptAction = acceptAction, + canAccept = canAccept, + isVisible = isVisible, + eventSink = eventSink, +) diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerView.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerView.kt new file mode 100644 index 0000000000..402b8ba182 --- /dev/null +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerView.kt @@ -0,0 +1,217 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.knockrequests.impl.banner + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.expandVertically +import androidx.compose.animation.shrinkVertically +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.knockrequests.impl.KnockRequest +import io.element.android.features.knockrequests.impl.R +import io.element.android.features.knockrequests.impl.getAvatarData +import io.element.android.libraries.designsystem.components.avatar.Avatar +import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.Button +import io.element.android.libraries.designsystem.theme.components.ButtonSize +import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.OutlinedButton +import io.element.android.libraries.designsystem.theme.components.Surface +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.ImmutableList + +private const val MAX_AVATAR_COUNT = 3 + +@Composable +fun KnockRequestsBannerView( + state: KnockRequestsBannerState, + onViewRequestsClick: () -> Unit, + modifier: Modifier = Modifier, +) { + AnimatedVisibility( + visible = state.isVisible, + enter = expandVertically(), + exit = shrinkVertically(), + modifier = modifier, + ) { + Surface( + shape = MaterialTheme.shapes.small, + color = ElementTheme.colors.bgCanvasDefaultLevel1, + shadowElevation = 24.dp, + modifier = Modifier.padding(16.dp), + ) { + KnockRequestsBannerContent( + state = state, + onViewRequestsClick = onViewRequestsClick, + ) + } + } +} + +@Composable +private fun KnockRequestsBannerContent( + state: KnockRequestsBannerState, + onViewRequestsClick: () -> Unit, + modifier: Modifier = Modifier, +) { + fun onDismissClick() { + state.eventSink(KnockRequestsBannerEvents.Dismiss) + } + + fun onAcceptClick() { + state.eventSink(KnockRequestsBannerEvents.AcceptSingleRequest) + } + + Column( + modifier + .fillMaxWidth() + .padding(all = 16.dp) + ) { + Row { + KnockRequestAvatarView(state.knockRequests) + Spacer(modifier = Modifier.width(10.dp)) + Column(modifier = Modifier.weight(1f)) { + Text( + text = state.formattedTitle(), + style = ElementTheme.typography.fontBodyMdMedium, + color = MaterialTheme.colorScheme.primary, + textAlign = TextAlign.Start, + ) + if (state.subtitle != null) { + Text( + text = state.subtitle, + style = ElementTheme.typography.fontBodySmRegular, + color = MaterialTheme.colorScheme.secondary, + textAlign = TextAlign.Start, + ) + } + } + Icon( + modifier = Modifier.clickable(onClick = ::onDismissClick), + imageVector = CompoundIcons.Close(), + contentDescription = stringResource(CommonStrings.action_close) + ) + } + if (state.reason != null) { + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = state.reason, + color = ElementTheme.colors.textPrimary, + style = ElementTheme.typography.fontBodyMdRegular, + maxLines = 2, + overflow = TextOverflow.Ellipsis, + ) + } + Spacer(modifier = Modifier.height(16.dp)) + Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(12.dp)) { + if (state.knockRequests.size > 1) { + Button( + text = stringResource(R.string.screen_room_multiple_knock_requests_view_all_button_title), + onClick = onViewRequestsClick, + size = ButtonSize.MediumLowPadding, + modifier = Modifier.weight(1f), + ) + } else { + OutlinedButton( + text = stringResource(R.string.screen_room_single_knock_request_view_button_title), + onClick = onViewRequestsClick, + size = ButtonSize.MediumLowPadding, + modifier = Modifier.weight(1f), + ) + if (state.canAccept) { + Button( + text = stringResource(R.string.screen_room_single_knock_request_accept_button_title), + onClick = ::onAcceptClick, + size = ButtonSize.MediumLowPadding, + modifier = Modifier.weight(1f), + ) + } + } + } + } +} + +@Composable +private fun KnockRequestAvatarView( + knockRequests: ImmutableList, + modifier: Modifier = Modifier, +) { + Box(modifier) { + when (knockRequests.size) { + 0 -> Unit + 1 -> Avatar(knockRequests.first().getAvatarData(AvatarSize.KnockRequestBanner)) + else -> KnockRequestAvatarListView(knockRequests) + } + } +} + +@Composable +private fun KnockRequestAvatarListView( + knockRequests: ImmutableList, + modifier: Modifier = Modifier, +) { + val avatarSize = AvatarSize.KnockRequestBanner.dp + Row( + modifier = modifier, + horizontalArrangement = Arrangement.spacedBy(-avatarSize / 2), + ) { + knockRequests + .take(MAX_AVATAR_COUNT) + .forEachIndexed { index, knockRequest -> + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .size(size = avatarSize) + .clip(CircleShape) + .background(color = ElementTheme.colors.bgCanvasDefaultLevel1) + .zIndex(-index.toFloat()), + ) { + Avatar( + modifier = Modifier.padding(2.dp), + avatarData = knockRequest.getAvatarData(AvatarSize.KnockRequestBanner), + ) + } + } + } +} + +@Composable +@PreviewsDayNight +internal fun KnockRequestsBannerViewPreview(@PreviewParameter(KnockRequestsBannerStateProvider::class) state: KnockRequestsBannerState) = ElementPreview { + KnockRequestsBannerView( + state = state, + onViewRequestsClick = {}, + ) +} diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListStateProvider.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListStateProvider.kt index 551f6b89b0..476bf556e1 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListStateProvider.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListStateProvider.kt @@ -9,6 +9,7 @@ package io.element.android.features.knockrequests.impl.list import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.knockrequests.impl.KnockRequest +import io.element.android.features.knockrequests.impl.aKnockRequest import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.UserId @@ -101,20 +102,6 @@ open class KnockRequestsListStateProvider : PreviewParameterProvider> = AsyncData.Success(persistentListOf()), currentAction: KnockRequestsCurrentAction = KnockRequestsCurrentAction.None, diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListView.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListView.kt index ca289eeef4..41b5553438 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListView.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListView.kt @@ -383,7 +383,7 @@ private fun KnockRequestsEmptyList( IconTitleSubtitleMolecule( title = stringResource(R.string.screen_knock_requests_list_empty_state_title), subTitle = stringResource(R.string.screen_knock_requests_list_empty_state_description), - iconStyle = BigIcon.Style.Default(CompoundIcons.Pin()), + iconStyle = BigIcon.Style.Default(CompoundIcons.AskToJoin()), ) } } diff --git a/features/knockrequests/impl/src/main/res/values/localazy.xml b/features/knockrequests/impl/src/main/res/values/localazy.xml index df14d665b8..d2e9dc5d14 100644 --- a/features/knockrequests/impl/src/main/res/values/localazy.xml +++ b/features/knockrequests/impl/src/main/res/values/localazy.xml @@ -14,4 +14,12 @@ "When somebody will ask to join the room, you’ll be able to see their request here." "No pending request to join" "Requests to join" + + "%1$s +%2$d other want to join this room" + "%1$s +%2$d others want to join this room" + + "View all" + "Accept" + "%1$s wants to join this room" + "View" diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts index 824e8c1692..e87d90cbdf 100644 --- a/features/messages/impl/build.gradle.kts +++ b/features/messages/impl/build.gradle.kts @@ -65,6 +65,7 @@ dependencies { implementation(libs.vanniktech.blurhash) implementation(libs.telephoto.zoomableimage) implementation(libs.matrix.emojibase.bindings) + implementation(projects.features.knockrequests.api) testImplementation(libs.test.junit) testImplementation(libs.coroutines.test) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index 8cbb7b6d74..27b219fc1c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -26,6 +26,7 @@ import im.vector.app.features.analytics.plan.Interaction import io.element.android.anvilannotations.ContributesNode import io.element.android.features.call.api.CallType import io.element.android.features.call.api.ElementCallEntryPoint +import io.element.android.features.knockrequests.api.list.KnockRequestsListEntryPoint import io.element.android.features.location.api.Location import io.element.android.features.location.api.SendLocationEntryPoint import io.element.android.features.location.api.ShowLocationEntryPoint @@ -95,6 +96,7 @@ class MessagesFlowNode @AssistedInject constructor( private val mentionSpanTheme: MentionSpanTheme, private val pinnedEventsTimelineProvider: PinnedEventsTimelineProvider, private val timelineController: TimelineController, + private val knockRequestsListEntryPoint: KnockRequestsListEntryPoint, ) : BaseFlowNode( backstack = BackStack( initialElement = plugins.filterIsInstance().first().initialTarget.toNavTarget(), @@ -146,6 +148,9 @@ class MessagesFlowNode @AssistedInject constructor( @Parcelize data object PinnedMessagesList : NavTarget + + @Parcelize + data object KnockRequestsList : NavTarget } private val callbacks = plugins() @@ -226,6 +231,10 @@ class MessagesFlowNode @AssistedInject constructor( override fun onViewAllPinnedEvents() { backstack.push(NavTarget.PinnedMessagesList) } + + override fun onViewKnockRequests() { + backstack.push(NavTarget.KnockRequestsList) + } } val inputs = MessagesNode.Inputs(focusedEventId = navTarget.focusedEventId) createNode(buildContext, listOf(callback, inputs)) @@ -326,6 +335,9 @@ class MessagesFlowNode @AssistedInject constructor( NavTarget.Empty -> { node(buildContext) {} } + NavTarget.KnockRequestsList -> { + knockRequestsListEntryPoint.createNode(this, buildContext) + } } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index 7d5bad4d63..4ee44fbc80 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -28,6 +28,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.compound.theme.ElementTheme +import io.element.android.features.knockrequests.api.banner.KnockRequestsBannerRenderer import io.element.android.features.messages.impl.actionlist.ActionListPresenter import io.element.android.features.messages.impl.actionlist.model.TimelineItemActionPostProcessor import io.element.android.features.messages.impl.attachments.Attachment @@ -71,6 +72,7 @@ class MessagesNode @AssistedInject constructor( private val timelineItemPresenterFactories: TimelineItemPresenterFactories, private val mediaPlayer: MediaPlayer, private val permalinkParser: PermalinkParser, + private val knockRequestsBannerRenderer: KnockRequestsBannerRenderer ) : Node(buildContext, plugins = plugins), MessagesNavigator { private val presenter = presenterFactory.create( navigator = this, @@ -98,6 +100,7 @@ class MessagesNode @AssistedInject constructor( fun onEditPollClick(eventId: EventId) fun onJoinCallClick(roomId: RoomId) fun onViewAllPinnedEvents() + fun onViewKnockRequests() } override fun onBuilt() { @@ -206,6 +209,10 @@ class MessagesNode @AssistedInject constructor( callbacks.forEach { it.onJoinCallClick(room.roomId) } } + private fun onViewKnockRequestsClick() { + callbacks.forEach { it.onViewKnockRequests() } + } + @Composable override fun View(modifier: Modifier) { val activity = LocalContext.current as Activity @@ -231,6 +238,12 @@ class MessagesNode @AssistedInject constructor( onCreatePollClick = this::onCreatePollClick, onJoinCallClick = this::onJoinCallClick, onViewAllPinnedMessagesClick = this::onViewAllPinnedMessagesClick, + knockRequestsBannerView = { + knockRequestsBannerRenderer.View( + modifier = Modifier, + onViewRequestsClick = this::onViewKnockRequestsClick + ) + }, modifier = modifier, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index 47e4721f7f..c0af0088c1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -118,6 +118,7 @@ fun MessagesView( onViewAllPinnedMessagesClick: () -> Unit, modifier: Modifier = Modifier, forceJumpToBottomVisibility: Boolean = false, + knockRequestsBannerView: @Composable () -> Unit, ) { OnLifecycleEvent { _, event -> state.voiceMessageComposerState.eventSink(VoiceMessageComposerEvents.LifecycleEvent(event)) @@ -195,8 +196,8 @@ fun MessagesView( MessagesViewContent( state = state, modifier = Modifier - .padding(padding) - .consumeWindowInsets(padding), + .padding(padding) + .consumeWindowInsets(padding), onContentClick = ::onContentClick, onMessageLongClick = ::onMessageLongClick, onUserDataClick = { hidingKeyboard { onUserDataClick(it) } }, @@ -215,6 +216,7 @@ fun MessagesView( forceJumpToBottomVisibility = forceJumpToBottomVisibility, onJoinCallClick = onJoinCallClick, onViewAllPinnedMessagesClick = onViewAllPinnedMessagesClick, + knockRequestsBannerView = knockRequestsBannerView, ) }, snackbarHost = { @@ -284,12 +286,13 @@ private fun MessagesViewContent( forceJumpToBottomVisibility: Boolean, onSwipeToReply: (TimelineItem.Event) -> Unit, modifier: Modifier = Modifier, + knockRequestsBannerView: @Composable () -> Unit, ) { Box( modifier = modifier - .fillMaxSize() - .navigationBarsPadding() - .imePadding(), + .fillMaxSize() + .navigationBarsPadding() + .imePadding(), ) { AttachmentsBottomSheet( state = state.composerState, @@ -372,6 +375,7 @@ private fun MessagesViewContent( onViewAllClick = onViewAllPinnedMessagesClick, ) } + knockRequestsBannerView() } }, sheetContent = { subcomposing: Boolean -> @@ -398,13 +402,13 @@ private fun MessagesViewComposerBottomSheetContents( Column(modifier = Modifier.fillMaxWidth()) { SuggestionsPickerView( modifier = Modifier - .heightIn(max = 230.dp) - // Consume all scrolling, preventing the bottom sheet from being dragged when interacting with the list of suggestions - .nestedScroll(object : NestedScrollConnection { - override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset { - return available - } - }), + .heightIn(max = 230.dp) + // Consume all scrolling, preventing the bottom sheet from being dragged when interacting with the list of suggestions + .nestedScroll(object : NestedScrollConnection { + override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset { + return available + } + }), roomId = state.roomId, roomName = state.roomName.dataOrNull(), roomAvatarData = state.roomAvatar.dataOrNull(), @@ -452,8 +456,8 @@ private fun MessagesViewTopBar( title = { val roundedCornerShape = RoundedCornerShape(8.dp) val titleModifier = Modifier - .clip(roundedCornerShape) - .clickable { onRoomDetailsClick() } + .clip(roundedCornerShape) + .clickable { onRoomDetailsClick() } if (roomName != null && roomAvatar != null) { RoomAvatarAndNameRow( roomName = roomName, @@ -508,9 +512,9 @@ private fun RoomAvatarAndNameRow( private fun CantSendMessageBanner() { Row( modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.secondary) - .padding(16.dp), + .fillMaxWidth() + .background(MaterialTheme.colorScheme.secondary) + .padding(16.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center ) { @@ -539,5 +543,6 @@ internal fun MessagesViewPreview(@PreviewParameter(MessagesStateProvider::class) onJoinCallClick = {}, onViewAllPinnedMessagesClick = { }, forceJumpToBottomVisibility = true, + knockRequestsBannerView = {}, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/MessagesViewWithIdentityChangePreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/MessagesViewWithIdentityChangePreview.kt index 34c58bdb06..5dc55b0dc4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/MessagesViewWithIdentityChangePreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/MessagesViewWithIdentityChangePreview.kt @@ -40,5 +40,6 @@ internal fun MessagesViewWithIdentityChangePreview( onCreatePollClick = {}, onJoinCallClick = {}, onViewAllPinnedMessagesClick = {}, + knockRequestsBannerView = {} ) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt index 1d4e1a43b3..b15f358828 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt @@ -533,6 +533,7 @@ private fun AndroidComposeTestRule.setMessa onCreatePollClick = onCreatePollClick, onJoinCallClick = onJoinCallClick, onViewAllPinnedMessagesClick = onViewAllPinnedMessagesClick, + knockRequestsBannerView = {} ) } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index d77cd9e6ea..d73b8e2626 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -242,7 +242,7 @@ fun RoomDetailsView( private fun KnockRequestsItem(knockRequestsCount: Int?, onKnockRequestsClick: () -> Unit) { ListItem( headlineContent = { Text(stringResource(R.string.screen_room_details_requests_to_join_title)) }, - leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Notifications())), + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.AskToJoin())), trailingContent = if (knockRequestsCount == null || knockRequestsCount == 0) { null } else { diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/List.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/List.kt new file mode 100644 index 0000000000..0dee04408a --- /dev/null +++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/List.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.core.extensions + +/** + * Returns the first element if the list contains exactly one element, otherwise returns null. + */ +inline fun List.firstIfSingle(): T? { + return if (size == 1) first() else null +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt index c5bd468abe..3f7d087f41 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt @@ -56,4 +56,5 @@ enum class AvatarSize(val dp: Dp) { Suggestion(32.dp), KnockRequestItem(52.dp), + KnockRequestBanner(32.dp), } diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 75b0f6853a..6e8569cae1 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -322,18 +322,10 @@ Reason: %1$s." "Your message was not sent because you have not verified one or more of your devices" "Failed processing media to upload, please try again." "Could not retrieve user details" - - "%1$s +%2$d other want to join this room" - "%1$s +%2$d others want to join this room" - - "View all" "%1$s of %2$s" "%1$s Pinned messages" "Loading message…" "View All" - "Accept" - "%1$s wants to join this room" - "View" "Chat" "Request to join sent" "Share location" diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en.png new file mode 100644 index 0000000000..3b63b61f2d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d20f97f6422fb2eaaccd13ab12b3e27e589eaafe032a65696f3c862ccae4b743 +size 29335 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en.png new file mode 100644 index 0000000000..1708abde01 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e0f508d82473c7b1dc9da20d25808843c3e1bfcac632ac2bf33151cc7626bf35 +size 34821 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en.png new file mode 100644 index 0000000000..d5a84e03df --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5262c97044361ab44066a8876b8417853c8ef5c1449390242c0248c06a0fc568 +size 17855 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en.png new file mode 100644 index 0000000000..c148a2032f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f247767c5f965cf75f1c7556f49bd5e3c7207b2effd9cd3d19a913581556842 +size 18744 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en.png new file mode 100644 index 0000000000..b606b20db0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78f259c410ad179c3f93b18fc7c10829a355e5a0f56b0dd4c1f4b4efc72910f0 +size 27309 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en.png new file mode 100644 index 0000000000..3b63b61f2d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d20f97f6422fb2eaaccd13ab12b3e27e589eaafe032a65696f3c862ccae4b743 +size 29335 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en.png new file mode 100644 index 0000000000..3b63b61f2d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d20f97f6422fb2eaaccd13ab12b3e27e589eaafe032a65696f3c862ccae4b743 +size 29335 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en.png new file mode 100644 index 0000000000..7b685bcbcb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a965026cd6257bd691bdf821dcb29c99276ab5aebff2060610acc287cebf4c35 +size 27357 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en.png new file mode 100644 index 0000000000..6d822fab84 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:77bc7bd3f4dab9e05ce7f68ddc50d06da03d414c33ce8e361bb83ccec38ad3c8 +size 32231 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en.png new file mode 100644 index 0000000000..9c7829d9fe --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cff0d41eb0ab572946867cb9ea57fcab1fb72b155273fc68b12855c100cdb0c0 +size 15974 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en.png new file mode 100644 index 0000000000..34c0b0c9ff --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8fcbb3f101b864cd0a5eeade5d43a76e42573705a90604706be696a810e7096c +size 17021 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en.png new file mode 100644 index 0000000000..06ad640021 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f89ebacc5f45844b3606a214a4c0df8ca150003de50bd7a04680d85ddea2ee15 +size 25224 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en.png new file mode 100644 index 0000000000..7b685bcbcb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a965026cd6257bd691bdf821dcb29c99276ab5aebff2060610acc287cebf4c35 +size 27357 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en.png new file mode 100644 index 0000000000..7b685bcbcb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a965026cd6257bd691bdf821dcb29c99276ab5aebff2060610acc287cebf4c35 +size 27357 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Day_1_en.png index 66c6102947..2548547d60 100644 --- a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9595edf8183796a0a0efa838a3c5265e07f70452933e6255d79f536cfca64ac -size 26138 +oid sha256:a67bb9ba78ea1d8802ce503e7d6a16be2ff46415df0e435efd7a36f3ee711b0d +size 26453 diff --git a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Night_1_en.png index 055bfccd93..613fccf45c 100644 --- a/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.knockrequests.impl.list_KnockRequestsListView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:37e24295a404463010f6d36ab94b1ac3c2e193d9122637abbeb0a43abcb427c1 -size 25669 +oid sha256:be2ae50ce1b0e23fec7bcbbe9ca0f0381af4d0f29f1e7498be837d9d53b75542 +size 26003 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_81_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_81_en.png new file mode 100644 index 0000000000..3c5b96d217 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_81_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5abc97134f8a5f0d037367c9278d8f007543463e13c8e043241f292949681ff6 +size 17728 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_82_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_82_en.png new file mode 100644 index 0000000000..1100737467 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_82_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:972653df5c62859c175a62d7809193afd0cb68832e3567760082f1b164e3424b +size 16990 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_83_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_83_en.png new file mode 100644 index 0000000000..72c687d439 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_83_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:491cb82462df122b0e8d21c5b70e8db8ac19c28aaee1289db6f4774e7d31d53f +size 19556 diff --git a/tools/localazy/config.json b/tools/localazy/config.json index fe95d1930e..a365379344 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -291,7 +291,9 @@ { "name" : ":features:knockrequests:impl", "includeRegex" : [ - "screen\\.knock_requests_list\\..*" + "screen\\.knock_requests_list\\..*", + "screen\\.room\\.single_knock_request.*", + "screen\\.room\\.multiple_knock_requests.*" ] } ]