From 57ac39673d1faee0b22fe8088771f798bb094fe9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Sat, 25 Oct 2025 15:31:10 +0200 Subject: [PATCH 001/216] Notification: show userId in notification when several accounts are configured. --- .../enterprise/test/FakeEnterpriseService.kt | 3 +- .../android/libraries/matrix/test/TestData.kt | 4 +- .../notifications/NotificationDataFactory.kt | 41 +++++------ .../notifications/NotificationRenderer.kt | 20 ++++-- .../notifications/RoomGroupMessageCreator.kt | 14 ++-- .../SummaryGroupMessageCreator.kt | 12 ++-- .../factories/NotificationAccountParams.kt | 17 +++++ .../factories/NotificationCreator.kt | 70 +++++++++---------- .../DefaultBaseRoomGroupMessageCreatorTest.kt | 26 +++---- .../DefaultNotificationDrawerManagerTest.kt | 39 +++++++++-- ...aultOnMissedCallNotificationHandlerTest.kt | 6 +- .../DefaultSummaryGroupMessageCreatorTest.kt | 8 +-- .../NotificationDataFactoryTest.kt | 41 +++++++---- .../notifications/NotificationRendererTest.kt | 23 ++++-- .../DefaultNotificationCreatorTest.kt | 24 +++---- .../factories/NotificationAccountParams.kt | 23 ++++++ .../fake/FakeNotificationCreator.kt | 47 +++++++------ .../fake/FakeNotificationDataFactory.kt | 26 ++++--- .../fake/FakeRoomGroupMessageCreator.kt | 13 ++-- .../fake/FakeSummaryGroupMessageCreator.kt | 10 ++- 20 files changed, 265 insertions(+), 202 deletions(-) create mode 100644 libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationAccountParams.kt create mode 100644 libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationAccountParams.kt diff --git a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt index 39b7c320d9..72f7640615 100644 --- a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt +++ b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt @@ -24,11 +24,12 @@ class FakeEnterpriseService( private val defaultHomeserverListResult: () -> List = { emptyList() }, private val isAllowedToConnectToHomeserverResult: (String) -> Boolean = { lambdaError() }, initialSemanticColors: SemanticColorsLightDark = SemanticColorsLightDark.default, + initialBrandColor: Color? = null, private val overrideBrandColorResult: (SessionId?, String?) -> Unit = { _, _ -> lambdaError() }, private val firebasePushGatewayResult: () -> String? = { lambdaError() }, private val unifiedPushDefaultPushGatewayResult: () -> String? = { lambdaError() }, ) : EnterpriseService { - private val brandColorState = MutableStateFlow(null) + private val brandColorState = MutableStateFlow(initialBrandColor) private val semanticColorsState = MutableStateFlow(initialSemanticColors) override suspend fun isEnterpriseUser(sessionId: SessionId): Boolean = simulateLongTask { diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt index 8db5fc6807..8c22c90cd7 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt @@ -7,6 +7,7 @@ package io.element.android.libraries.matrix.test +import androidx.annotation.ColorInt import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.EventId @@ -99,4 +100,5 @@ const val A_FORMATTED_DATE = "April 6, 1980 at 6:35 PM" const val A_LOGIN_HINT = "mxid:@alice:example.org" -const val A_COLOR_INT = 0xFF0000 +@ColorInt +const val A_COLOR_INT: Int = 0xFFFF0000.toInt() diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactory.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactory.kt index a3becd94c1..329d34d5f5 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactory.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactory.kt @@ -10,7 +10,6 @@ package io.element.android.libraries.push.impl.notifications import android.app.Notification import android.graphics.Typeface import android.text.style.StyleSpan -import androidx.annotation.ColorInt import androidx.core.text.buildSpannedString import androidx.core.text.inSpans import coil3.ImageLoader @@ -19,8 +18,8 @@ import dev.zacsweers.metro.ContributesBinding import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.ThreadId -import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.R +import io.element.android.libraries.push.impl.notifications.factories.NotificationAccountParams import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent @@ -31,39 +30,37 @@ import io.element.android.services.toolbox.api.strings.StringProvider interface NotificationDataFactory { suspend fun toNotifications( messages: List, - currentUser: MatrixUser, imageLoader: ImageLoader, - @ColorInt color: Int, + notificationAccountParams: NotificationAccountParams, ): List @JvmName("toNotificationInvites") @Suppress("INAPPLICABLE_JVM_NAME") fun toNotifications( invites: List, - @ColorInt color: Int, + notificationAccountParams: NotificationAccountParams, ): List @JvmName("toNotificationSimpleEvents") @Suppress("INAPPLICABLE_JVM_NAME") fun toNotifications( simpleEvents: List, - @ColorInt color: Int, + notificationAccountParams: NotificationAccountParams, ): List @JvmName("toNotificationFallbackEvents") @Suppress("INAPPLICABLE_JVM_NAME") fun toNotifications( fallback: List, - @ColorInt color: Int, + notificationAccountParams: NotificationAccountParams, ): List fun createSummaryNotification( - currentUser: MatrixUser, roomNotifications: List, invitationNotifications: List, simpleNotifications: List, fallbackNotifications: List, - @ColorInt color: Int, + notificationAccountParams: NotificationAccountParams, ): SummaryNotification } @@ -77,9 +74,8 @@ class DefaultNotificationDataFactory( ) : NotificationDataFactory { override suspend fun toNotifications( messages: List, - currentUser: MatrixUser, imageLoader: ImageLoader, - @ColorInt color: Int, + notificationAccountParams: NotificationAccountParams, ): List { val messagesToDisplay = messages.filterNot { it.canNotBeDisplayed() } .groupBy { it.roomId } @@ -90,13 +86,12 @@ class DefaultNotificationDataFactory( eventsByThreadId.map { (threadId, events) -> val notification = roomGroupMessageCreator.createRoomMessage( - currentUser = currentUser, events = events, roomId = roomId, threadId = threadId, imageLoader = imageLoader, - existingNotification = getExistingNotificationForMessages(currentUser.userId, roomId, threadId), - color = color, + existingNotification = getExistingNotificationForMessages(notificationAccountParams.user.userId, roomId, threadId), + notificationAccountParams = notificationAccountParams, ) RoomNotification( notification = notification, @@ -121,12 +116,12 @@ class DefaultNotificationDataFactory( @Suppress("INAPPLICABLE_JVM_NAME") override fun toNotifications( invites: List, - @ColorInt color: Int, + notificationAccountParams: NotificationAccountParams, ): List { return invites.map { event -> OneShotNotification( key = event.roomId.value, - notification = notificationCreator.createRoomInvitationNotification(event, color), + notification = notificationCreator.createRoomInvitationNotification(notificationAccountParams, event), summaryLine = event.description, isNoisy = event.noisy, timestamp = event.timestamp @@ -138,12 +133,12 @@ class DefaultNotificationDataFactory( @Suppress("INAPPLICABLE_JVM_NAME") override fun toNotifications( simpleEvents: List, - @ColorInt color: Int, + notificationAccountParams: NotificationAccountParams, ): List { return simpleEvents.map { event -> OneShotNotification( key = event.eventId.value, - notification = notificationCreator.createSimpleEventNotification(event, color), + notification = notificationCreator.createSimpleEventNotification(notificationAccountParams, event), summaryLine = event.description, isNoisy = event.noisy, timestamp = event.timestamp @@ -155,12 +150,12 @@ class DefaultNotificationDataFactory( @Suppress("INAPPLICABLE_JVM_NAME") override fun toNotifications( fallback: List, - @ColorInt color: Int, + notificationAccountParams: NotificationAccountParams, ): List { return fallback.map { event -> OneShotNotification( key = event.eventId.value, - notification = notificationCreator.createFallbackNotification(event, color), + notification = notificationCreator.createFallbackNotification(notificationAccountParams, event), summaryLine = event.description.orEmpty(), isNoisy = false, timestamp = event.timestamp @@ -169,23 +164,21 @@ class DefaultNotificationDataFactory( } override fun createSummaryNotification( - currentUser: MatrixUser, roomNotifications: List, invitationNotifications: List, simpleNotifications: List, fallbackNotifications: List, - @ColorInt color: Int, + notificationAccountParams: NotificationAccountParams, ): SummaryNotification { return when { roomNotifications.isEmpty() && invitationNotifications.isEmpty() && simpleNotifications.isEmpty() -> SummaryNotification.Removed else -> SummaryNotification.Update( summaryGroupMessageCreator.createSummaryNotification( - currentUser = currentUser, roomNotifications = roomNotifications, invitationNotifications = invitationNotifications, simpleNotifications = simpleNotifications, fallbackNotifications = fallbackNotifications, - color = color, + notificationAccountParams = notificationAccountParams, ) ) } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt index 5248bce175..05b72b0bcd 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt @@ -15,6 +15,7 @@ import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.api.notifications.NotificationIdProvider +import io.element.android.libraries.push.impl.notifications.factories.NotificationAccountParams import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent @@ -22,6 +23,7 @@ import io.element.android.libraries.push.impl.notifications.model.NotifiableEven import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableRingingCallEvent import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent +import io.element.android.libraries.sessionstorage.api.SessionStore import kotlinx.coroutines.flow.first import timber.log.Timber @@ -32,6 +34,7 @@ class NotificationRenderer( private val notificationDisplayer: NotificationDisplayer, private val notificationDataFactory: NotificationDataFactory, private val enterpriseService: EnterpriseService, + private val sessionStore: SessionStore, ) { suspend fun render( currentUser: MatrixUser, @@ -41,18 +44,23 @@ class NotificationRenderer( ) { val color = enterpriseService.brandColorsFlow(currentUser.userId).first()?.toArgb() ?: NotificationConfig.NOTIFICATION_ACCENT_COLOR + val numberOfAccounts = sessionStore.getAllSessions().size + val notificationAccountParams = NotificationAccountParams( + user = currentUser, + color = color, + showSessionId = numberOfAccounts > 1, + ) val groupedEvents = eventsToProcess.groupByType() - val roomNotifications = notificationDataFactory.toNotifications(groupedEvents.roomEvents, currentUser, imageLoader, color) - val invitationNotifications = notificationDataFactory.toNotifications(groupedEvents.invitationEvents, color) - val simpleNotifications = notificationDataFactory.toNotifications(groupedEvents.simpleEvents, color) - val fallbackNotifications = notificationDataFactory.toNotifications(groupedEvents.fallbackEvents, color) + val roomNotifications = notificationDataFactory.toNotifications(groupedEvents.roomEvents, imageLoader, notificationAccountParams) + val invitationNotifications = notificationDataFactory.toNotifications(groupedEvents.invitationEvents, notificationAccountParams) + val simpleNotifications = notificationDataFactory.toNotifications(groupedEvents.simpleEvents, notificationAccountParams) + val fallbackNotifications = notificationDataFactory.toNotifications(groupedEvents.fallbackEvents, notificationAccountParams) val summaryNotification = notificationDataFactory.createSummaryNotification( - currentUser = currentUser, roomNotifications = roomNotifications, invitationNotifications = invitationNotifications, simpleNotifications = simpleNotifications, fallbackNotifications = fallbackNotifications, - color = color, + notificationAccountParams = notificationAccountParams, ) // Remove summary first to avoid briefly displaying it after dismissing the last notification diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt index 853e7ffdfc..79121db5d7 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt @@ -9,15 +9,14 @@ package io.element.android.libraries.push.impl.notifications import android.app.Notification import android.graphics.Bitmap -import androidx.annotation.ColorInt import coil3.ImageLoader import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.ThreadId -import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.api.notifications.NotificationBitmapLoader import io.element.android.libraries.push.impl.R +import io.element.android.libraries.push.impl.notifications.factories.NotificationAccountParams import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator import io.element.android.libraries.push.impl.notifications.factories.isSmartReplyError import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent @@ -25,13 +24,12 @@ import io.element.android.services.toolbox.api.strings.StringProvider interface RoomGroupMessageCreator { suspend fun createRoomMessage( - currentUser: MatrixUser, + notificationAccountParams: NotificationAccountParams, events: List, roomId: RoomId, threadId: ThreadId?, imageLoader: ImageLoader, existingNotification: Notification?, - @ColorInt color: Int, ): Notification } @@ -42,13 +40,12 @@ class DefaultRoomGroupMessageCreator( private val notificationCreator: NotificationCreator, ) : RoomGroupMessageCreator { override suspend fun createRoomMessage( - currentUser: MatrixUser, + notificationAccountParams: NotificationAccountParams, events: List, roomId: RoomId, threadId: ThreadId?, imageLoader: ImageLoader, existingNotification: Notification?, - @ColorInt color: Int, ): Notification { val lastKnownRoomEvent = events.last() val roomName = lastKnownRoomEvent.roomName ?: lastKnownRoomEvent.senderDisambiguatedDisplayName ?: "Room name (${roomId.value.take(8)}…)" @@ -66,8 +63,9 @@ class DefaultRoomGroupMessageCreator( val smartReplyErrors = events.filter { it.isSmartReplyError() } val roomIsDm = !roomIsGroup return notificationCreator.createMessagesListNotification( + notificationAccountParams = notificationAccountParams, RoomEventGroupInfo( - sessionId = currentUser.userId, + sessionId = notificationAccountParams.user.userId, roomId = roomId, roomDisplayName = roomName, isDm = roomIsDm, @@ -80,11 +78,9 @@ class DefaultRoomGroupMessageCreator( largeIcon = largeBitmap, lastMessageTimestamp = lastMessageTimestamp, tickerText = tickerText, - currentUser = currentUser, existingNotification = existingNotification, imageLoader = imageLoader, events = events, - color = color, ) } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt index 85947226f1..8600472c9d 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt @@ -8,22 +8,20 @@ package io.element.android.libraries.push.impl.notifications import android.app.Notification -import androidx.annotation.ColorInt import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding -import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.R +import io.element.android.libraries.push.impl.notifications.factories.NotificationAccountParams import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator import io.element.android.services.toolbox.api.strings.StringProvider interface SummaryGroupMessageCreator { fun createSummaryNotification( - currentUser: MatrixUser, + notificationAccountParams: NotificationAccountParams, roomNotifications: List, invitationNotifications: List, simpleNotifications: List, fallbackNotifications: List, - @ColorInt color: Int, ): Notification } @@ -42,12 +40,11 @@ class DefaultSummaryGroupMessageCreator( private val notificationCreator: NotificationCreator, ) : SummaryGroupMessageCreator { override fun createSummaryNotification( - currentUser: MatrixUser, + notificationAccountParams: NotificationAccountParams, roomNotifications: List, invitationNotifications: List, simpleNotifications: List, fallbackNotifications: List, - @ColorInt color: Int, ): Notification { val summaryIsNoisy = roomNotifications.any { it.shouldBing } || invitationNotifications.any { it.isNoisy } || @@ -61,11 +58,10 @@ class DefaultSummaryGroupMessageCreator( val nbEvents = roomNotifications.size + simpleNotifications.size val sumTitle = stringProvider.getQuantityString(R.plurals.notification_compat_summary_title, nbEvents, nbEvents) return notificationCreator.createSummaryListNotification( - currentUser, + notificationAccountParams = notificationAccountParams, sumTitle, noisy = summaryIsNoisy, lastMessageTimestamp = lastMessageTimestamp, - color = color, ) } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationAccountParams.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationAccountParams.kt new file mode 100644 index 0000000000..da122b6a2a --- /dev/null +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationAccountParams.kt @@ -0,0 +1,17 @@ +/* + * 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. + */ + +package io.element.android.libraries.push.impl.notifications.factories + +import androidx.annotation.ColorInt +import io.element.android.libraries.matrix.api.user.MatrixUser + +data class NotificationAccountParams( + val user: MatrixUser, + @ColorInt val color: Int, + val showSessionId: Boolean, +) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt index efe54bd3d9..411cb25db1 100755 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt @@ -47,42 +47,40 @@ interface NotificationCreator { * Create a notification for a Room. */ suspend fun createMessagesListNotification( + notificationAccountParams: NotificationAccountParams, roomInfo: RoomEventGroupInfo, threadId: ThreadId?, largeIcon: Bitmap?, lastMessageTimestamp: Long, tickerText: String, - currentUser: MatrixUser, existingNotification: Notification?, imageLoader: ImageLoader, events: List, - @ColorInt color: Int, ): Notification fun createRoomInvitationNotification( + notificationAccountParams: NotificationAccountParams, inviteNotifiableEvent: InviteNotifiableEvent, - @ColorInt color: Int, ): Notification fun createSimpleEventNotification( + notificationAccountParams: NotificationAccountParams, simpleNotifiableEvent: SimpleNotifiableEvent, - @ColorInt color: Int, ): Notification fun createFallbackNotification( + notificationAccountParams: NotificationAccountParams, fallbackNotifiableEvent: FallbackNotifiableEvent, - @ColorInt color: Int, ): Notification /** * Create the summary notification. */ fun createSummaryListNotification( - currentUser: MatrixUser, + notificationAccountParams: NotificationAccountParams, compatSummary: String, noisy: Boolean, lastMessageTimestamp: Long, - @ColorInt color: Int, ): Notification fun createDiagnosticNotification( @@ -118,16 +116,15 @@ class DefaultNotificationCreator( * Create a notification for a Room. */ override suspend fun createMessagesListNotification( + notificationAccountParams: NotificationAccountParams, roomInfo: RoomEventGroupInfo, threadId: ThreadId?, largeIcon: Bitmap?, lastMessageTimestamp: Long, tickerText: String, - currentUser: MatrixUser, existingNotification: Notification?, imageLoader: ImageLoader, events: List, - @ColorInt color: Int, ): Notification { // Build the pending intent for when the notification is clicked val eventId = events.firstOrNull()?.eventId @@ -135,7 +132,6 @@ class DefaultNotificationCreator( threadId != null -> pendingIntentFactory.createOpenThreadPendingIntent(roomInfo.sessionId, roomInfo.roomId, eventId, threadId) else -> pendingIntentFactory.createOpenRoomPendingIntent(roomInfo.sessionId, roomInfo.roomId, eventId) } - val smallIcon = CommonDrawables.ic_notification val containsMissedCall = events.any { it.type == EventType.RTC_NOTIFICATION } val channelId = if (containsMissedCall) { notificationChannels.getChannelForIncomingCall(false) @@ -172,7 +168,7 @@ class DefaultNotificationCreator( val messagingStyle = existingNotification?.let { MessagingStyle.extractMessagingStyleFromNotification(it) } ?: messagingStyleFromCurrentUser( - user = currentUser, + user = notificationAccountParams.user, imageLoader = imageLoader, roomName = roomInfo.roomDisplayName, isThread = threadId != null, @@ -187,9 +183,7 @@ class DefaultNotificationCreator( .setWhen(lastMessageTimestamp) // MESSAGING_STYLE sets title and content for API 16 and above devices. .setStyle(messagingStyle) - .setSmallIcon(smallIcon) - // Set primary color (important for Wear 2.0 Notifications). - .setColor(color) + .configureWith(notificationAccountParams) // Sets priority for 25 and below. For 26 and above, 'priority' is deprecated for // 'importance' which is set in the NotificationChannel. The integers representing // 'priority' are different from 'importance', so make sure you don't mix them. @@ -202,7 +196,7 @@ class DefaultNotificationCreator( setSound(it) } */ - setLights(color, 500, 500) + setLights(notificationAccountParams.color, 500, 500) } else { priority = NotificationCompat.PRIORITY_LOW } @@ -234,10 +228,9 @@ class DefaultNotificationCreator( } override fun createRoomInvitationNotification( + notificationAccountParams: NotificationAccountParams, inviteNotifiableEvent: InviteNotifiableEvent, - @ColorInt color: Int, ): Notification { - val smallIcon = CommonDrawables.ic_notification val channelId = notificationChannels.getChannelIdForMessage(inviteNotifiableEvent.noisy) return NotificationCompat.Builder(context, channelId) .setOnlyAlertOnce(true) @@ -245,8 +238,7 @@ class DefaultNotificationCreator( .setContentText(inviteNotifiableEvent.description.annotateForDebug(6)) .setGroup(inviteNotifiableEvent.sessionId.value) .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL) - .setSmallIcon(smallIcon) - .setColor(color) + .configureWith(notificationAccountParams) .apply { addAction(rejectInvitationActionFactory.create(inviteNotifiableEvent)) addAction(acceptInvitationActionFactory.create(inviteNotifiableEvent)) @@ -261,7 +253,7 @@ class DefaultNotificationCreator( setSound(it) } */ - setLights(color, 500, 500) + setLights(notificationAccountParams.color, 500, 500) } else { priority = NotificationCompat.PRIORITY_LOW } @@ -277,10 +269,9 @@ class DefaultNotificationCreator( } override fun createSimpleEventNotification( + notificationAccountParams: NotificationAccountParams, simpleNotifiableEvent: SimpleNotifiableEvent, - @ColorInt color: Int, ): Notification { - val smallIcon = CommonDrawables.ic_notification val channelId = notificationChannels.getChannelIdForMessage(simpleNotifiableEvent.noisy) return NotificationCompat.Builder(context, channelId) .setOnlyAlertOnce(true) @@ -288,8 +279,7 @@ class DefaultNotificationCreator( .setContentText(simpleNotifiableEvent.description.annotateForDebug(8)) .setGroup(simpleNotifiableEvent.sessionId.value) .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL) - .setSmallIcon(smallIcon) - .setColor(color) + .configureWith(notificationAccountParams) .setAutoCancel(true) .setContentIntent(pendingIntentFactory.createOpenRoomPendingIntent(simpleNotifiableEvent.sessionId, simpleNotifiableEvent.roomId, null)) .apply { @@ -301,7 +291,7 @@ class DefaultNotificationCreator( setSound(it) } */ - setLights(color, 500, 500) + setLights(notificationAccountParams.color, 500, 500) } else { priority = NotificationCompat.PRIORITY_LOW } @@ -310,10 +300,9 @@ class DefaultNotificationCreator( } override fun createFallbackNotification( + notificationAccountParams: NotificationAccountParams, fallbackNotifiableEvent: FallbackNotifiableEvent, - @ColorInt color: Int, ): Notification { - val smallIcon = CommonDrawables.ic_notification val channelId = notificationChannels.getChannelIdForMessage(false) return NotificationCompat.Builder(context, channelId) .setOnlyAlertOnce(true) @@ -321,8 +310,7 @@ class DefaultNotificationCreator( .setContentText(fallbackNotifiableEvent.description.orEmpty().annotateForDebug(8)) .setGroup(fallbackNotifiableEvent.sessionId.value) .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL) - .setSmallIcon(smallIcon) - .setColor(color) + .configureWith(notificationAccountParams) .setAutoCancel(true) .setWhen(fallbackNotifiableEvent.timestamp) // Ideally we'd use `createOpenRoomPendingIntent` here, but the broken notification might apply to an invite @@ -343,24 +331,22 @@ class DefaultNotificationCreator( * Create the summary notification. */ override fun createSummaryListNotification( - currentUser: MatrixUser, + notificationAccountParams: NotificationAccountParams, compatSummary: String, noisy: Boolean, lastMessageTimestamp: Long, - @ColorInt color: Int, ): Notification { - val smallIcon = CommonDrawables.ic_notification val channelId = notificationChannels.getChannelIdForMessage(noisy) + val userId = notificationAccountParams.user.userId return NotificationCompat.Builder(context, channelId) .setOnlyAlertOnce(true) // used in compat < N, after summary is built based on child notifications .setWhen(lastMessageTimestamp) .setCategory(NotificationCompat.CATEGORY_MESSAGE) - .setSmallIcon(smallIcon) - .setGroup(currentUser.userId.value) + .setGroup(userId.value) // set this notification as the summary for the group .setGroupSummary(true) - .setColor(color) + .configureWith(notificationAccountParams) .apply { if (noisy) { // Compat @@ -370,14 +356,14 @@ class DefaultNotificationCreator( setSound(it) } */ - setLights(color, 500, 500) + setLights(notificationAccountParams.color, 500, 500) } else { // compat priority = NotificationCompat.PRIORITY_LOW } } - .setContentIntent(pendingIntentFactory.createOpenSessionPendingIntent(currentUser.userId)) - .setDeleteIntent(pendingIntentFactory.createDismissSummaryPendingIntent(currentUser.userId)) + .setContentIntent(pendingIntentFactory.createOpenSessionPendingIntent(userId)) + .setDeleteIntent(pendingIntentFactory.createDismissSummaryPendingIntent(userId)) .build() } @@ -487,4 +473,12 @@ class DefaultNotificationCreator( } } +private fun NotificationCompat.Builder.configureWith(notificationAccountParams: NotificationAccountParams) = apply { + setSmallIcon(CommonDrawables.ic_notification) + setColor(notificationAccountParams.color) + if (notificationAccountParams.showSessionId) { + setSubText(notificationAccountParams.user.userId.value) + } +} + fun NotifiableMessageEvent.isSmartReplyError() = outGoingMessage && outGoingMessageFailed diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultBaseRoomGroupMessageCreatorTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultBaseRoomGroupMessageCreatorTest.kt index 694319c9e7..00848390b5 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultBaseRoomGroupMessageCreatorTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultBaseRoomGroupMessageCreatorTest.kt @@ -13,7 +13,6 @@ import androidx.core.app.NotificationCompat import com.google.common.truth.Truth.assertThat import io.element.android.appconfig.NotificationConfig import io.element.android.libraries.matrix.api.media.MediaSource -import io.element.android.libraries.matrix.test.A_COLOR_INT import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_TIMESTAMP import io.element.android.libraries.matrix.ui.components.aMatrixUser @@ -21,6 +20,7 @@ import io.element.android.libraries.matrix.ui.media.AVATAR_THUMBNAIL_SIZE_IN_PIX import io.element.android.libraries.matrix.ui.media.MediaRequestData import io.element.android.libraries.push.impl.notifications.factories.MARK_AS_READ_ACTION_TITLE import io.element.android.libraries.push.impl.notifications.factories.QUICK_REPLY_ACTION_TITLE +import io.element.android.libraries.push.impl.notifications.factories.aNotificationAccountParams import io.element.android.libraries.push.impl.notifications.factories.createNotificationCreator import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent import io.element.android.libraries.push.test.notifications.FakeImageLoader @@ -44,7 +44,7 @@ class DefaultBaseRoomGroupMessageCreatorTest { val sut = createRoomGroupMessageCreator() val fakeImageLoader = FakeImageLoader() val result = sut.createRoomMessage( - currentUser = aMatrixUser(), + notificationAccountParams = aNotificationAccountParams(), events = listOf( aNotifiableMessageEvent(timestamp = A_TIMESTAMP).copy( imageUriString = "aUri", @@ -54,7 +54,6 @@ class DefaultBaseRoomGroupMessageCreatorTest { imageLoader = fakeImageLoader.getImageLoader(), existingNotification = null, threadId = null, - color = A_COLOR_INT, ) assertThat(result.number).isEqualTo(1) @Suppress("DEPRECATION") @@ -68,7 +67,7 @@ class DefaultBaseRoomGroupMessageCreatorTest { val sut = createRoomGroupMessageCreator() val fakeImageLoader = FakeImageLoader() val result = sut.createRoomMessage( - currentUser = aMatrixUser(), + notificationAccountParams = aNotificationAccountParams(), events = listOf( aNotifiableMessageEvent(timestamp = A_TIMESTAMP).copy( noisy = true, @@ -78,7 +77,6 @@ class DefaultBaseRoomGroupMessageCreatorTest { imageLoader = fakeImageLoader.getImageLoader(), existingNotification = null, threadId = null, - color = A_COLOR_INT, ) @Suppress("DEPRECATION") assertThat(result.priority).isEqualTo(NotificationCompat.PRIORITY_DEFAULT) @@ -130,9 +128,11 @@ class DefaultBaseRoomGroupMessageCreatorTest { sdkIntProvider = FakeBuildVersionSdkIntProvider(api) ) val result = sut.createRoomMessage( - currentUser = aMatrixUser( - // Some user avatar - avatarUrl = A_USER_AVATAR_1, + notificationAccountParams = aNotificationAccountParams( + user = aMatrixUser( + // Some user avatar + avatarUrl = A_USER_AVATAR_1, + ) ), events = listOf( aNotifiableMessageEvent(timestamp = A_TIMESTAMP).copy( @@ -144,7 +144,6 @@ class DefaultBaseRoomGroupMessageCreatorTest { imageLoader = fakeImageLoader.getImageLoader(), existingNotification = null, threadId = null, - color = A_COLOR_INT, ) assertThat(result.number).isEqualTo(1) assertThat(fakeImageLoader.getCoilRequests()).containsExactlyElementsIn(expectedCoilRequests) @@ -155,7 +154,7 @@ class DefaultBaseRoomGroupMessageCreatorTest { val sut = createRoomGroupMessageCreator() val fakeImageLoader = FakeImageLoader() val result = sut.createRoomMessage( - currentUser = aMatrixUser(), + notificationAccountParams = aNotificationAccountParams(), events = listOf( aNotifiableMessageEvent(timestamp = A_TIMESTAMP), aNotifiableMessageEvent(timestamp = A_TIMESTAMP + 10), @@ -164,7 +163,6 @@ class DefaultBaseRoomGroupMessageCreatorTest { imageLoader = fakeImageLoader.getImageLoader(), existingNotification = null, threadId = null, - color = A_COLOR_INT, ) assertThat(result.number).isEqualTo(2) assertThat(result.`when`).isEqualTo(A_TIMESTAMP + 10) @@ -183,7 +181,7 @@ class DefaultBaseRoomGroupMessageCreatorTest { val sut = createRoomGroupMessageCreator() val fakeImageLoader = FakeImageLoader() val result = sut.createRoomMessage( - currentUser = aMatrixUser(), + notificationAccountParams = aNotificationAccountParams(), events = listOf( aNotifiableMessageEvent(timestamp = A_TIMESTAMP).copy( outGoingMessage = true, @@ -194,7 +192,6 @@ class DefaultBaseRoomGroupMessageCreatorTest { imageLoader = fakeImageLoader.getImageLoader(), existingNotification = null, threadId = null, - color = A_COLOR_INT, ) val actionTitles = result.actions?.map { it.title } assertThat(actionTitles).isEqualTo( @@ -210,7 +207,7 @@ class DefaultBaseRoomGroupMessageCreatorTest { val sut = createRoomGroupMessageCreator() val fakeImageLoader = FakeImageLoader() val result = sut.createRoomMessage( - currentUser = aMatrixUser(), + notificationAccountParams = aNotificationAccountParams(), events = listOf( aNotifiableMessageEvent(timestamp = A_TIMESTAMP).copy( roomIsDm = true, @@ -220,7 +217,6 @@ class DefaultBaseRoomGroupMessageCreatorTest { imageLoader = fakeImageLoader.getImageLoader(), existingNotification = null, threadId = null, - color = A_COLOR_INT, ) assertThat(result.number).isEqualTo(1) assertThat(result.`when`).isEqualTo(A_TIMESTAMP) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt index b2a4cc1c9b..275c1ac490 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt @@ -8,8 +8,10 @@ package io.element.android.libraries.push.impl.notifications import android.app.Notification +import androidx.compose.ui.graphics.Color import androidx.core.app.NotificationManagerCompat import com.google.common.truth.Truth.assertThat +import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.AN_EVENT_ID @@ -21,12 +23,15 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClientProvider import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.push.api.notifications.NotificationIdProvider +import io.element.android.libraries.push.impl.notifications.factories.aNotificationAccountParams import io.element.android.libraries.push.impl.notifications.fake.FakeActiveNotificationsProvider import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationCreator import io.element.android.libraries.push.impl.notifications.fake.FakeRoomGroupMessageCreator import io.element.android.libraries.push.impl.notifications.fake.FakeSummaryGroupMessageCreator import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent import io.element.android.libraries.push.test.notifications.FakeImageLoaderHolder +import io.element.android.libraries.sessionstorage.api.SessionStore +import io.element.android.libraries.sessionstorage.test.InMemorySessionStore import io.element.android.services.appnavstate.api.AppNavigationState import io.element.android.services.appnavstate.api.AppNavigationStateService import io.element.android.services.appnavstate.api.NavigationState @@ -64,8 +69,8 @@ class DefaultNotificationDrawerManagerTest { // For now just call all the API. Later, add more valuable tests. val matrixUser = aMatrixUser(id = A_SESSION_ID.value, displayName = "alice", avatarUrl = "mxc://data") val mockRoomGroupMessageCreator = FakeRoomGroupMessageCreator( - createRoomMessageResult = lambdaRecorder { user, _, roomId, _, _, existingNotification -> - assertThat(user).isEqualTo(matrixUser) + createRoomMessageResult = lambdaRecorder { notificationAccountParams, _, roomId, _, _, existingNotification -> + assertThat(notificationAccountParams.user).isEqualTo(matrixUser) assertThat(roomId).isEqualTo(A_ROOM_ID) assertThat(existingNotification).isNull() Notification() @@ -128,6 +133,9 @@ class DefaultNotificationDrawerManagerTest { val defaultNotificationDrawerManager = createDefaultNotificationDrawerManager( matrixClientProvider = matrixClientProvider, roomGroupMessageCreator = messageCreator, + enterpriseService = FakeEnterpriseService( + initialBrandColor = Color.Red, + ) ) // Gets a display name from MatrixClient.getUserProfile matrixClient.givenGetProfileResult(A_SESSION_ID, Result.success(aMatrixUser(id = A_SESSION_ID.value, displayName = "alice"))) @@ -144,15 +152,29 @@ class DefaultNotificationDrawerManagerTest { messageCreator.createRoomMessageResult.assertions() .isCalledExactly(3) .withSequence( - listOf(value(aMatrixUser(id = A_SESSION_ID.value, displayName = "alice")), any(), any(), any(), any(), any()), - listOf(value(aMatrixUser(id = A_SESSION_ID.value, displayName = A_SESSION_ID.value)), any(), any(), any(), any(), any()), listOf( - value(aMatrixUser(id = A_SESSION_ID.value, displayName = A_SESSION_ID.value, avatarUrl = AN_AVATAR_URL)), + value(aNotificationAccountParams(user = aMatrixUser(id = A_SESSION_ID.value, displayName = "alice"))), + any(), + any(), + any(), + any(), + any(), + ), + listOf( + value(aNotificationAccountParams(user = aMatrixUser(id = A_SESSION_ID.value, displayName = A_SESSION_ID.value))), + any(), + any(), + any(), + any(), + any(), + ), + listOf( + value(aNotificationAccountParams(user = aMatrixUser(id = A_SESSION_ID.value, displayName = A_SESSION_ID.value, avatarUrl = AN_AVATAR_URL))), + any(), any(), any(), any(), any(), - any() ), ) @@ -194,6 +216,8 @@ class DefaultNotificationDrawerManagerTest { summaryGroupMessageCreator: SummaryGroupMessageCreator = FakeSummaryGroupMessageCreator(), activeNotificationsProvider: FakeActiveNotificationsProvider = FakeActiveNotificationsProvider(), matrixClientProvider: FakeMatrixClientProvider = FakeMatrixClientProvider(), + sessionStore: SessionStore = InMemorySessionStore(), + enterpriseService: EnterpriseService = FakeEnterpriseService(), ): DefaultNotificationDrawerManager { val context = RuntimeEnvironment.getApplication() return DefaultNotificationDrawerManager( @@ -207,7 +231,8 @@ class DefaultNotificationDrawerManagerTest { activeNotificationsProvider = activeNotificationsProvider, stringProvider = FakeStringProvider(), ), - enterpriseService = FakeEnterpriseService(), + enterpriseService = enterpriseService, + sessionStore = sessionStore, ), appNavigationStateService = appNavigationStateService, coroutineScope = this, diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultOnMissedCallNotificationHandlerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultOnMissedCallNotificationHandlerTest.kt index 8b668a7f77..e5a4b01043 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultOnMissedCallNotificationHandlerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultOnMissedCallNotificationHandlerTest.kt @@ -7,7 +7,6 @@ package io.element.android.libraries.push.impl.notifications -import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID @@ -18,7 +17,6 @@ import io.element.android.libraries.matrix.test.notification.FakeNotificationSer import io.element.android.libraries.matrix.test.notification.aNotificationData import io.element.android.libraries.push.impl.notifications.fake.FakeActiveNotificationsProvider import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDataFactory -import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDisplayer import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent import io.element.android.libraries.push.test.notifications.FakeCallNotificationEventResolver import io.element.android.libraries.push.test.notifications.FakeImageLoaderHolder @@ -53,10 +51,8 @@ class DefaultOnMissedCallNotificationHandlerTest { matrixClientProvider = matrixClientProvider, defaultNotificationDrawerManager = DefaultNotificationDrawerManager( notificationManager = mockk(relaxed = true), - notificationRenderer = NotificationRenderer( - notificationDisplayer = FakeNotificationDisplayer(), + notificationRenderer = createNotificationRenderer( notificationDataFactory = dataFactory, - enterpriseService = FakeEnterpriseService(), ), appNavigationStateService = FakeAppNavigationStateService(), coroutineScope = backgroundScope, diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultSummaryGroupMessageCreatorTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultSummaryGroupMessageCreatorTest.kt index e34ea0848c..f701dae6c7 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultSummaryGroupMessageCreatorTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultSummaryGroupMessageCreatorTest.kt @@ -10,9 +10,8 @@ package io.element.android.libraries.push.impl.notifications import android.app.Notification import androidx.core.app.NotificationCompat import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.matrix.test.A_COLOR_INT import io.element.android.libraries.matrix.test.A_ROOM_ID -import io.element.android.libraries.matrix.ui.components.aMatrixUser +import io.element.android.libraries.push.impl.notifications.factories.aNotificationAccountParams import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationCreator import io.element.android.services.toolbox.test.strings.FakeStringProvider import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP @@ -34,7 +33,7 @@ class DefaultSummaryGroupMessageCreatorTest { ) val result = summaryCreator.createSummaryNotification( - currentUser = aMatrixUser(), + notificationAccountParams = aNotificationAccountParams(), roomNotifications = listOf( RoomNotification( notification = Notification(), @@ -49,12 +48,11 @@ class DefaultSummaryGroupMessageCreatorTest { invitationNotifications = emptyList(), simpleNotifications = emptyList(), fallbackNotifications = emptyList(), - color = A_COLOR_INT, ) notificationCreator.createSummaryListNotificationResult.assertions() .isCalledOnce() - .with(any(), nonNull(), any(), any()) + .with(any(), any(), nonNull(), any(), any()) // Set from the events included @Suppress("DEPRECATION") diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt index f563106c14..2b11ce949b 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt @@ -11,9 +11,9 @@ import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.test.AN_EVENT_ID -import io.element.android.libraries.matrix.test.A_COLOR_INT import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.push.impl.notifications.factories.aNotificationAccountParams import io.element.android.libraries.push.impl.notifications.fake.FakeActiveNotificationsProvider import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationCreator import io.element.android.libraries.push.impl.notifications.fake.FakeRoomGroupMessageCreator @@ -51,10 +51,13 @@ class NotificationDataFactoryTest { @Test fun `given a room invitation when mapping to notification then it's added`() = testWith(notificationDataFactory) { - val expectedNotification = notificationCreator.createRoomInvitationNotificationResult(AN_INVITATION_EVENT) + val expectedNotification = notificationCreator.createRoomInvitationNotificationResult( + aNotificationAccountParams(), + AN_INVITATION_EVENT, + ) val roomInvitation = listOf(AN_INVITATION_EVENT) - val result = toNotifications(roomInvitation, A_COLOR_INT) + val result = toNotifications(roomInvitation, aNotificationAccountParams()) assertThat(result).isEqualTo( listOf( @@ -71,10 +74,13 @@ class NotificationDataFactoryTest { @Test fun `given a simple event when mapping to notification then it's added`() = testWith(notificationDataFactory) { - val expectedNotification = notificationCreator.createRoomInvitationNotificationResult(AN_INVITATION_EVENT) + val expectedNotification = notificationCreator.createRoomInvitationNotificationResult( + aNotificationAccountParams(), + AN_INVITATION_EVENT, + ) val roomInvitation = listOf(A_SIMPLE_EVENT) - val result = toNotifications(roomInvitation, A_COLOR_INT) + val result = toNotifications(roomInvitation, aNotificationAccountParams()) assertThat(result).isEqualTo( listOf( @@ -94,13 +100,14 @@ class NotificationDataFactoryTest { val events = listOf(A_MESSAGE_EVENT) val expectedNotification = RoomNotification( notification = fakeRoomGroupMessageCreator.createRoomMessage( - currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), + notificationAccountParams = aNotificationAccountParams( + user = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), + ), events = events, roomId = A_ROOM_ID, threadId = null, imageLoader = FakeImageLoader().getImageLoader(), existingNotification = null, - color = A_COLOR_INT, ), roomId = A_ROOM_ID, summaryLine = "A room name: Bob Hello world!", @@ -113,10 +120,11 @@ class NotificationDataFactoryTest { val fakeImageLoader = FakeImageLoader() val result = toNotifications( + notificationAccountParams = aNotificationAccountParams( + user = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), + ), messages = roomWithMessage, - currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), imageLoader = fakeImageLoader.getImageLoader(), - color = A_COLOR_INT, ) assertThat(result.size).isEqualTo(1) @@ -130,10 +138,11 @@ class NotificationDataFactoryTest { val fakeImageLoader = FakeImageLoader() val result = toNotifications( + notificationAccountParams = aNotificationAccountParams( + user = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), + ), messages = redactedRoom, - currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), imageLoader = fakeImageLoader.getImageLoader(), - color = A_COLOR_INT, ) assertThat(result).isEmpty() @@ -151,13 +160,14 @@ class NotificationDataFactoryTest { val withRedactedRemoved = listOf(A_MESSAGE_EVENT.copy(eventId = EventId("\$not-redacted"))) val expectedNotification = RoomNotification( notification = fakeRoomGroupMessageCreator.createRoomMessage( - currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), + notificationAccountParams = aNotificationAccountParams( + user = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), + ), events = withRedactedRemoved, roomId = A_ROOM_ID, threadId = null, imageLoader = FakeImageLoader().getImageLoader(), existingNotification = null, - color = A_COLOR_INT, ), roomId = A_ROOM_ID, summaryLine = "A room name: Bob Hello world!", @@ -169,10 +179,11 @@ class NotificationDataFactoryTest { val fakeImageLoader = FakeImageLoader() val result = toNotifications( + notificationAccountParams = aNotificationAccountParams( + user = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), + ), messages = roomWithRedactedMessage, - currentUser = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), imageLoader = fakeImageLoader.getImageLoader(), - color = A_COLOR_INT, ) assertThat(result.size).isEqualTo(1) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt index 589d7876f5..1026dca849 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt @@ -7,6 +7,7 @@ package io.element.android.libraries.push.impl.notifications +import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.test.AN_EVENT_ID @@ -15,6 +16,7 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.push.api.notifications.NotificationIdProvider import io.element.android.libraries.push.impl.notifications.fake.FakeActiveNotificationsProvider import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationCreator +import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDataFactory import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDisplayer import io.element.android.libraries.push.impl.notifications.fake.FakeRoomGroupMessageCreator import io.element.android.libraries.push.impl.notifications.fake.FakeSummaryGroupMessageCreator @@ -24,6 +26,8 @@ import io.element.android.libraries.push.impl.notifications.fixtures.aSimpleNoti import io.element.android.libraries.push.impl.notifications.fixtures.anInviteNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.element.android.libraries.push.test.notifications.FakeImageLoader +import io.element.android.libraries.sessionstorage.api.SessionStore +import io.element.android.libraries.sessionstorage.test.InMemorySessionStore import io.element.android.services.toolbox.test.strings.FakeStringProvider import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value @@ -56,10 +60,9 @@ class NotificationRendererTest { ) private val notificationIdProvider = NotificationIdProvider - private val notificationRenderer = NotificationRenderer( + private val notificationRenderer = createNotificationRenderer( notificationDisplayer = notificationDisplayer, notificationDataFactory = notificationDataFactory, - enterpriseService = FakeEnterpriseService(), ) @Test @@ -83,7 +86,7 @@ class NotificationRendererTest { @Test fun `given a simple notification is added when rendering then show the simple notification and update summary`() = runTest { - notificationCreator.createSimpleNotificationResult = lambdaRecorder { _ -> ONE_SHOT_NOTIFICATION.copy(key = AN_EVENT_ID.value).notification } + notificationCreator.createSimpleNotificationResult = lambdaRecorder { _, _ -> ONE_SHOT_NOTIFICATION.copy(key = AN_EVENT_ID.value).notification } renderEventsAsNotifications(listOf(aSimpleNotifiableEvent(eventId = AN_EVENT_ID))) @@ -95,7 +98,7 @@ class NotificationRendererTest { @Test fun `given an invitation notification is added when rendering then show the invitation notification and update summary`() = runTest { - notificationCreator.createRoomInvitationNotificationResult = lambdaRecorder { _ -> ONE_SHOT_NOTIFICATION.copy(key = AN_EVENT_ID.value).notification } + notificationCreator.createRoomInvitationNotificationResult = lambdaRecorder { _, _ -> ONE_SHOT_NOTIFICATION.copy(key = AN_EVENT_ID.value).notification } renderEventsAsNotifications(listOf(anInviteNotifiableEvent())) @@ -114,3 +117,15 @@ class NotificationRendererTest { ) } } + +fun createNotificationRenderer( + notificationDisplayer: NotificationDisplayer = FakeNotificationDisplayer(), + notificationDataFactory: NotificationDataFactory = FakeNotificationDataFactory(), + enterpriseService: EnterpriseService = FakeEnterpriseService(), + sessionStore: SessionStore = InMemorySessionStore(), +) = NotificationRenderer( + notificationDisplayer = notificationDisplayer, + notificationDataFactory = notificationDataFactory, + enterpriseService = enterpriseService, + sessionStore = sessionStore, +) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt index 045cc0492d..df9f106972 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt @@ -65,6 +65,7 @@ class DefaultNotificationCreatorTest { fun `test createFallbackNotification`() { val sut = createNotificationCreator() val result = sut.createFallbackNotification( + notificationAccountParams = aNotificationAccountParams(), FallbackNotifiableEvent( sessionId = A_SESSION_ID, roomId = A_ROOM_ID, @@ -77,7 +78,6 @@ class DefaultNotificationCreatorTest { timestamp = A_FAKE_TIMESTAMP, cause = null, ), - color = A_COLOR_INT, ) result.commonAssertions( expectedCategory = null, @@ -88,6 +88,7 @@ class DefaultNotificationCreatorTest { fun `test createSimpleEventNotification`() { val sut = createNotificationCreator() val result = sut.createSimpleEventNotification( + notificationAccountParams = aNotificationAccountParams(), SimpleNotifiableEvent( sessionId = A_SESSION_ID, roomId = A_ROOM_ID, @@ -103,7 +104,6 @@ class DefaultNotificationCreatorTest { isRedacted = false, isUpdated = false, ), - color = A_COLOR_INT, ) result.commonAssertions( expectedCategory = null, @@ -114,6 +114,7 @@ class DefaultNotificationCreatorTest { fun `test createSimpleEventNotification noisy`() { val sut = createNotificationCreator() val result = sut.createSimpleEventNotification( + notificationAccountParams = aNotificationAccountParams(), SimpleNotifiableEvent( sessionId = A_SESSION_ID, roomId = A_ROOM_ID, @@ -129,7 +130,6 @@ class DefaultNotificationCreatorTest { isRedacted = false, isUpdated = false, ), - color = A_COLOR_INT, ) result.commonAssertions( expectedCategory = null, @@ -140,6 +140,7 @@ class DefaultNotificationCreatorTest { fun `test createRoomInvitationNotification`() { val sut = createNotificationCreator() val result = sut.createRoomInvitationNotification( + notificationAccountParams = aNotificationAccountParams(), InviteNotifiableEvent( sessionId = A_SESSION_ID, roomId = A_ROOM_ID, @@ -156,7 +157,6 @@ class DefaultNotificationCreatorTest { isUpdated = false, roomName = "roomName", ), - color = A_COLOR_INT, ) result.commonAssertions( expectedCategory = null, @@ -174,6 +174,7 @@ class DefaultNotificationCreatorTest { fun `test createRoomInvitationNotification noisy`() { val sut = createNotificationCreator() val result = sut.createRoomInvitationNotification( + notificationAccountParams = aNotificationAccountParams(), InviteNotifiableEvent( sessionId = A_SESSION_ID, roomId = A_ROOM_ID, @@ -190,7 +191,6 @@ class DefaultNotificationCreatorTest { isUpdated = false, roomName = "roomName", ), - color = A_COLOR_INT, ) result.commonAssertions( expectedCategory = null, @@ -202,11 +202,10 @@ class DefaultNotificationCreatorTest { val sut = createNotificationCreator() val matrixUser = aMatrixUser() val result = sut.createSummaryListNotification( - currentUser = matrixUser, + notificationAccountParams = aNotificationAccountParams(user = matrixUser), compatSummary = "compatSummary", noisy = false, lastMessageTimestamp = 123_456L, - color = A_COLOR_INT, ) result.commonAssertions( expectedGroup = matrixUser.userId.value, @@ -218,11 +217,10 @@ class DefaultNotificationCreatorTest { val sut = createNotificationCreator() val matrixUser = aMatrixUser() val result = sut.createSummaryListNotification( - currentUser = matrixUser, + notificationAccountParams = aNotificationAccountParams(user = matrixUser), compatSummary = "compatSummary", noisy = true, lastMessageTimestamp = 123_456L, - color = A_COLOR_INT, ) result.commonAssertions( expectedGroup = matrixUser.userId.value, @@ -232,8 +230,8 @@ class DefaultNotificationCreatorTest { @Test fun `test createMessagesListNotification`() = runTest { val sut = createNotificationCreator() - aMatrixUser() val result = sut.createMessagesListNotification( + notificationAccountParams = aNotificationAccountParams(), roomInfo = RoomEventGroupInfo( sessionId = A_SESSION_ID, roomId = A_ROOM_ID, @@ -247,11 +245,9 @@ class DefaultNotificationCreatorTest { largeIcon = null, lastMessageTimestamp = 123_456L, tickerText = "tickerText", - currentUser = aMatrixUser(), existingNotification = null, imageLoader = FakeImageLoader().getImageLoader(), events = listOf(aNotifiableMessageEvent()), - color = A_COLOR_INT, ) result.commonAssertions() } @@ -259,8 +255,8 @@ class DefaultNotificationCreatorTest { @Test fun `test createMessagesListNotification should bing and thread`() = runTest { val sut = createNotificationCreator() - aMatrixUser() val result = sut.createMessagesListNotification( + notificationAccountParams = aNotificationAccountParams(), roomInfo = RoomEventGroupInfo( sessionId = A_SESSION_ID, roomId = A_ROOM_ID, @@ -274,11 +270,9 @@ class DefaultNotificationCreatorTest { largeIcon = null, lastMessageTimestamp = 123_456L, tickerText = "tickerText", - currentUser = aMatrixUser(), existingNotification = null, imageLoader = FakeImageLoader().getImageLoader(), events = listOf(aNotifiableMessageEvent()), - color = A_COLOR_INT, ) result.commonAssertions() } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationAccountParams.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationAccountParams.kt new file mode 100644 index 0000000000..eca910b07e --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationAccountParams.kt @@ -0,0 +1,23 @@ +/* + * 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. + */ + +package io.element.android.libraries.push.impl.notifications.factories + +import androidx.annotation.ColorInt +import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.test.A_COLOR_INT +import io.element.android.libraries.matrix.ui.components.aMatrixUser + +fun aNotificationAccountParams( + user: MatrixUser = aMatrixUser(), + @ColorInt color: Int = A_COLOR_INT, + showSessionId: Boolean = false, +) = NotificationAccountParams( + user = user, + color = color, + showSessionId = showSessionId, +) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationCreator.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationCreator.kt index 1cc4468b15..3a4acb757f 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationCreator.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationCreator.kt @@ -12,81 +12,84 @@ import android.graphics.Bitmap import androidx.annotation.ColorInt import coil3.ImageLoader import io.element.android.libraries.matrix.api.core.ThreadId -import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.notifications.RoomEventGroupInfo +import io.element.android.libraries.push.impl.notifications.factories.NotificationAccountParams import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator import io.element.android.libraries.push.impl.notifications.fixtures.A_NOTIFICATION import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent -import io.element.android.tests.testutils.lambda.LambdaFourParamsRecorder +import io.element.android.tests.testutils.lambda.LambdaFiveParamsRecorder import io.element.android.tests.testutils.lambda.LambdaListAnyParamsRecorder -import io.element.android.tests.testutils.lambda.LambdaNoParamRecorder import io.element.android.tests.testutils.lambda.LambdaOneParamRecorder +import io.element.android.tests.testutils.lambda.LambdaTwoParamsRecorder import io.element.android.tests.testutils.lambda.lambdaAnyRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder class FakeNotificationCreator( var createMessagesListNotificationResult: LambdaListAnyParamsRecorder = lambdaAnyRecorder { A_NOTIFICATION }, - var createRoomInvitationNotificationResult: LambdaOneParamRecorder = lambdaRecorder { _ -> A_NOTIFICATION }, - var createSimpleNotificationResult: LambdaOneParamRecorder = lambdaRecorder { _ -> A_NOTIFICATION }, - var createFallbackNotificationResult: LambdaOneParamRecorder = lambdaRecorder { _ -> A_NOTIFICATION }, - var createSummaryListNotificationResult: LambdaFourParamsRecorder = - lambdaRecorder { _, _, _, _ -> A_NOTIFICATION }, - var createDiagnosticNotificationResult: LambdaNoParamRecorder = lambdaRecorder { A_NOTIFICATION }, + var createRoomInvitationNotificationResult: LambdaTwoParamsRecorder = + lambdaRecorder { _, _ -> A_NOTIFICATION }, + var createSimpleNotificationResult: LambdaTwoParamsRecorder = + lambdaRecorder { _, _ -> A_NOTIFICATION }, + var createFallbackNotificationResult: LambdaTwoParamsRecorder = + lambdaRecorder { _, _ -> A_NOTIFICATION }, + var createSummaryListNotificationResult: LambdaFiveParamsRecorder< + NotificationAccountParams, String, Boolean, Long, NotificationAccountParams, Notification + > = lambdaRecorder { _, _, _, _, _ -> A_NOTIFICATION }, + var createDiagnosticNotificationResult: LambdaOneParamRecorder = + lambdaRecorder { _ -> A_NOTIFICATION }, ) : NotificationCreator { override suspend fun createMessagesListNotification( + notificationAccountParams: NotificationAccountParams, roomInfo: RoomEventGroupInfo, threadId: ThreadId?, largeIcon: Bitmap?, lastMessageTimestamp: Long, tickerText: String, - currentUser: MatrixUser, existingNotification: Notification?, imageLoader: ImageLoader, events: List, - @ColorInt color: Int, ): Notification { return createMessagesListNotificationResult( - listOf(roomInfo, threadId, largeIcon, lastMessageTimestamp, tickerText, currentUser, existingNotification, imageLoader, events) + listOf(notificationAccountParams, roomInfo, threadId, largeIcon, lastMessageTimestamp, tickerText, existingNotification, imageLoader, events) ) } override fun createRoomInvitationNotification( + notificationAccountParams: NotificationAccountParams, inviteNotifiableEvent: InviteNotifiableEvent, - @ColorInt color: Int, ): Notification { - return createRoomInvitationNotificationResult(inviteNotifiableEvent) + return createRoomInvitationNotificationResult(notificationAccountParams, inviteNotifiableEvent) } override fun createSimpleEventNotification( + notificationAccountParams: NotificationAccountParams, simpleNotifiableEvent: SimpleNotifiableEvent, - @ColorInt color: Int, ): Notification { - return createSimpleNotificationResult(simpleNotifiableEvent) + return createSimpleNotificationResult(notificationAccountParams, simpleNotifiableEvent) } override fun createFallbackNotification( + notificationAccountParams: NotificationAccountParams, fallbackNotifiableEvent: FallbackNotifiableEvent, - @ColorInt color: Int, ): Notification { - return createFallbackNotificationResult(fallbackNotifiableEvent) + return createFallbackNotificationResult(notificationAccountParams, fallbackNotifiableEvent) } override fun createSummaryListNotification( - currentUser: MatrixUser, + notificationAccountParams: NotificationAccountParams, compatSummary: String, noisy: Boolean, lastMessageTimestamp: Long, - @ColorInt color: Int, ): Notification { - return createSummaryListNotificationResult(currentUser, compatSummary, noisy, lastMessageTimestamp) + return createSummaryListNotificationResult(notificationAccountParams, compatSummary, noisy, lastMessageTimestamp, notificationAccountParams) } override fun createDiagnosticNotification( @ColorInt color: Int, ): Notification { - return createDiagnosticNotificationResult() + return createDiagnosticNotificationResult(color) } } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationDataFactory.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationDataFactory.kt index 9a0a5fe7ef..a897dbfc09 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationDataFactory.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationDataFactory.kt @@ -7,13 +7,12 @@ package io.element.android.libraries.push.impl.notifications.fake -import androidx.annotation.ColorInt import coil3.ImageLoader -import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.notifications.NotificationDataFactory import io.element.android.libraries.push.impl.notifications.OneShotNotification import io.element.android.libraries.push.impl.notifications.RoomNotification import io.element.android.libraries.push.impl.notifications.SummaryNotification +import io.element.android.libraries.push.impl.notifications.factories.NotificationAccountParams import io.element.android.libraries.push.impl.notifications.fixtures.A_NOTIFICATION import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent @@ -25,14 +24,15 @@ import io.element.android.tests.testutils.lambda.LambdaThreeParamsRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder class FakeNotificationDataFactory( - var messageEventToNotificationsResult: LambdaThreeParamsRecorder, MatrixUser, ImageLoader, List> = - lambdaRecorder { _, _, _ -> emptyList() }, + var messageEventToNotificationsResult: LambdaThreeParamsRecorder< + List, ImageLoader, NotificationAccountParams, List + > = lambdaRecorder { _, _, _ -> emptyList() }, var summaryToNotificationsResult: LambdaFiveParamsRecorder< - MatrixUser, List, List, List, List, + NotificationAccountParams, SummaryNotification > = lambdaRecorder { _, _, _, _, _ -> SummaryNotification.Update(A_NOTIFICATION) }, var inviteToNotificationsResult: LambdaOneParamRecorder, List> = lambdaRecorder { _ -> emptyList() }, @@ -42,18 +42,17 @@ class FakeNotificationDataFactory( ) : NotificationDataFactory { override suspend fun toNotifications( messages: List, - currentUser: MatrixUser, imageLoader: ImageLoader, - @ColorInt color: Int, + notificationAccountParams: NotificationAccountParams, ): List { - return messageEventToNotificationsResult(messages, currentUser, imageLoader) + return messageEventToNotificationsResult(messages, imageLoader, notificationAccountParams) } @JvmName("toNotificationInvites") @Suppress("INAPPLICABLE_JVM_NAME") override fun toNotifications( invites: List, - @ColorInt color: Int, + notificationAccountParams: NotificationAccountParams, ): List { return inviteToNotificationsResult(invites) } @@ -62,7 +61,7 @@ class FakeNotificationDataFactory( @Suppress("INAPPLICABLE_JVM_NAME") override fun toNotifications( simpleEvents: List, - @ColorInt color: Int, + notificationAccountParams: NotificationAccountParams, ): List { return simpleEventToNotificationsResult(simpleEvents) } @@ -71,25 +70,24 @@ class FakeNotificationDataFactory( @Suppress("INAPPLICABLE_JVM_NAME") override fun toNotifications( fallback: List, - @ColorInt color: Int, + notificationAccountParams: NotificationAccountParams, ): List { return fallbackEventToNotificationsResult(fallback) } override fun createSummaryNotification( - currentUser: MatrixUser, roomNotifications: List, invitationNotifications: List, simpleNotifications: List, fallbackNotifications: List, - @ColorInt color: Int, + notificationAccountParams: NotificationAccountParams, ): SummaryNotification { return summaryToNotificationsResult( - currentUser, roomNotifications, invitationNotifications, simpleNotifications, fallbackNotifications, + notificationAccountParams, ) } } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt index 351300937b..6bb5b63977 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt @@ -8,12 +8,11 @@ package io.element.android.libraries.push.impl.notifications.fake import android.app.Notification -import androidx.annotation.ColorInt import coil3.ImageLoader import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.ThreadId -import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.notifications.RoomGroupMessageCreator +import io.element.android.libraries.push.impl.notifications.factories.NotificationAccountParams import io.element.android.libraries.push.impl.notifications.fixtures.A_NOTIFICATION import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent import io.element.android.tests.testutils.lambda.LambdaSixParamsRecorder @@ -22,18 +21,18 @@ import io.element.android.tests.testutils.lambda.lambdaRecorder // We just can't make the param types fit @Suppress("MaxLineLength", "ktlint:standard:max-line-length", "ktlint:standard:parameter-wrapping") class FakeRoomGroupMessageCreator( - var createRoomMessageResult: LambdaSixParamsRecorder, RoomId, ThreadId?, ImageLoader, Notification?, Notification> = - lambdaRecorder { _, _, _, _, _, _ -> A_NOTIFICATION } + var createRoomMessageResult: LambdaSixParamsRecorder< + NotificationAccountParams, List, RoomId, ThreadId?, ImageLoader, Notification?, Notification + > = lambdaRecorder { _, _, _, _, _, _ -> A_NOTIFICATION } ) : RoomGroupMessageCreator { override suspend fun createRoomMessage( - currentUser: MatrixUser, + notificationAccountParams: NotificationAccountParams, events: List, roomId: RoomId, threadId: ThreadId?, imageLoader: ImageLoader, existingNotification: Notification?, - @ColorInt color: Int, ): Notification { - return createRoomMessageResult(currentUser, events, roomId, threadId, imageLoader, existingNotification) + return createRoomMessageResult(notificationAccountParams, events, roomId, threadId, imageLoader, existingNotification) } } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeSummaryGroupMessageCreator.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeSummaryGroupMessageCreator.kt index bc8a5515c9..8a1e8bb4ed 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeSummaryGroupMessageCreator.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeSummaryGroupMessageCreator.kt @@ -8,30 +8,28 @@ package io.element.android.libraries.push.impl.notifications.fake import android.app.Notification -import androidx.annotation.ColorInt -import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.notifications.OneShotNotification import io.element.android.libraries.push.impl.notifications.RoomNotification import io.element.android.libraries.push.impl.notifications.SummaryGroupMessageCreator +import io.element.android.libraries.push.impl.notifications.factories.NotificationAccountParams import io.element.android.libraries.push.impl.notifications.fixtures.A_NOTIFICATION import io.element.android.tests.testutils.lambda.LambdaFiveParamsRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder class FakeSummaryGroupMessageCreator( var createSummaryNotificationResult: LambdaFiveParamsRecorder< - MatrixUser, List, List, List, List, Notification> = + NotificationAccountParams, List, List, List, List, Notification> = lambdaRecorder { _, _, _, _, _ -> A_NOTIFICATION } ) : SummaryGroupMessageCreator { override fun createSummaryNotification( - currentUser: MatrixUser, + notificationAccountParams: NotificationAccountParams, roomNotifications: List, invitationNotifications: List, simpleNotifications: List, fallbackNotifications: List, - @ColorInt color: Int, ): Notification { return createSummaryNotificationResult( - currentUser, + notificationAccountParams, roomNotifications, invitationNotifications, simpleNotifications, From 7d7ea5d67c35bc7996b71fedb5b562fbc1710c9d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Sun, 26 Oct 2025 08:49:48 +0100 Subject: [PATCH 002/216] NotificationDataFactory: improve API --- .../notifications/NotificationDataFactory.kt | 32 ++++++--------- .../notifications/NotificationRenderer.kt | 16 ++++++-- .../NotificationDataFactoryTest.kt | 41 ++++++------------- .../fake/FakeNotificationDataFactory.kt | 20 ++++----- 4 files changed, 45 insertions(+), 64 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactory.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactory.kt index 329d34d5f5..25cac5922d 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactory.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactory.kt @@ -28,30 +28,26 @@ import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiab import io.element.android.services.toolbox.api.strings.StringProvider interface NotificationDataFactory { - suspend fun toNotifications( - messages: List, + suspend fun List.toNotifications( imageLoader: ImageLoader, notificationAccountParams: NotificationAccountParams, ): List @JvmName("toNotificationInvites") @Suppress("INAPPLICABLE_JVM_NAME") - fun toNotifications( - invites: List, + fun List.toNotifications( notificationAccountParams: NotificationAccountParams, ): List @JvmName("toNotificationSimpleEvents") @Suppress("INAPPLICABLE_JVM_NAME") - fun toNotifications( - simpleEvents: List, + fun List.toNotifications( notificationAccountParams: NotificationAccountParams, ): List @JvmName("toNotificationFallbackEvents") @Suppress("INAPPLICABLE_JVM_NAME") - fun toNotifications( - fallback: List, + fun List.toNotifications( notificationAccountParams: NotificationAccountParams, ): List @@ -72,12 +68,11 @@ class DefaultNotificationDataFactory( private val activeNotificationsProvider: ActiveNotificationsProvider, private val stringProvider: StringProvider, ) : NotificationDataFactory { - override suspend fun toNotifications( - messages: List, + override suspend fun List.toNotifications( imageLoader: ImageLoader, notificationAccountParams: NotificationAccountParams, ): List { - val messagesToDisplay = messages.filterNot { it.canNotBeDisplayed() } + val messagesToDisplay = filterNot { it.canNotBeDisplayed() } .groupBy { it.roomId } return messagesToDisplay.flatMap { (roomId, events) -> val roomName = events.lastOrNull()?.roomName ?: roomId.value @@ -114,11 +109,10 @@ class DefaultNotificationDataFactory( @JvmName("toNotificationInvites") @Suppress("INAPPLICABLE_JVM_NAME") - override fun toNotifications( - invites: List, + override fun List.toNotifications( notificationAccountParams: NotificationAccountParams, ): List { - return invites.map { event -> + return map { event -> OneShotNotification( key = event.roomId.value, notification = notificationCreator.createRoomInvitationNotification(notificationAccountParams, event), @@ -131,11 +125,10 @@ class DefaultNotificationDataFactory( @JvmName("toNotificationSimpleEvents") @Suppress("INAPPLICABLE_JVM_NAME") - override fun toNotifications( - simpleEvents: List, + override fun List.toNotifications( notificationAccountParams: NotificationAccountParams, ): List { - return simpleEvents.map { event -> + return map { event -> OneShotNotification( key = event.eventId.value, notification = notificationCreator.createSimpleEventNotification(notificationAccountParams, event), @@ -148,11 +141,10 @@ class DefaultNotificationDataFactory( @JvmName("toNotificationFallbackEvents") @Suppress("INAPPLICABLE_JVM_NAME") - override fun toNotifications( - fallback: List, + override fun List.toNotifications( notificationAccountParams: NotificationAccountParams, ): List { - return fallback.map { event -> + return map { event -> OneShotNotification( key = event.eventId.value, notification = notificationCreator.createFallbackNotification(notificationAccountParams, event), diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt index 05b72b0bcd..4d6e588726 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt @@ -51,10 +51,18 @@ class NotificationRenderer( showSessionId = numberOfAccounts > 1, ) val groupedEvents = eventsToProcess.groupByType() - val roomNotifications = notificationDataFactory.toNotifications(groupedEvents.roomEvents, imageLoader, notificationAccountParams) - val invitationNotifications = notificationDataFactory.toNotifications(groupedEvents.invitationEvents, notificationAccountParams) - val simpleNotifications = notificationDataFactory.toNotifications(groupedEvents.simpleEvents, notificationAccountParams) - val fallbackNotifications = notificationDataFactory.toNotifications(groupedEvents.fallbackEvents, notificationAccountParams) + val roomNotifications = with(notificationDataFactory) { + groupedEvents.roomEvents.toNotifications(imageLoader, notificationAccountParams) + } + val invitationNotifications = with(notificationDataFactory) { + groupedEvents.invitationEvents.toNotifications(notificationAccountParams) + } + val simpleNotifications = with(notificationDataFactory) { + groupedEvents.simpleEvents.toNotifications(notificationAccountParams) + } + val fallbackNotifications = with(notificationDataFactory) { + groupedEvents.fallbackEvents.toNotifications(notificationAccountParams) + } val summaryNotification = notificationDataFactory.createSummaryNotification( roomNotifications = roomNotifications, invitationNotifications = invitationNotifications, diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt index 2b11ce949b..c8a7e6ba33 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt @@ -55,10 +55,7 @@ class NotificationDataFactoryTest { aNotificationAccountParams(), AN_INVITATION_EVENT, ) - val roomInvitation = listOf(AN_INVITATION_EVENT) - - val result = toNotifications(roomInvitation, aNotificationAccountParams()) - + val result = listOf(AN_INVITATION_EVENT).toNotifications(aNotificationAccountParams()) assertThat(result).isEqualTo( listOf( OneShotNotification( @@ -78,19 +75,14 @@ class NotificationDataFactoryTest { aNotificationAccountParams(), AN_INVITATION_EVENT, ) - val roomInvitation = listOf(A_SIMPLE_EVENT) - - val result = toNotifications(roomInvitation, aNotificationAccountParams()) - - assertThat(result).isEqualTo( - listOf( - OneShotNotification( - notification = expectedNotification, - key = AN_EVENT_ID.value, - summaryLine = A_SIMPLE_EVENT.description, - isNoisy = A_SIMPLE_EVENT.noisy, - timestamp = AN_INVITATION_EVENT.timestamp - ) + val result = listOf(A_SIMPLE_EVENT).toNotifications(aNotificationAccountParams()) + assertThat(result).containsExactly( + OneShotNotification( + notification = expectedNotification, + key = AN_EVENT_ID.value, + summaryLine = A_SIMPLE_EVENT.description, + isNoisy = A_SIMPLE_EVENT.noisy, + timestamp = AN_INVITATION_EVENT.timestamp ) ) } @@ -116,14 +108,11 @@ class NotificationDataFactoryTest { shouldBing = events.any { it.noisy }, threadId = null, ) - val roomWithMessage = listOf(A_MESSAGE_EVENT) - val fakeImageLoader = FakeImageLoader() - val result = toNotifications( + val result = listOf(A_MESSAGE_EVENT).toNotifications( notificationAccountParams = aNotificationAccountParams( user = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), ), - messages = roomWithMessage, imageLoader = fakeImageLoader.getImageLoader(), ) @@ -134,17 +123,14 @@ class NotificationDataFactoryTest { @Test fun `given a room with only redacted events when mapping to notification then is Empty`() = testWith(notificationDataFactory) { - val redactedRoom = listOf(A_MESSAGE_EVENT.copy(isRedacted = true)) - + val redactedRoom = A_MESSAGE_EVENT.copy(isRedacted = true) val fakeImageLoader = FakeImageLoader() - val result = toNotifications( + val result = listOf(redactedRoom).toNotifications( notificationAccountParams = aNotificationAccountParams( user = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), ), - messages = redactedRoom, imageLoader = fakeImageLoader.getImageLoader(), ) - assertThat(result).isEmpty() assertThat(fakeImageLoader.getCoilRequests().size).isEqualTo(0) } @@ -178,11 +164,10 @@ class NotificationDataFactoryTest { ) val fakeImageLoader = FakeImageLoader() - val result = toNotifications( + val result = roomWithRedactedMessage.toNotifications( notificationAccountParams = aNotificationAccountParams( user = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), ), - messages = roomWithRedactedMessage, imageLoader = fakeImageLoader.getImageLoader(), ) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationDataFactory.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationDataFactory.kt index a897dbfc09..89326f5ad9 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationDataFactory.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationDataFactory.kt @@ -40,39 +40,35 @@ class FakeNotificationDataFactory( var fallbackEventToNotificationsResult: LambdaOneParamRecorder, List> = lambdaRecorder { _ -> emptyList() }, ) : NotificationDataFactory { - override suspend fun toNotifications( - messages: List, + override suspend fun List.toNotifications( imageLoader: ImageLoader, notificationAccountParams: NotificationAccountParams, ): List { - return messageEventToNotificationsResult(messages, imageLoader, notificationAccountParams) + return messageEventToNotificationsResult(this, imageLoader, notificationAccountParams) } @JvmName("toNotificationInvites") @Suppress("INAPPLICABLE_JVM_NAME") - override fun toNotifications( - invites: List, + override fun List.toNotifications( notificationAccountParams: NotificationAccountParams, ): List { - return inviteToNotificationsResult(invites) + return inviteToNotificationsResult(this) } @JvmName("toNotificationSimpleEvents") @Suppress("INAPPLICABLE_JVM_NAME") - override fun toNotifications( - simpleEvents: List, + override fun List.toNotifications( notificationAccountParams: NotificationAccountParams, ): List { - return simpleEventToNotificationsResult(simpleEvents) + return simpleEventToNotificationsResult(this) } @JvmName("toNotificationFallbackEvents") @Suppress("INAPPLICABLE_JVM_NAME") - override fun toNotifications( - fallback: List, + override fun List.toNotifications( notificationAccountParams: NotificationAccountParams, ): List { - return fallbackEventToNotificationsResult(fallback) + return fallbackEventToNotificationsResult(this) } override fun createSummaryNotification( From 7ee00a65d8d73518eb672ddd33f6fa1b61aa74c2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Sun, 26 Oct 2025 09:05:12 +0100 Subject: [PATCH 003/216] Rename OneShotNotification.key to OneShotNotification.tag for clarity. --- .../push/impl/notifications/NotificationDataFactory.kt | 8 ++++---- .../push/impl/notifications/NotificationRenderer.kt | 8 ++++---- .../impl/notifications/NotificationDataFactoryTest.kt | 4 ++-- .../push/impl/notifications/NotificationRendererTest.kt | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactory.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactory.kt index 25cac5922d..ab8406f1af 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactory.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactory.kt @@ -114,7 +114,7 @@ class DefaultNotificationDataFactory( ): List { return map { event -> OneShotNotification( - key = event.roomId.value, + tag = event.roomId.value, notification = notificationCreator.createRoomInvitationNotification(notificationAccountParams, event), summaryLine = event.description, isNoisy = event.noisy, @@ -130,7 +130,7 @@ class DefaultNotificationDataFactory( ): List { return map { event -> OneShotNotification( - key = event.eventId.value, + tag = event.eventId.value, notification = notificationCreator.createSimpleEventNotification(notificationAccountParams, event), summaryLine = event.description, isNoisy = event.noisy, @@ -146,7 +146,7 @@ class DefaultNotificationDataFactory( ): List { return map { event -> OneShotNotification( - key = event.eventId.value, + tag = event.eventId.value, notification = notificationCreator.createFallbackNotification(notificationAccountParams, event), summaryLine = event.description.orEmpty(), isNoisy = false, @@ -239,7 +239,7 @@ data class RoomNotification( data class OneShotNotification( val notification: Notification, - val key: String, + val tag: String, val summaryLine: CharSequence, val isNoisy: Boolean, val timestamp: Long, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt index 4d6e588726..46018d48f4 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt @@ -94,9 +94,9 @@ class NotificationRenderer( invitationNotifications.forEach { notificationData -> if (useCompleteNotificationFormat) { - Timber.tag(loggerTag.value).d("Updating invitation notification ${notificationData.key}") + Timber.tag(loggerTag.value).d("Updating invitation notification ${notificationData.tag}") notificationDisplayer.showNotificationMessage( - tag = notificationData.key, + tag = notificationData.tag, id = NotificationIdProvider.getRoomInvitationNotificationId(currentUser.userId), notification = notificationData.notification ) @@ -105,9 +105,9 @@ class NotificationRenderer( simpleNotifications.forEach { notificationData -> if (useCompleteNotificationFormat) { - Timber.tag(loggerTag.value).d("Updating simple notification ${notificationData.key}") + Timber.tag(loggerTag.value).d("Updating simple notification ${notificationData.tag}") notificationDisplayer.showNotificationMessage( - tag = notificationData.key, + tag = notificationData.tag, id = NotificationIdProvider.getRoomEventNotificationId(currentUser.userId), notification = notificationData.notification ) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt index c8a7e6ba33..7bc19640e6 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt @@ -60,7 +60,7 @@ class NotificationDataFactoryTest { listOf( OneShotNotification( notification = expectedNotification, - key = A_ROOM_ID.value, + tag = A_ROOM_ID.value, summaryLine = AN_INVITATION_EVENT.description, isNoisy = AN_INVITATION_EVENT.noisy, timestamp = AN_INVITATION_EVENT.timestamp @@ -79,7 +79,7 @@ class NotificationDataFactoryTest { assertThat(result).containsExactly( OneShotNotification( notification = expectedNotification, - key = AN_EVENT_ID.value, + tag = AN_EVENT_ID.value, summaryLine = A_SIMPLE_EVENT.description, isNoisy = A_SIMPLE_EVENT.noisy, timestamp = AN_INVITATION_EVENT.timestamp diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt index 1026dca849..fa34552769 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt @@ -42,7 +42,7 @@ private const val USE_COMPLETE_NOTIFICATION_FORMAT = true private val A_SUMMARY_NOTIFICATION = SummaryNotification.Update(A_NOTIFICATION) private val ONE_SHOT_NOTIFICATION = - OneShotNotification(notification = A_NOTIFICATION, key = "ignored", summaryLine = "ignored", isNoisy = false, timestamp = -1) + OneShotNotification(notification = A_NOTIFICATION, tag = "ignored", summaryLine = "ignored", isNoisy = false, timestamp = -1) @RunWith(RobolectricTestRunner::class) class NotificationRendererTest { @@ -86,7 +86,7 @@ class NotificationRendererTest { @Test fun `given a simple notification is added when rendering then show the simple notification and update summary`() = runTest { - notificationCreator.createSimpleNotificationResult = lambdaRecorder { _, _ -> ONE_SHOT_NOTIFICATION.copy(key = AN_EVENT_ID.value).notification } + notificationCreator.createSimpleNotificationResult = lambdaRecorder { _, _ -> ONE_SHOT_NOTIFICATION.copy(tag = AN_EVENT_ID.value).notification } renderEventsAsNotifications(listOf(aSimpleNotifiableEvent(eventId = AN_EVENT_ID))) @@ -98,7 +98,7 @@ class NotificationRendererTest { @Test fun `given an invitation notification is added when rendering then show the invitation notification and update summary`() = runTest { - notificationCreator.createRoomInvitationNotificationResult = lambdaRecorder { _, _ -> ONE_SHOT_NOTIFICATION.copy(key = AN_EVENT_ID.value).notification } + notificationCreator.createRoomInvitationNotificationResult = lambdaRecorder { _, _ -> ONE_SHOT_NOTIFICATION.copy(tag = AN_EVENT_ID.value).notification } renderEventsAsNotifications(listOf(anInviteNotifiableEvent())) From 6fe85dc57935228d812f0428b702300c22b08c77 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Sun, 26 Oct 2025 09:07:57 +0100 Subject: [PATCH 004/216] Use better names for API. --- .../impl/notifications/NotificationDisplayer.kt | 12 ++++++------ .../impl/notifications/NotificationRenderer.kt | 12 ++++++------ .../push/impl/push/OnRedactedEventReceived.kt | 2 +- .../impl/notifications/NotificationRendererTest.kt | 6 +++--- .../fake/FakeNotificationDisplayer.kt | 14 +++++++------- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDisplayer.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDisplayer.kt index 4348c9bfb5..deb486547b 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDisplayer.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDisplayer.kt @@ -19,8 +19,8 @@ import io.element.android.libraries.di.annotations.ApplicationContext import timber.log.Timber interface NotificationDisplayer { - fun showNotificationMessage(tag: String?, id: Int, notification: Notification): Boolean - fun cancelNotificationMessage(tag: String?, id: Int) + fun showNotification(tag: String?, id: Int, notification: Notification): Boolean + fun cancelNotification(tag: String?, id: Int) fun displayDiagnosticNotification(notification: Notification): Boolean fun dismissDiagnosticNotification() } @@ -30,7 +30,7 @@ class DefaultNotificationDisplayer( @ApplicationContext private val context: Context, private val notificationManager: NotificationManagerCompat ) : NotificationDisplayer { - override fun showNotificationMessage(tag: String?, id: Int, notification: Notification): Boolean { + override fun showNotification(tag: String?, id: Int, notification: Notification): Boolean { if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { Timber.w("Not allowed to notify.") return false @@ -40,12 +40,12 @@ class DefaultNotificationDisplayer( return true } - override fun cancelNotificationMessage(tag: String?, id: Int) { + override fun cancelNotification(tag: String?, id: Int) { notificationManager.cancel(tag, id) } override fun displayDiagnosticNotification(notification: Notification): Boolean { - return showNotificationMessage( + return showNotification( tag = "DIAGNOSTIC", id = NOTIFICATION_ID_DIAGNOSTIC, notification = notification @@ -53,7 +53,7 @@ class DefaultNotificationDisplayer( } override fun dismissDiagnosticNotification() { - cancelNotificationMessage( + cancelNotification( tag = "DIAGNOSTIC", id = NOTIFICATION_ID_DIAGNOSTIC ) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt index 46018d48f4..9f24ccecb0 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt @@ -74,7 +74,7 @@ class NotificationRenderer( // Remove summary first to avoid briefly displaying it after dismissing the last notification if (summaryNotification == SummaryNotification.Removed) { Timber.tag(loggerTag.value).d("Removing summary notification") - notificationDisplayer.cancelNotificationMessage( + notificationDisplayer.cancelNotification( tag = null, id = NotificationIdProvider.getSummaryNotificationId(currentUser.userId) ) @@ -85,7 +85,7 @@ class NotificationRenderer( roomId = notificationData.roomId, threadId = notificationData.threadId ) - notificationDisplayer.showNotificationMessage( + notificationDisplayer.showNotification( tag = tag, id = NotificationIdProvider.getRoomMessagesNotificationId(currentUser.userId), notification = notificationData.notification @@ -95,7 +95,7 @@ class NotificationRenderer( invitationNotifications.forEach { notificationData -> if (useCompleteNotificationFormat) { Timber.tag(loggerTag.value).d("Updating invitation notification ${notificationData.tag}") - notificationDisplayer.showNotificationMessage( + notificationDisplayer.showNotification( tag = notificationData.tag, id = NotificationIdProvider.getRoomInvitationNotificationId(currentUser.userId), notification = notificationData.notification @@ -106,7 +106,7 @@ class NotificationRenderer( simpleNotifications.forEach { notificationData -> if (useCompleteNotificationFormat) { Timber.tag(loggerTag.value).d("Updating simple notification ${notificationData.tag}") - notificationDisplayer.showNotificationMessage( + notificationDisplayer.showNotification( tag = notificationData.tag, id = NotificationIdProvider.getRoomEventNotificationId(currentUser.userId), notification = notificationData.notification @@ -117,7 +117,7 @@ class NotificationRenderer( // Show only the first fallback notification if (fallbackNotifications.isNotEmpty()) { Timber.tag(loggerTag.value).d("Showing fallback notification") - notificationDisplayer.showNotificationMessage( + notificationDisplayer.showNotification( tag = "FALLBACK", id = NotificationIdProvider.getFallbackNotificationId(currentUser.userId), notification = fallbackNotifications.first().notification @@ -127,7 +127,7 @@ class NotificationRenderer( // Update summary last to avoid briefly displaying it before other notifications if (summaryNotification is SummaryNotification.Update) { Timber.tag(loggerTag.value).d("Updating summary notification") - notificationDisplayer.showNotificationMessage( + notificationDisplayer.showNotification( tag = null, id = NotificationIdProvider.getSummaryNotificationId(currentUser.userId), notification = summaryNotification.notification diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/OnRedactedEventReceived.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/OnRedactedEventReceived.kt index 37d3b32c80..5be67606a4 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/OnRedactedEventReceived.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/OnRedactedEventReceived.kt @@ -73,7 +73,7 @@ class DefaultOnRedactedEventReceived( oldMessage.person ) messagingStyle.messages[messageToRedactIndex] = newMessage - notificationDisplayer.showNotificationMessage( + notificationDisplayer.showNotification( statusBarNotification.tag, statusBarNotification.id, NotificationCompat.Builder(context, notification) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt index fa34552769..cb97174cb1 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt @@ -78,7 +78,7 @@ class NotificationRendererTest { renderEventsAsNotifications(listOf(aNotifiableMessageEvent())) - notificationDisplayer.showNotificationMessageResult.assertions().isCalledExactly(2).withSequence( + notificationDisplayer.showNotificationResult.assertions().isCalledExactly(2).withSequence( listOf(value(A_ROOM_ID.value), value(notificationIdProvider.getRoomMessagesNotificationId(A_SESSION_ID)), value(A_NOTIFICATION)), listOf(value(null), value(notificationIdProvider.getSummaryNotificationId(A_SESSION_ID)), value(A_SUMMARY_NOTIFICATION.notification)) ) @@ -90,7 +90,7 @@ class NotificationRendererTest { renderEventsAsNotifications(listOf(aSimpleNotifiableEvent(eventId = AN_EVENT_ID))) - notificationDisplayer.showNotificationMessageResult.assertions().isCalledExactly(2).withSequence( + notificationDisplayer.showNotificationResult.assertions().isCalledExactly(2).withSequence( listOf(value(AN_EVENT_ID.value), value(notificationIdProvider.getRoomEventNotificationId(A_SESSION_ID)), value(A_NOTIFICATION)), listOf(value(null), value(notificationIdProvider.getSummaryNotificationId(A_SESSION_ID)), value(A_SUMMARY_NOTIFICATION.notification)) ) @@ -102,7 +102,7 @@ class NotificationRendererTest { renderEventsAsNotifications(listOf(anInviteNotifiableEvent())) - notificationDisplayer.showNotificationMessageResult.assertions().isCalledExactly(2).withSequence( + notificationDisplayer.showNotificationResult.assertions().isCalledExactly(2).withSequence( listOf(value(A_ROOM_ID.value), value(notificationIdProvider.getRoomInvitationNotificationId(A_SESSION_ID)), value(A_NOTIFICATION)), listOf(value(null), value(notificationIdProvider.getSummaryNotificationId(A_SESSION_ID)), value(A_SUMMARY_NOTIFICATION.notification)) ) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationDisplayer.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationDisplayer.kt index cd3d047e2e..d1c5de9ffb 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationDisplayer.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationDisplayer.kt @@ -19,17 +19,17 @@ import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value class FakeNotificationDisplayer( - var showNotificationMessageResult: LambdaThreeParamsRecorder = lambdaRecorder { _, _, _ -> true }, - var cancelNotificationMessageResult: LambdaTwoParamsRecorder = lambdaRecorder { _, _ -> }, + var showNotificationResult: LambdaThreeParamsRecorder = lambdaRecorder { _, _, _ -> true }, + var cancelNotificationResult: LambdaTwoParamsRecorder = lambdaRecorder { _, _ -> }, var displayDiagnosticNotificationResult: LambdaOneParamRecorder = lambdaRecorder { _ -> true }, var dismissDiagnosticNotificationResult: LambdaNoParamRecorder = lambdaRecorder { -> }, ) : NotificationDisplayer { - override fun showNotificationMessage(tag: String?, id: Int, notification: Notification): Boolean { - return showNotificationMessageResult(tag, id, notification) + override fun showNotification(tag: String?, id: Int, notification: Notification): Boolean { + return showNotificationResult(tag, id, notification) } - override fun cancelNotificationMessage(tag: String?, id: Int) { - return cancelNotificationMessageResult(tag, id) + override fun cancelNotification(tag: String?, id: Int) { + return cancelNotificationResult(tag, id) } override fun displayDiagnosticNotification(notification: Notification): Boolean { @@ -41,7 +41,7 @@ class FakeNotificationDisplayer( } fun verifySummaryCancelled(times: Int = 1) { - cancelNotificationMessageResult.assertions().isCalledExactly(times).withSequence( + cancelNotificationResult.assertions().isCalledExactly(times).withSequence( listOf(value(null), value(NotificationIdProvider.getSummaryNotificationId(A_SESSION_ID))) ) } From f4f623c4174d7216785e602e8a1352288c75dee1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Sun, 26 Oct 2025 09:09:10 +0100 Subject: [PATCH 005/216] Create const for diagnostic tag. --- .../push/impl/notifications/NotificationDisplayer.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDisplayer.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDisplayer.kt index deb486547b..decee9f198 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDisplayer.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDisplayer.kt @@ -46,7 +46,7 @@ class DefaultNotificationDisplayer( override fun displayDiagnosticNotification(notification: Notification): Boolean { return showNotification( - tag = "DIAGNOSTIC", + tag = TAG_DIAGNOSTIC, id = NOTIFICATION_ID_DIAGNOSTIC, notification = notification ) @@ -54,12 +54,13 @@ class DefaultNotificationDisplayer( override fun dismissDiagnosticNotification() { cancelNotification( - tag = "DIAGNOSTIC", + tag = TAG_DIAGNOSTIC, id = NOTIFICATION_ID_DIAGNOSTIC ) } companion object { + private const val TAG_DIAGNOSTIC = "DIAGNOSTIC" /* ========================================================================================== * IDs for notifications * ========================================================================================== */ From 04e5d684d4a6f4da0218b43bfdb7c4e451b80936 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 27 Oct 2025 08:26:12 +0100 Subject: [PATCH 006/216] Let DefaultNotificationDrawerManager use NotificationDisplayer instead of NotificationManagerCompat --- features/call/impl/build.gradle.kts | 1 + .../RingingCallNotificationCreatorTest.kt | 2 +- .../utils/DefaultActiveCallManagerTest.kt | 3 +- libraries/matrixui-test/build.gradle.kts | 20 ++++++++ .../matrix/ui/test/media/FakeImageLoader.kt | 50 +++++++++++++++++++ .../ui/test/media}/FakeImageLoaderHolder.kt | 11 ++-- libraries/push/impl/build.gradle.kts | 1 + .../DefaultNotificationDrawerManager.kt | 17 +++---- .../notifications/NotificationDisplayer.kt | 1 + .../DefaultBaseRoomGroupMessageCreatorTest.kt | 26 +++++----- .../DefaultNotificationDrawerManagerTest.kt | 31 ++++++------ ...aultOnMissedCallNotificationHandlerTest.kt | 9 ++-- .../NotificationDataFactoryTest.kt | 18 +++---- .../notifications/NotificationRendererTest.kt | 4 +- ...aultNotificationConversationServiceTest.kt | 2 +- .../DefaultNotificationCreatorTest.kt | 6 +-- .../test/notifications/FakeImageLoader.kt | 45 ----------------- 17 files changed, 136 insertions(+), 111 deletions(-) create mode 100644 libraries/matrixui-test/build.gradle.kts create mode 100644 libraries/matrixui-test/src/main/kotlin/io/element/android/libraries/matrix/ui/test/media/FakeImageLoader.kt rename libraries/{push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications => matrixui-test/src/main/kotlin/io/element/android/libraries/matrix/ui/test/media}/FakeImageLoaderHolder.kt (67%) delete mode 100644 libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeImageLoader.kt diff --git a/features/call/impl/build.gradle.kts b/features/call/impl/build.gradle.kts index 9efe5ba75b..5c1b4ef0a6 100644 --- a/features/call/impl/build.gradle.kts +++ b/features/call/impl/build.gradle.kts @@ -93,6 +93,7 @@ dependencies { testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.libraries.matrixuiTest) testImplementation(projects.libraries.push.test) testImplementation(projects.services.analytics.test) testImplementation(projects.services.appnavstate.test) diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/notifications/RingingCallNotificationCreatorTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/notifications/RingingCallNotificationCreatorTest.kt index 0af482fc49..7ec5532e83 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/notifications/RingingCallNotificationCreatorTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/notifications/RingingCallNotificationCreatorTest.kt @@ -18,7 +18,7 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_USER_ID_2 import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClientProvider -import io.element.android.libraries.push.test.notifications.FakeImageLoaderHolder +import io.element.android.libraries.matrix.ui.test.media.FakeImageLoaderHolder import io.element.android.libraries.push.test.notifications.push.FakeNotificationBitmapLoader import io.element.android.tests.testutils.lambda.lambdaRecorder import kotlinx.coroutines.test.runTest diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt index 3d1c35df4d..9464af603f 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt @@ -33,9 +33,9 @@ import io.element.android.libraries.matrix.test.FakeMatrixClientProvider 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.test.media.FakeImageLoaderHolder import io.element.android.libraries.push.api.notifications.ForegroundServiceType import io.element.android.libraries.push.api.notifications.NotificationIdProvider -import io.element.android.libraries.push.test.notifications.FakeImageLoaderHolder import io.element.android.libraries.push.test.notifications.FakeOnMissedCallNotificationHandler import io.element.android.libraries.push.test.notifications.push.FakeNotificationBitmapLoader import io.element.android.services.appnavstate.test.FakeAppForegroundStateService @@ -415,6 +415,7 @@ class DefaultActiveCallManagerTest { verify { notificationManagerCompat.cancel(any()) } } + @OptIn(ExperimentalCoroutinesApi::class) @Test fun `IncomingCall - ignore expired ring lifetime`() = runTest { diff --git a/libraries/matrixui-test/build.gradle.kts b/libraries/matrixui-test/build.gradle.kts new file mode 100644 index 0000000000..c385d05b54 --- /dev/null +++ b/libraries/matrixui-test/build.gradle.kts @@ -0,0 +1,20 @@ +/* + * 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.libraries.matrix.ui.test" +} + +dependencies { + implementation(projects.libraries.matrix.api) + implementation(projects.libraries.matrixui) + implementation(libs.coil.compose) +} diff --git a/libraries/matrixui-test/src/main/kotlin/io/element/android/libraries/matrix/ui/test/media/FakeImageLoader.kt b/libraries/matrixui-test/src/main/kotlin/io/element/android/libraries/matrix/ui/test/media/FakeImageLoader.kt new file mode 100644 index 0000000000..05e380fb9e --- /dev/null +++ b/libraries/matrixui-test/src/main/kotlin/io/element/android/libraries/matrix/ui/test/media/FakeImageLoader.kt @@ -0,0 +1,50 @@ +/* + * 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. + */ + +package io.element.android.libraries.matrix.ui.test.media + +import coil3.ComponentRegistry +import coil3.ImageLoader +import coil3.disk.DiskCache +import coil3.memory.MemoryCache +import coil3.request.Disposable +import coil3.request.ImageRequest +import coil3.request.ImageResult + +class FakeImageLoader : ImageLoader { + private val executedRequests = mutableListOf() + + override val defaults: ImageRequest.Defaults + get() = error("Not implemented") + override val components: ComponentRegistry + get() = error("Not implemented") + override val memoryCache: MemoryCache? + get() = error("Not implemented") + override val diskCache: DiskCache? + get() = error("Not implemented") + + override fun enqueue(request: ImageRequest): Disposable { + error("Not implemented") + } + + override suspend fun execute(request: ImageRequest): ImageResult { + executedRequests.add(request) + error("Not implemented") + } + + override fun shutdown() { + error("Not implemented") + } + + override fun newBuilder(): ImageLoader.Builder { + error("Not implemented") + } + + fun getExecutedRequestsData(): List { + return executedRequests.map { it.data } + } +} diff --git a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeImageLoaderHolder.kt b/libraries/matrixui-test/src/main/kotlin/io/element/android/libraries/matrix/ui/test/media/FakeImageLoaderHolder.kt similarity index 67% rename from libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeImageLoaderHolder.kt rename to libraries/matrixui-test/src/main/kotlin/io/element/android/libraries/matrix/ui/test/media/FakeImageLoaderHolder.kt index 4c92dc8c18..4deef7274b 100644 --- a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeImageLoaderHolder.kt +++ b/libraries/matrixui-test/src/main/kotlin/io/element/android/libraries/matrix/ui/test/media/FakeImageLoaderHolder.kt @@ -1,21 +1,22 @@ /* - * Copyright 2024 New Vector 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. */ -package io.element.android.libraries.push.test.notifications +package io.element.android.libraries.matrix.ui.test.media import coil3.ImageLoader import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder -class FakeImageLoaderHolder : ImageLoaderHolder { - private val fakeImageLoader = FakeImageLoader() +class FakeImageLoaderHolder( + val fakeImageLoader: ImageLoader = FakeImageLoader(), +) : ImageLoaderHolder { override fun get(client: MatrixClient): ImageLoader { - return fakeImageLoader.getImageLoader() + return fakeImageLoader } override fun remove(sessionId: SessionId) { diff --git a/libraries/push/impl/build.gradle.kts b/libraries/push/impl/build.gradle.kts index 87b3c681f1..2ea597b4f2 100644 --- a/libraries/push/impl/build.gradle.kts +++ b/libraries/push/impl/build.gradle.kts @@ -70,6 +70,7 @@ dependencies { testCommonDependencies(libs) testImplementation(libs.coil.test) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.libraries.matrixuiTest) testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.sessionStorage.test) testImplementation(projects.libraries.push.test) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt index 01a1b1f9a9..072ddcc891 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt @@ -8,7 +8,6 @@ package io.element.android.libraries.push.impl.notifications import androidx.annotation.VisibleForTesting -import androidx.core.app.NotificationManagerCompat import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.SingleIn @@ -46,7 +45,7 @@ private val loggerTag = LoggerTag("DefaultNotificationDrawerManager", LoggerTag. @SingleIn(AppScope::class) @ContributesBinding(AppScope::class) class DefaultNotificationDrawerManager( - private val notificationManager: NotificationManagerCompat, + private val notificationDisplayer: NotificationDisplayer, private val notificationRenderer: NotificationRenderer, private val appNavigationStateService: AppNavigationStateService, @AppCoroutineScope @@ -124,7 +123,7 @@ class DefaultNotificationDrawerManager( * Clear all known message events for a [sessionId]. */ override fun clearAllMessagesEvents(sessionId: SessionId) { - notificationManager.cancel(null, NotificationIdProvider.getRoomMessagesNotificationId(sessionId)) + notificationDisplayer.cancelNotification(null, NotificationIdProvider.getRoomMessagesNotificationId(sessionId)) clearSummaryNotificationIfNeeded(sessionId) } @@ -133,7 +132,7 @@ class DefaultNotificationDrawerManager( */ fun clearAllEvents(sessionId: SessionId) { activeNotificationsProvider.getNotificationsForSession(sessionId) - .forEach { notificationManager.cancel(it.tag, it.id) } + .forEach { notificationDisplayer.cancelNotification(it.tag, it.id) } } /** @@ -142,7 +141,7 @@ class DefaultNotificationDrawerManager( * Can also be called when a notification for this room is dismissed by the user. */ override fun clearMessagesForRoom(sessionId: SessionId, roomId: RoomId) { - notificationManager.cancel(roomId.value, NotificationIdProvider.getRoomMessagesNotificationId(sessionId)) + notificationDisplayer.cancelNotification(roomId.value, NotificationIdProvider.getRoomMessagesNotificationId(sessionId)) clearSummaryNotificationIfNeeded(sessionId) } @@ -158,7 +157,7 @@ class DefaultNotificationDrawerManager( override fun clearMembershipNotificationForSession(sessionId: SessionId) { activeNotificationsProvider.getMembershipNotificationForSession(sessionId) - .forEach { notificationManager.cancel(it.tag, it.id) } + .forEach { notificationDisplayer.cancelNotification(it.tag, it.id) } clearSummaryNotificationIfNeeded(sessionId) } @@ -167,7 +166,7 @@ class DefaultNotificationDrawerManager( */ override fun clearMembershipNotificationForRoom(sessionId: SessionId, roomId: RoomId) { activeNotificationsProvider.getMembershipNotificationForRoom(sessionId, roomId) - .forEach { notificationManager.cancel(it.tag, it.id) } + .forEach { notificationDisplayer.cancelNotification(it.tag, it.id) } clearSummaryNotificationIfNeeded(sessionId) } @@ -176,14 +175,14 @@ class DefaultNotificationDrawerManager( */ override fun clearEvent(sessionId: SessionId, eventId: EventId) { val id = NotificationIdProvider.getRoomEventNotificationId(sessionId) - notificationManager.cancel(eventId.value, id) + notificationDisplayer.cancelNotification(eventId.value, id) clearSummaryNotificationIfNeeded(sessionId) } private fun clearSummaryNotificationIfNeeded(sessionId: SessionId) { val summaryNotification = activeNotificationsProvider.getSummaryNotification(sessionId) if (summaryNotification != null && activeNotificationsProvider.count(sessionId) == 1) { - notificationManager.cancel(null, summaryNotification.id) + notificationDisplayer.cancelNotification(null, summaryNotification.id) } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDisplayer.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDisplayer.kt index decee9f198..c1e7ba1d29 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDisplayer.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDisplayer.kt @@ -61,6 +61,7 @@ class DefaultNotificationDisplayer( companion object { private const val TAG_DIAGNOSTIC = "DIAGNOSTIC" + /* ========================================================================================== * IDs for notifications * ========================================================================================== */ diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultBaseRoomGroupMessageCreatorTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultBaseRoomGroupMessageCreatorTest.kt index 00848390b5..f5acd8ddb4 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultBaseRoomGroupMessageCreatorTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultBaseRoomGroupMessageCreatorTest.kt @@ -18,12 +18,12 @@ import io.element.android.libraries.matrix.test.A_TIMESTAMP import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.media.AVATAR_THUMBNAIL_SIZE_IN_PIXEL import io.element.android.libraries.matrix.ui.media.MediaRequestData +import io.element.android.libraries.matrix.ui.test.media.FakeImageLoader import io.element.android.libraries.push.impl.notifications.factories.MARK_AS_READ_ACTION_TITLE import io.element.android.libraries.push.impl.notifications.factories.QUICK_REPLY_ACTION_TITLE import io.element.android.libraries.push.impl.notifications.factories.aNotificationAccountParams import io.element.android.libraries.push.impl.notifications.factories.createNotificationCreator import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent -import io.element.android.libraries.push.test.notifications.FakeImageLoader import io.element.android.services.toolbox.api.sdk.BuildVersionSdkIntProvider import io.element.android.services.toolbox.impl.strings.AndroidStringProvider import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvider @@ -51,7 +51,7 @@ class DefaultBaseRoomGroupMessageCreatorTest { ) ), roomId = A_ROOM_ID, - imageLoader = fakeImageLoader.getImageLoader(), + imageLoader = fakeImageLoader, existingNotification = null, threadId = null, ) @@ -59,7 +59,7 @@ class DefaultBaseRoomGroupMessageCreatorTest { @Suppress("DEPRECATION") assertThat(result.priority).isEqualTo(NotificationCompat.PRIORITY_LOW) assertThat(result.`when`).isEqualTo(A_TIMESTAMP) - assertThat(fakeImageLoader.getCoilRequests().size).isEqualTo(0) + assertThat(fakeImageLoader.getExecutedRequestsData()).isEmpty() } @Test @@ -74,13 +74,13 @@ class DefaultBaseRoomGroupMessageCreatorTest { ) ), roomId = A_ROOM_ID, - imageLoader = fakeImageLoader.getImageLoader(), + imageLoader = fakeImageLoader, existingNotification = null, threadId = null, ) @Suppress("DEPRECATION") assertThat(result.priority).isEqualTo(NotificationCompat.PRIORITY_DEFAULT) - assertThat(fakeImageLoader.getCoilRequests().size).isEqualTo(0) + assertThat(fakeImageLoader.getExecutedRequestsData()).isEmpty() } @Test @@ -141,12 +141,12 @@ class DefaultBaseRoomGroupMessageCreatorTest { ) ), roomId = A_ROOM_ID, - imageLoader = fakeImageLoader.getImageLoader(), + imageLoader = fakeImageLoader, existingNotification = null, threadId = null, ) assertThat(result.number).isEqualTo(1) - assertThat(fakeImageLoader.getCoilRequests()).containsExactlyElementsIn(expectedCoilRequests) + assertThat(fakeImageLoader.getExecutedRequestsData()).containsExactlyElementsIn(expectedCoilRequests) } @Test @@ -160,7 +160,7 @@ class DefaultBaseRoomGroupMessageCreatorTest { aNotifiableMessageEvent(timestamp = A_TIMESTAMP + 10), ), roomId = A_ROOM_ID, - imageLoader = fakeImageLoader.getImageLoader(), + imageLoader = fakeImageLoader, existingNotification = null, threadId = null, ) @@ -173,7 +173,7 @@ class DefaultBaseRoomGroupMessageCreatorTest { QUICK_REPLY_ACTION_TITLE.takeIf { NotificationConfig.SHOW_QUICK_REPLY_ACTION }, ) ) - assertThat(fakeImageLoader.getCoilRequests().size).isEqualTo(0) + assertThat(fakeImageLoader.getExecutedRequestsData()).isEmpty() } @Test @@ -189,7 +189,7 @@ class DefaultBaseRoomGroupMessageCreatorTest { ), ), roomId = A_ROOM_ID, - imageLoader = fakeImageLoader.getImageLoader(), + imageLoader = fakeImageLoader, existingNotification = null, threadId = null, ) @@ -199,7 +199,7 @@ class DefaultBaseRoomGroupMessageCreatorTest { MARK_AS_READ_ACTION_TITLE.takeIf { NotificationConfig.SHOW_MARK_AS_READ_ACTION } ) ) - assertThat(fakeImageLoader.getCoilRequests().size).isEqualTo(0) + assertThat(fakeImageLoader.getExecutedRequestsData()).isEmpty() } @Test @@ -214,13 +214,13 @@ class DefaultBaseRoomGroupMessageCreatorTest { ), ), roomId = A_ROOM_ID, - imageLoader = fakeImageLoader.getImageLoader(), + imageLoader = fakeImageLoader, existingNotification = null, threadId = null, ) assertThat(result.number).isEqualTo(1) assertThat(result.`when`).isEqualTo(A_TIMESTAMP) - assertThat(fakeImageLoader.getCoilRequests().size).isEqualTo(0) + assertThat(fakeImageLoader.getExecutedRequestsData()).isEmpty() } } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt index 275c1ac490..b3c4569470 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt @@ -9,7 +9,6 @@ package io.element.android.libraries.push.impl.notifications import android.app.Notification import androidx.compose.ui.graphics.Color -import androidx.core.app.NotificationManagerCompat import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService @@ -22,14 +21,15 @@ import io.element.android.libraries.matrix.test.A_THREAD_ID import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClientProvider import io.element.android.libraries.matrix.ui.components.aMatrixUser +import io.element.android.libraries.matrix.ui.test.media.FakeImageLoaderHolder import io.element.android.libraries.push.api.notifications.NotificationIdProvider import io.element.android.libraries.push.impl.notifications.factories.aNotificationAccountParams import io.element.android.libraries.push.impl.notifications.fake.FakeActiveNotificationsProvider import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationCreator +import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDisplayer import io.element.android.libraries.push.impl.notifications.fake.FakeRoomGroupMessageCreator import io.element.android.libraries.push.impl.notifications.fake.FakeSummaryGroupMessageCreator import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent -import io.element.android.libraries.push.test.notifications.FakeImageLoaderHolder import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.test.InMemorySessionStore import io.element.android.services.appnavstate.api.AppNavigationState @@ -43,19 +43,14 @@ import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value import io.mockk.every import io.mockk.mockk -import io.mockk.verify import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -import org.robolectric.RuntimeEnvironment @OptIn(ExperimentalCoroutinesApi::class) -@RunWith(RobolectricTestRunner::class) class DefaultNotificationDrawerManagerTest { @Test fun `clearAllEvents should have no effect when queue is empty`() = runTest { @@ -183,10 +178,12 @@ class DefaultNotificationDrawerManagerTest { @Test fun `clearSummaryNotificationIfNeeded will run after clearing all other notifications`() = runTest { - val notificationManager = mockk { - every { cancel(any(), any()) } returns Unit - } + val cancelNotificationResult = lambdaRecorder { _, _ -> } + val notificationDisplayer = FakeNotificationDisplayer( + cancelNotificationResult = cancelNotificationResult, + ) val summaryId = NotificationIdProvider.getSummaryNotificationId(A_SESSION_ID) + val roomMessageId = NotificationIdProvider.getRoomMessagesNotificationId(A_SESSION_ID) val activeNotificationsProvider = FakeActiveNotificationsProvider( getSummaryNotificationResult = { mockk { @@ -196,7 +193,7 @@ class DefaultNotificationDrawerManagerTest { countResult = { 1 }, ) val defaultNotificationDrawerManager = createDefaultNotificationDrawerManager( - notificationManager = notificationManager, + notificationDisplayer = notificationDisplayer, activeNotificationsProvider = activeNotificationsProvider, ) @@ -204,13 +201,16 @@ class DefaultNotificationDrawerManagerTest { defaultNotificationDrawerManager.clearAllMessagesEvents(A_SESSION_ID) // Verify we asked to cancel the notification with summaryId - verify { notificationManager.cancel(null, summaryId) } + cancelNotificationResult.assertions().isCalledExactly(2).withSequence( + listOf(value(null), value(roomMessageId)), + listOf(value(null), value(summaryId)), + ) defaultNotificationDrawerManager.destroy() } private fun TestScope.createDefaultNotificationDrawerManager( - notificationManager: NotificationManagerCompat = NotificationManagerCompat.from(RuntimeEnvironment.getApplication()), + notificationDisplayer: NotificationDisplayer = FakeNotificationDisplayer(), appNavigationStateService: AppNavigationStateService = FakeAppNavigationStateService(), roomGroupMessageCreator: RoomGroupMessageCreator = FakeRoomGroupMessageCreator(), summaryGroupMessageCreator: SummaryGroupMessageCreator = FakeSummaryGroupMessageCreator(), @@ -219,11 +219,10 @@ class DefaultNotificationDrawerManagerTest { sessionStore: SessionStore = InMemorySessionStore(), enterpriseService: EnterpriseService = FakeEnterpriseService(), ): DefaultNotificationDrawerManager { - val context = RuntimeEnvironment.getApplication() return DefaultNotificationDrawerManager( - notificationManager = notificationManager, + notificationDisplayer = notificationDisplayer, notificationRenderer = NotificationRenderer( - notificationDisplayer = DefaultNotificationDisplayer(context, NotificationManagerCompat.from(context)), + notificationDisplayer = FakeNotificationDisplayer(), notificationDataFactory = DefaultNotificationDataFactory( notificationCreator = FakeNotificationCreator(), roomGroupMessageCreator = roomGroupMessageCreator, diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultOnMissedCallNotificationHandlerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultOnMissedCallNotificationHandlerTest.kt index e5a4b01043..f2b26b3437 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultOnMissedCallNotificationHandlerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultOnMissedCallNotificationHandlerTest.kt @@ -15,22 +15,19 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClientProvider import io.element.android.libraries.matrix.test.notification.FakeNotificationService import io.element.android.libraries.matrix.test.notification.aNotificationData +import io.element.android.libraries.matrix.ui.test.media.FakeImageLoaderHolder import io.element.android.libraries.push.impl.notifications.fake.FakeActiveNotificationsProvider import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDataFactory +import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDisplayer import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent import io.element.android.libraries.push.test.notifications.FakeCallNotificationEventResolver -import io.element.android.libraries.push.test.notifications.FakeImageLoaderHolder import io.element.android.services.appnavstate.test.FakeAppNavigationStateService import io.element.android.tests.testutils.lambda.lambdaRecorder -import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -@RunWith(RobolectricTestRunner::class) class DefaultOnMissedCallNotificationHandlerTest { @OptIn(ExperimentalCoroutinesApi::class) @Test @@ -50,7 +47,7 @@ class DefaultOnMissedCallNotificationHandlerTest { val defaultOnMissedCallNotificationHandler = DefaultOnMissedCallNotificationHandler( matrixClientProvider = matrixClientProvider, defaultNotificationDrawerManager = DefaultNotificationDrawerManager( - notificationManager = mockk(relaxed = true), + notificationDisplayer = FakeNotificationDisplayer(), notificationRenderer = createNotificationRenderer( notificationDataFactory = dataFactory, ), diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt index 7bc19640e6..50e0b5dfb8 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt @@ -13,6 +13,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.matrix.ui.test.media.FakeImageLoader import io.element.android.libraries.push.impl.notifications.factories.aNotificationAccountParams import io.element.android.libraries.push.impl.notifications.fake.FakeActiveNotificationsProvider import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationCreator @@ -21,7 +22,6 @@ import io.element.android.libraries.push.impl.notifications.fake.FakeSummaryGrou import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent import io.element.android.libraries.push.impl.notifications.fixtures.aSimpleNotifiableEvent import io.element.android.libraries.push.impl.notifications.fixtures.anInviteNotifiableEvent -import io.element.android.libraries.push.test.notifications.FakeImageLoader import io.element.android.services.toolbox.test.strings.FakeStringProvider import kotlinx.coroutines.test.runTest import org.junit.Test @@ -98,7 +98,7 @@ class NotificationDataFactoryTest { events = events, roomId = A_ROOM_ID, threadId = null, - imageLoader = FakeImageLoader().getImageLoader(), + imageLoader = FakeImageLoader(), existingNotification = null, ), roomId = A_ROOM_ID, @@ -113,12 +113,12 @@ class NotificationDataFactoryTest { notificationAccountParams = aNotificationAccountParams( user = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), ), - imageLoader = fakeImageLoader.getImageLoader(), + imageLoader = fakeImageLoader, ) assertThat(result.size).isEqualTo(1) assertThat(result.first().isDataEqualTo(expectedNotification)).isTrue() - assertThat(fakeImageLoader.getCoilRequests().size).isEqualTo(0) + assertThat(fakeImageLoader.getExecutedRequestsData()).isEmpty() } @Test @@ -129,10 +129,10 @@ class NotificationDataFactoryTest { notificationAccountParams = aNotificationAccountParams( user = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), ), - imageLoader = fakeImageLoader.getImageLoader(), + imageLoader = fakeImageLoader, ) assertThat(result).isEmpty() - assertThat(fakeImageLoader.getCoilRequests().size).isEqualTo(0) + assertThat(fakeImageLoader.getExecutedRequestsData()).isEmpty() } @Test @@ -152,7 +152,7 @@ class NotificationDataFactoryTest { events = withRedactedRemoved, roomId = A_ROOM_ID, threadId = null, - imageLoader = FakeImageLoader().getImageLoader(), + imageLoader = FakeImageLoader(), existingNotification = null, ), roomId = A_ROOM_ID, @@ -168,12 +168,12 @@ class NotificationDataFactoryTest { notificationAccountParams = aNotificationAccountParams( user = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), ), - imageLoader = fakeImageLoader.getImageLoader(), + imageLoader = fakeImageLoader, ) assertThat(result.size).isEqualTo(1) assertThat(result.first().isDataEqualTo(expectedNotification)).isTrue() - assertThat(fakeImageLoader.getCoilRequests().size).isEqualTo(0) + assertThat(fakeImageLoader.getExecutedRequestsData()).isEmpty() } } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt index cb97174cb1..00ce37b09b 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt @@ -13,6 +13,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.matrix.ui.test.media.FakeImageLoader import io.element.android.libraries.push.api.notifications.NotificationIdProvider import io.element.android.libraries.push.impl.notifications.fake.FakeActiveNotificationsProvider import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationCreator @@ -25,7 +26,6 @@ import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiable import io.element.android.libraries.push.impl.notifications.fixtures.aSimpleNotifiableEvent import io.element.android.libraries.push.impl.notifications.fixtures.anInviteNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent -import io.element.android.libraries.push.test.notifications.FakeImageLoader import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.test.InMemorySessionStore import io.element.android.services.toolbox.test.strings.FakeStringProvider @@ -113,7 +113,7 @@ class NotificationRendererTest { MatrixUser(A_SESSION_ID, MY_USER_DISPLAY_NAME, MY_USER_AVATAR_URL), useCompleteNotificationFormat = USE_COMPLETE_NOTIFICATION_FORMAT, eventsToProcess = events, - imageLoader = FakeImageLoader().getImageLoader(), + imageLoader = FakeImageLoader(), ) } } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationServiceTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationServiceTest.kt index d05966f9a6..82441883a7 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationServiceTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationServiceTest.kt @@ -20,9 +20,9 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID_2 import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_SESSION_ID_2 import io.element.android.libraries.matrix.test.FakeMatrixClientProvider +import io.element.android.libraries.matrix.ui.test.media.FakeImageLoaderHolder import io.element.android.libraries.push.impl.notifications.factories.FakeIntentProvider import io.element.android.libraries.push.impl.notifications.shortcut.createShortcutId -import io.element.android.libraries.push.test.notifications.FakeImageLoaderHolder import io.element.android.libraries.push.test.notifications.push.FakeNotificationBitmapLoader import io.element.android.libraries.sessionstorage.test.observer.FakeSessionObserver import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt index df9f106972..b95b3128fc 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt @@ -22,6 +22,7 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_THREAD_ID import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.ui.components.aMatrixUser +import io.element.android.libraries.matrix.ui.test.media.FakeImageLoader import io.element.android.libraries.push.api.notifications.NotificationBitmapLoader import io.element.android.libraries.push.impl.notifications.DefaultNotificationBitmapLoader import io.element.android.libraries.push.impl.notifications.NotificationActionIds @@ -36,7 +37,6 @@ import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiable import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent -import io.element.android.libraries.push.test.notifications.FakeImageLoader import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvider import io.element.android.services.toolbox.test.strings.FakeStringProvider import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP @@ -246,7 +246,7 @@ class DefaultNotificationCreatorTest { lastMessageTimestamp = 123_456L, tickerText = "tickerText", existingNotification = null, - imageLoader = FakeImageLoader().getImageLoader(), + imageLoader = FakeImageLoader(), events = listOf(aNotifiableMessageEvent()), ) result.commonAssertions() @@ -271,7 +271,7 @@ class DefaultNotificationCreatorTest { lastMessageTimestamp = 123_456L, tickerText = "tickerText", existingNotification = null, - imageLoader = FakeImageLoader().getImageLoader(), + imageLoader = FakeImageLoader(), events = listOf(aNotifiableMessageEvent()), ) result.commonAssertions() diff --git a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeImageLoader.kt b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeImageLoader.kt deleted file mode 100644 index 067376cfbc..0000000000 --- a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeImageLoader.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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. - */ - -package io.element.android.libraries.push.test.notifications - -import android.graphics.Color -import android.graphics.drawable.ColorDrawable -import coil3.ImageLoader -import coil3.test.FakeImageLoaderEngine -import coil3.test.intercept -import org.robolectric.RuntimeEnvironment - -class FakeImageLoader { - private val coilRequests = mutableListOf() - - private var cache: ImageLoader? = null - - fun getImageLoader(): ImageLoader { - return cache ?: ImageLoader.Builder(RuntimeEnvironment.getApplication()) - .components { - val engine = FakeImageLoaderEngine.Builder() - .intercept( - predicate = { - coilRequests.add(it) - true - }, - drawable = ColorDrawable(Color.BLUE) - ) - .build() - add(engine) - } - .build() - .also { - cache = it - } - } - - fun getCoilRequests(): List { - return coilRequests.toList() - } -} From 3382aa2cfc0d9db10ff5441f7fb63475ec39475c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 27 Oct 2025 10:58:28 +0100 Subject: [PATCH 007/216] Use TestScope.backgroundScope --- .../DefaultNotificationDrawerManager.kt | 12 +----------- .../DefaultNotificationDrawerManagerTest.kt | 11 ++--------- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt index 072ddcc891..71e5f8b7af 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt @@ -7,7 +7,6 @@ package io.element.android.libraries.push.impl.notifications -import androidx.annotation.VisibleForTesting import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.SingleIn @@ -31,7 +30,6 @@ import io.element.android.services.appnavstate.api.AppNavigationStateService import io.element.android.services.appnavstate.api.NavigationState import io.element.android.services.appnavstate.api.currentSessionId import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job import kotlinx.coroutines.launch import timber.log.Timber @@ -54,25 +52,17 @@ class DefaultNotificationDrawerManager( private val imageLoaderHolder: ImageLoaderHolder, private val activeNotificationsProvider: ActiveNotificationsProvider, ) : NotificationCleaner { - private var appNavigationStateObserver: Job? = null - // TODO EAx add a setting per user for this private var useCompleteNotificationFormat = true init { // Observe application state - appNavigationStateObserver = coroutineScope.launch { + coroutineScope.launch { appNavigationStateService.appNavigationState .collect { onAppNavigationStateChange(it.navigationState) } } } - // For test only - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - internal fun destroy() { - appNavigationStateObserver?.cancel() - } - private var currentAppNavigationState: NavigationState? = null private fun onAppNavigationStateChange(navigationState: NavigationState) { diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt index b3c4569470..ff484df96f 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt @@ -56,7 +56,6 @@ class DefaultNotificationDrawerManagerTest { fun `clearAllEvents should have no effect when queue is empty`() = runTest { val defaultNotificationDrawerManager = createDefaultNotificationDrawerManager() defaultNotificationDrawerManager.clearAllEvents(A_SESSION_ID) - defaultNotificationDrawerManager.destroy() } @Test @@ -88,7 +87,6 @@ class DefaultNotificationDrawerManagerTest { defaultNotificationDrawerManager.onNotifiableEventReceived(aNotifiableMessageEvent()) // Add the same Event again (will be ignored) defaultNotificationDrawerManager.onNotifiableEventReceived(aNotifiableMessageEvent()) - defaultNotificationDrawerManager.destroy() } @Test @@ -101,7 +99,7 @@ class DefaultNotificationDrawerManagerTest { ) ) val appNavigationStateService = FakeAppNavigationStateService(appNavigationState = appNavigationStateFlow) - val defaultNotificationDrawerManager = createDefaultNotificationDrawerManager( + createDefaultNotificationDrawerManager( appNavigationStateService = appNavigationStateService ) appNavigationStateFlow.emit(AppNavigationState(aNavigationState(), isInForeground = true)) @@ -117,7 +115,6 @@ class DefaultNotificationDrawerManagerTest { // Like a user sign out appNavigationStateFlow.emit(AppNavigationState(aNavigationState(), isInForeground = true)) runCurrent() - defaultNotificationDrawerManager.destroy() } @Test @@ -172,8 +169,6 @@ class DefaultNotificationDrawerManagerTest { any(), ), ) - - defaultNotificationDrawerManager.destroy() } @Test @@ -205,8 +200,6 @@ class DefaultNotificationDrawerManagerTest { listOf(value(null), value(roomMessageId)), listOf(value(null), value(summaryId)), ) - - defaultNotificationDrawerManager.destroy() } private fun TestScope.createDefaultNotificationDrawerManager( @@ -234,7 +227,7 @@ class DefaultNotificationDrawerManagerTest { sessionStore = sessionStore, ), appNavigationStateService = appNavigationStateService, - coroutineScope = this, + coroutineScope = backgroundScope, matrixClientProvider = matrixClientProvider, imageLoaderHolder = FakeImageLoaderHolder(), activeNotificationsProvider = activeNotificationsProvider, From 674814e32267def59f135fd97dfd27fa9f8bb1a6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 27 Oct 2025 12:32:36 +0100 Subject: [PATCH 008/216] Move call to `setGroup` to `configureWith` --- .../impl/notifications/factories/NotificationCreator.kt | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt index 411cb25db1..79b4bb7db0 100755 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt @@ -155,9 +155,6 @@ class DefaultNotificationCreator( setShortcutId(createShortcutId(roomInfo.sessionId, roomInfo.roomId)) } } - // Auto-bundling is enabled for 4 or more notifications on API 24+ (N+) - // devices and all Wear devices. But we want a custom grouping, so we specify the groupID - .setGroup(roomInfo.sessionId.value) .setGroupSummary(false) // In order to avoid notification making sound twice (due to the summary notification) .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN) @@ -236,7 +233,6 @@ class DefaultNotificationCreator( .setOnlyAlertOnce(true) .setContentTitle((inviteNotifiableEvent.roomName ?: buildMeta.applicationName).annotateForDebug(5)) .setContentText(inviteNotifiableEvent.description.annotateForDebug(6)) - .setGroup(inviteNotifiableEvent.sessionId.value) .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL) .configureWith(notificationAccountParams) .apply { @@ -277,7 +273,6 @@ class DefaultNotificationCreator( .setOnlyAlertOnce(true) .setContentTitle(buildMeta.applicationName.annotateForDebug(7)) .setContentText(simpleNotifiableEvent.description.annotateForDebug(8)) - .setGroup(simpleNotifiableEvent.sessionId.value) .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL) .configureWith(notificationAccountParams) .setAutoCancel(true) @@ -308,7 +303,6 @@ class DefaultNotificationCreator( .setOnlyAlertOnce(true) .setContentTitle(buildMeta.applicationName.annotateForDebug(7)) .setContentText(fallbackNotifiableEvent.description.orEmpty().annotateForDebug(8)) - .setGroup(fallbackNotifiableEvent.sessionId.value) .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL) .configureWith(notificationAccountParams) .setAutoCancel(true) @@ -343,7 +337,6 @@ class DefaultNotificationCreator( // used in compat < N, after summary is built based on child notifications .setWhen(lastMessageTimestamp) .setCategory(NotificationCompat.CATEGORY_MESSAGE) - .setGroup(userId.value) // set this notification as the summary for the group .setGroupSummary(true) .configureWith(notificationAccountParams) @@ -476,6 +469,7 @@ class DefaultNotificationCreator( private fun NotificationCompat.Builder.configureWith(notificationAccountParams: NotificationAccountParams) = apply { setSmallIcon(CommonDrawables.ic_notification) setColor(notificationAccountParams.color) + setGroup(notificationAccountParams.user.userId.value) if (notificationAccountParams.showSessionId) { setSubText(notificationAccountParams.user.userId.value) } From e759dca9dbcc03fe782fcbc72da22c15734a39d4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 27 Oct 2025 12:40:59 +0100 Subject: [PATCH 009/216] Small cleanup --- .../push/impl/notifications/factories/NotificationCreator.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt index 79b4bb7db0..390f6b66d7 100755 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt @@ -164,7 +164,7 @@ class DefaultNotificationCreator( val messagingStyle = existingNotification?.let { MessagingStyle.extractMessagingStyleFromNotification(it) - } ?: messagingStyleFromCurrentUser( + } ?: createMessagingStyleFromCurrentUser( user = notificationAccountParams.user, imageLoader = imageLoader, roomName = roomInfo.roomDisplayName, @@ -437,7 +437,7 @@ class DefaultNotificationCreator( } } - private suspend fun messagingStyleFromCurrentUser( + private suspend fun createMessagingStyleFromCurrentUser( user: MatrixUser, imageLoader: ImageLoader, roomName: String, From 2b98a266942999cc2c0bd71ed1ff69624b190420 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 27 Oct 2025 12:50:12 +0100 Subject: [PATCH 010/216] Remove obsolete TODO --- .../push/impl/notifications/SummaryGroupMessageCreator.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt index 8600472c9d..80bc92dc23 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt @@ -49,13 +49,10 @@ class DefaultSummaryGroupMessageCreator( val summaryIsNoisy = roomNotifications.any { it.shouldBing } || invitationNotifications.any { it.isNoisy } || simpleNotifications.any { it.isNoisy } - val lastMessageTimestamp = roomNotifications.lastOrNull()?.latestTimestamp ?: invitationNotifications.lastOrNull()?.timestamp ?: simpleNotifications.last().timestamp - - // FIXME roomIdToEventMap.size is not correct, this is the number of rooms - val nbEvents = roomNotifications.size + simpleNotifications.size + val nbEvents = roomNotifications.size + invitationNotifications.size + simpleNotifications.size val sumTitle = stringProvider.getQuantityString(R.plurals.notification_compat_summary_title, nbEvents, nbEvents) return notificationCreator.createSummaryListNotification( notificationAccountParams = notificationAccountParams, From 6cbb679375fad0b9a42e6619cc10c12acb6086f1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 31 Oct 2025 09:42:22 +0100 Subject: [PATCH 011/216] Fix issue after rebase. --- .../push/impl/notifications/DefaultNotificationDrawerManager.kt | 2 +- .../push/impl/push/DefaultOnRedactedEventReceivedTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt index 71e5f8b7af..f3ba1c6d84 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt @@ -141,7 +141,7 @@ class DefaultNotificationDrawerManager( */ override fun clearMessagesForThread(sessionId: SessionId, roomId: RoomId, threadId: ThreadId) { val tag = NotificationCreator.messageTag(roomId, threadId) - notificationManager.cancel(tag, NotificationIdProvider.getRoomMessagesNotificationId(sessionId)) + notificationDisplayer.cancelNotification(tag, NotificationIdProvider.getRoomMessagesNotificationId(sessionId)) clearSummaryNotificationIfNeeded(sessionId) } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultOnRedactedEventReceivedTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultOnRedactedEventReceivedTest.kt index b27c96d8de..71ec7c3545 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultOnRedactedEventReceivedTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultOnRedactedEventReceivedTest.kt @@ -122,7 +122,7 @@ class DefaultOnRedactedEventReceivedTest { } ) }, - displayer = FakeNotificationDisplayer(showNotificationMessageResult = showNotificationLambda), + displayer = FakeNotificationDisplayer(showNotificationResult = showNotificationLambda), ) sut.onRedactedEventsReceived(listOf(ResolvedPushEvent.Redaction(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID, null))) From fed09eeefb6d3bfc74b2bd2fdcdc979fe836deb1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 31 Oct 2025 10:10:00 +0100 Subject: [PATCH 012/216] Fix test. --- .../notifications/factories/DefaultNotificationCreatorTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt index b95b3128fc..419a348f08 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt @@ -278,7 +278,7 @@ class DefaultNotificationCreatorTest { } private fun Notification.commonAssertions( - expectedGroup: String? = A_SESSION_ID.value, + expectedGroup: String? = aMatrixUser().userId.value, expectedCategory: String? = NotificationCompat.CATEGORY_MESSAGE, ) { assertThat(contentIntent).isNotNull() From d49fecf3455b026eac24ce8daea963fe65c629c0 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 30 Oct 2025 22:15:08 +0100 Subject: [PATCH 013/216] feature(space) : starts space settings screen --- .../impl/settings/SpaceSettingsEvents.kt | 10 + .../space/impl/settings/SpaceSettingsNode.kt | 58 +++++ .../impl/settings/SpaceSettingsPresenter.kt | 38 ++++ .../space/impl/settings/SpaceSettingsState.kt | 23 ++ .../settings/SpaceSettingsStateProvider.kt | 44 ++++ .../space/impl/settings/SpaceSettingsView.kt | 215 ++++++++++++++++++ 6 files changed, 388 insertions(+) create mode 100644 features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsEvents.kt create mode 100644 features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsNode.kt create mode 100644 features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsPresenter.kt create mode 100644 features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsState.kt create mode 100644 features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt create mode 100644 features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsEvents.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsEvents.kt new file mode 100644 index 0000000000..a6fe90ade6 --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsEvents.kt @@ -0,0 +1,10 @@ +/* + * 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. + */ + +package io.element.android.features.space.impl.settings + +sealed interface SpaceSettingsEvents diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsNode.kt new file mode 100644 index 0000000000..77ae924f94 --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsNode.kt @@ -0,0 +1,58 @@ +/* + * 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. + */ + +package io.element.android.features.space.impl.settings + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import com.bumble.appyx.core.plugin.plugins +import dev.zacsweers.metro.Assisted +import dev.zacsweers.metro.AssistedInject +import io.element.android.annotations.ContributesNode +import io.element.android.features.space.impl.di.SpaceFlowScope +import io.element.android.libraries.architecture.appyx.launchMolecule + +@ContributesNode(SpaceFlowScope::class) +@AssistedInject +class SpaceSettingsNode( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, + private val presenter: SpaceSettingsPresenter, +) : Node(buildContext, plugins = plugins) { + interface Callback : Plugin { + fun onBackClick() + + fun onSpaceInfoClick() + fun onMembersClick() + fun onRolesAndPermissionsClick() + fun onSecurityAndPrivacyClick() + fun onLeaveSpaceClick() + } + + private val callback = plugins().single() + private val stateFlow = launchMolecule { presenter.present() } + + @Composable + override fun View(modifier: Modifier) { + val state by stateFlow.collectAsState() + SpaceSettingsView( + state = state, + modifier = modifier, + onSpaceInfoClick = callback::onSpaceInfoClick, + onBackClick = callback::onBackClick, + onMembersClick = callback::onMembersClick, + onRolesAndPermissionsClick = callback::onRolesAndPermissionsClick, + onSecurityAndPrivacyClick = callback::onSecurityAndPrivacyClick, + onLeaveSpaceClick = callback::onLeaveSpaceClick, + ) + } +} diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsPresenter.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsPresenter.kt new file mode 100644 index 0000000000..48cb9f0b3b --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsPresenter.kt @@ -0,0 +1,38 @@ +/* + * 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. + */ + +package io.element.android.features.space.impl.settings + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import dev.zacsweers.metro.Inject +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.matrix.api.room.JoinedRoom +import io.element.android.libraries.matrix.ui.room.isOwnUserAdmin + +@Inject +class SpaceSettingsPresenter( + private val room: JoinedRoom, +) : Presenter { + @Composable + override fun present(): SpaceSettingsState { + val roomInfo by room.roomInfoFlow.collectAsState() + val isUserAdmin = room.isOwnUserAdmin() + return SpaceSettingsState( + roomId = room.roomId, + name = roomInfo.name.orEmpty(), + canonicalAlias = roomInfo.canonicalAlias, + avatarUrl = roomInfo.avatarUrl, + memberCount = roomInfo.activeMembersCount, + showRolesAndPermissions = isUserAdmin, + showSecurityAndPrivacy = isUserAdmin, + isTombstoned = roomInfo.successorRoom != null, + eventSink = {}, + ) + } +} diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsState.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsState.kt new file mode 100644 index 0000000000..b3d3353f06 --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsState.kt @@ -0,0 +1,23 @@ +/* + * 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. + */ + +package io.element.android.features.space.impl.settings + +import io.element.android.libraries.matrix.api.core.RoomAlias +import io.element.android.libraries.matrix.api.core.RoomId + +data class SpaceSettingsState( + val roomId: RoomId, + val name: String, + val canonicalAlias: RoomAlias?, + val avatarUrl: String?, + val isTombstoned: Boolean, + val memberCount: Long, + val showRolesAndPermissions: Boolean, + val showSecurityAndPrivacy: Boolean, + val eventSink: (SpaceSettingsEvents) -> Unit +) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt new file mode 100644 index 0000000000..e248c1798a --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt @@ -0,0 +1,44 @@ +/* + * 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. + */ + +package io.element.android.features.space.impl.settings + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.matrix.api.core.RoomAlias +import io.element.android.libraries.matrix.api.core.RoomId + +open class SpaceSettingsStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aSpaceSettingsState(), + aSpaceSettingsState(alias = null), + aSpaceSettingsState(showSecurityAndPrivacy = true), + aSpaceSettingsState(showRolesAndPermissions = true), + ) +} + +fun aSpaceSettingsState( + roomId: RoomId = RoomId("!aRoomId:element.io"), + name: String = "Space name", + alias: RoomAlias? = RoomAlias("#spacename:element.io"), + avatarUrl: String? = null, + memberCount: Long = 100, + isTombstoned: Boolean = false, + showRolesAndPermissions: Boolean = false, + showSecurityAndPrivacy: Boolean = false, + eventSink: (SpaceSettingsEvents) -> Unit = {}, +) = SpaceSettingsState( + roomId = roomId, + name = name, + canonicalAlias = alias, + avatarUrl = avatarUrl, + isTombstoned = isTombstoned, + memberCount = memberCount, + showRolesAndPermissions = showRolesAndPermissions, + showSecurityAndPrivacy = showSecurityAndPrivacy, + eventSink = eventSink, +) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt new file mode 100644 index 0000000000..eaefcf59d5 --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt @@ -0,0 +1,215 @@ +/* + * 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. + */ + +package io.element.android.features.space.impl.settings + +import androidx.compose.foundation.clickable +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.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.libraries.designsystem.components.avatar.Avatar +import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.components.avatar.AvatarType +import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.components.list.ListItemContent +import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.IconSource +import io.element.android.libraries.designsystem.theme.components.ListItem +import io.element.android.libraries.designsystem.theme.components.ListItemStyle +import io.element.android.libraries.designsystem.theme.components.Scaffold +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.theme.components.TopAppBar +import io.element.android.libraries.ui.strings.CommonStrings + +@Composable +fun SpaceSettingsView( + state: SpaceSettingsState, + onBackClick: () -> Unit, + onSpaceInfoClick: ()->Unit, + onMembersClick: () -> Unit, + onRolesAndPermissionsClick: () -> Unit, + onSecurityAndPrivacyClick: () -> Unit, + onLeaveSpaceClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Scaffold( + modifier = modifier, + topBar = { + SpaceSettingsTopBar(onBackClick = onBackClick) + }, + ) { padding -> + Column( + modifier = Modifier + .padding(padding) + .verticalScroll(rememberScrollState()) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable(onClick = onSpaceInfoClick) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Avatar( + avatarData = AvatarData(state.roomId.value, state.name, state.avatarUrl, AvatarSize.SpaceListItem), + avatarType = AvatarType.Space( + isTombstoned = state.isTombstoned, + ), + contentDescription = state.avatarUrl?.let { stringResource(CommonStrings.a11y_room_avatar) }, + ) + Spacer(Modifier.width(16.dp)) + Column { + Text( + text = state.name, + style = ElementTheme.typography.fontHeadingMdRegular, + color = ElementTheme.colors.textPrimary, + ) + if (state.canonicalAlias != null) { + Text( + text = state.canonicalAlias.value, + style = ElementTheme.typography.fontBodyMdRegular, + color = ElementTheme.colors.textSecondary, + ) + } + } + } + Section(isVisible = state.showSecurityAndPrivacy) { + SecurityAndPrivacyItem( + onClick = onSecurityAndPrivacyClick + ) + } + Section { + MembersItem(state.memberCount, onClick = onMembersClick) + if (state.showRolesAndPermissions) { + RolesAndPermissionsItem(onClick = onRolesAndPermissionsClick) + } + } + Section { + LeaveSpaceItem( + onClick = onLeaveSpaceClick + ) + } + + } + } +} + +@Composable +private fun ColumnScope.Section( + modifier: Modifier = Modifier, + isVisible: Boolean = true, + content: @Composable ColumnScope.() -> Unit, +) { + if (isVisible) { + PreferenceCategory(content = content, modifier = modifier) + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun SpaceSettingsTopBar( + onBackClick: () -> Unit, + modifier: Modifier = Modifier, +) { + TopAppBar( + titleStr = stringResource(CommonStrings.common_settings), + navigationIcon = { BackButton(onClick = onBackClick) }, + modifier = modifier, + ) +} + +@Composable +private fun SecurityAndPrivacyItem( + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + ListItem( + headlineContent = { Text("Security & privacy") }, + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Lock())), + onClick = onClick, + modifier = modifier, + ) +} + +@Composable +private fun MembersItem( + memberCount: Long, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + ListItem( + headlineContent = { Text(stringResource(CommonStrings.common_people)) }, + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.User())), + trailingContent = ListItemContent.Text(memberCount.toString()), + onClick = onClick, + modifier = modifier, + ) +} + +@Composable +private fun RolesAndPermissionsItem( + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + ListItem( + headlineContent = { Text("Roles & permissions") }, + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Admin())), + onClick = onClick, + modifier = modifier, + ) +} + +@Composable +private fun LeaveSpaceItem( + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + ListItem( + headlineContent = { + Text(stringResource(CommonStrings.action_leave_space)) + }, + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Leave())), + style = ListItemStyle.Destructive, + onClick = onClick, + modifier = modifier, + ) +} + +@PreviewsDayNight +@Composable +internal fun SpaceSettingsViewPreview( + @PreviewParameter(SpaceSettingsStateProvider::class) state: SpaceSettingsState +) = ElementPreview { + SpaceSettingsView( + state = state, + onBackClick = {}, + onSpaceInfoClick = {}, + onMembersClick = {}, + onRolesAndPermissionsClick = {}, + onSecurityAndPrivacyClick = {}, + onLeaveSpaceClick = {}, + modifier = Modifier, + ) +} From f86a1c62a57590a1abc7bf1910118c86241f9106 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 30 Oct 2025 22:15:30 +0100 Subject: [PATCH 014/216] feature(space) : remove dead code # Conflicts: # appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt --- .../io/element/android/appnav/room/RoomFlowNode.kt | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt index a41eb8d777..3189384f53 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt @@ -30,7 +30,6 @@ import io.element.android.features.joinroom.api.JoinRoomEntryPoint import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint.Params import io.element.android.features.roomdirectory.api.RoomDescription -import io.element.android.features.space.api.SpaceEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.NodeInputs @@ -70,7 +69,6 @@ class RoomFlowNode( private val joinRoomEntryPoint: JoinRoomEntryPoint, private val roomAliasResolverEntryPoint: RoomAliasResolverEntryPoint, private val membershipObserver: RoomMembershipObserver, - private val spaceEntryPoint: SpaceEntryPoint, ) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Loading, @@ -105,9 +103,6 @@ class RoomFlowNode( @Parcelize data class JoinedRoom(val roomId: RoomId) : NavTarget - - @Parcelize - data class JoinedSpace(val spaceId: RoomId) : NavTarget } override fun onBuilt() { @@ -209,15 +204,6 @@ class RoomFlowNode( ) createNode(buildContext, plugins = listOf(inputs) + roomFlowNodeCallback) } - is NavTarget.JoinedSpace -> { - val spaceCallback = plugins().single() - spaceEntryPoint.createNode( - parentNode = this, - buildContext = buildContext, - inputs = SpaceEntryPoint.Inputs(roomId = navTarget.spaceId), - callback = spaceCallback, - ) - } } } From 9beed3aeba90cc42618b0023d724f740fd9d8e59 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 30 Oct 2025 22:16:20 +0100 Subject: [PATCH 015/216] feature(space) : plumb up space settings screen --- .../room/joined/JoinedRoomLoadedFlowNode.kt | 4 -- .../features/space/api/SpaceEntryPoint.kt | 1 - .../features/space/impl/SpaceFlowNode.kt | 47 ++++++++++++++++--- .../space/impl/leave/LeaveSpaceNode.kt | 7 ++- .../features/space/impl/root/SpaceNode.kt | 4 +- 5 files changed, 45 insertions(+), 18 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt index 16eaff89b1..d6e3e13e89 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt @@ -195,10 +195,6 @@ class JoinedRoomLoadedFlowNode( callback.navigateToRoom(roomId, viaParameters) } - override fun navigateToRoomDetails() { - backstack.push(NavTarget.RoomDetails) - } - override fun navigateToRoomMemberList() { backstack.push(NavTarget.RoomMemberList) } diff --git a/features/space/api/src/main/kotlin/io/element/android/features/space/api/SpaceEntryPoint.kt b/features/space/api/src/main/kotlin/io/element/android/features/space/api/SpaceEntryPoint.kt index 6b5bd7f892..e05a7d1e8b 100644 --- a/features/space/api/src/main/kotlin/io/element/android/features/space/api/SpaceEntryPoint.kt +++ b/features/space/api/src/main/kotlin/io/element/android/features/space/api/SpaceEntryPoint.kt @@ -28,7 +28,6 @@ interface SpaceEntryPoint : FeatureEntryPoint { interface Callback : Plugin { fun navigateToRoom(roomId: RoomId, viaParameters: List) - fun navigateToRoomDetails() fun navigateToRoomMemberList() } } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt index 1ef496d319..6684d07952 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt @@ -18,6 +18,7 @@ import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.navmodel.backstack.BackStack +import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject @@ -26,14 +27,15 @@ import io.element.android.features.space.api.SpaceEntryPoint import io.element.android.features.space.impl.di.SpaceFlowGraph import io.element.android.features.space.impl.leave.LeaveSpaceNode import io.element.android.features.space.impl.root.SpaceNode +import io.element.android.features.space.impl.settings.SpaceSettingsNode import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode -import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.DependencyInjectionGraphOwner import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.spaces.SpaceService import kotlinx.parcelize.Parcelize @@ -42,6 +44,7 @@ import kotlinx.parcelize.Parcelize class SpaceFlowNode( @Assisted val buildContext: BuildContext, @Assisted plugins: List, + room: BaseRoom, spaceService: SpaceService, graphFactory: SpaceFlowGraph.Factory, ) : BaseFlowNode( @@ -52,15 +55,17 @@ class SpaceFlowNode( buildContext = buildContext, plugins = plugins, ), DependencyInjectionGraphOwner { - private val inputs: SpaceEntryPoint.Inputs = inputs() private val callback: SpaceEntryPoint.Callback = callback() - private val spaceRoomList = spaceService.spaceRoomList(inputs.roomId) + private val spaceRoomList = spaceService.spaceRoomList(room.roomId) override val graph = graphFactory.create(spaceRoomList) sealed interface NavTarget : Parcelable { @Parcelize data object Root : NavTarget + @Parcelize + data object Settings : NavTarget + @Parcelize data object Leave : NavTarget } @@ -77,7 +82,7 @@ class SpaceFlowNode( override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { NavTarget.Leave -> { - createNode(buildContext, listOf(inputs)) + createNode(buildContext) } NavTarget.Root -> { val callback = object : SpaceNode.Callback { @@ -85,8 +90,8 @@ class SpaceFlowNode( callback.navigateToRoom(roomId, viaParameters) } - override fun navigateToRoomDetails() { - callback.navigateToRoomDetails() + override fun navigateToSpaceSettings() { + backstack.push(NavTarget.Settings) } override fun navigateToRoomMemberList() { @@ -97,7 +102,35 @@ class SpaceFlowNode( backstack.push(NavTarget.Leave) } } - createNode(buildContext, listOf(inputs, callback)) + createNode(buildContext, listOf(callback)) + } + NavTarget.Settings -> { + val callback = object : SpaceSettingsNode.Callback { + override fun onBackClick() { + backstack.pop() + } + + override fun onSpaceInfoClick() { + //TODO + } + + override fun onMembersClick() { + callback.navigateToRoomMemberList() + } + + override fun onRolesAndPermissionsClick() { + //TODO + } + + override fun onSecurityAndPrivacyClick() { + //TODO + } + + override fun onLeaveSpaceClick() { + backstack.push(NavTarget.Leave) + } + } + createNode(buildContext, listOf(callback)) } } } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceNode.kt index c60bddea1d..215f06bca5 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceNode.kt @@ -16,10 +16,9 @@ import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode -import io.element.android.features.space.api.SpaceEntryPoint import io.element.android.features.space.impl.di.SpaceFlowScope -import io.element.android.libraries.architecture.inputs import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.room.JoinedRoom @ContributesNode(SpaceFlowScope::class) @AssistedInject @@ -27,10 +26,10 @@ class LeaveSpaceNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, matrixClient: MatrixClient, + room: JoinedRoom, presenterFactory: LeaveSpacePresenter.Factory, ) : Node(buildContext, plugins = plugins) { - private val inputs: SpaceEntryPoint.Inputs = inputs() - private val leaveSpaceHandle = matrixClient.spaceService.getLeaveSpaceHandle(inputs.roomId) + private val leaveSpaceHandle = matrixClient.spaceService.getLeaveSpaceHandle(room.roomId) private val presenter: LeaveSpacePresenter = presenterFactory.create(leaveSpaceHandle) override fun onBuilt() { diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceNode.kt index 174fa71ee8..c0271782b4 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceNode.kt @@ -42,7 +42,7 @@ class SpaceNode( ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { fun navigateToRoom(roomId: RoomId, viaParameters: List) - fun navigateToRoomDetails() + fun navigateToSpaceSettings() fun navigateToRoomMemberList() fun startLeaveSpaceFlow() } @@ -80,7 +80,7 @@ class SpaceNode( callback.navigateToRoom(spaceRoom.roomId, spaceRoom.via) }, onDetailsClick = { - callback.navigateToRoomDetails() + callback.navigateToSpaceSettings() }, onShareSpace = { onShareRoom(context) From 0894e8b1f2dc2ea38d9bcf05b8fffa0d091b0f99 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 31 Oct 2025 14:33:11 +0100 Subject: [PATCH 016/216] feature(space) : iterate on SpaceSettings --- .../impl/settings/SpaceSettingsPresenter.kt | 1 - .../space/impl/settings/SpaceSettingsState.kt | 1 - .../settings/SpaceSettingsStateProvider.kt | 2 - .../space/impl/settings/SpaceSettingsView.kt | 92 +++++++++++-------- 4 files changed, 54 insertions(+), 42 deletions(-) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsPresenter.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsPresenter.kt index 48cb9f0b3b..6e89a0ba8d 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsPresenter.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsPresenter.kt @@ -31,7 +31,6 @@ class SpaceSettingsPresenter( memberCount = roomInfo.activeMembersCount, showRolesAndPermissions = isUserAdmin, showSecurityAndPrivacy = isUserAdmin, - isTombstoned = roomInfo.successorRoom != null, eventSink = {}, ) } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsState.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsState.kt index b3d3353f06..95b3615f63 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsState.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsState.kt @@ -15,7 +15,6 @@ data class SpaceSettingsState( val name: String, val canonicalAlias: RoomAlias?, val avatarUrl: String?, - val isTombstoned: Boolean, val memberCount: Long, val showRolesAndPermissions: Boolean, val showSecurityAndPrivacy: Boolean, diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt index e248c1798a..db1b336653 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt @@ -27,7 +27,6 @@ fun aSpaceSettingsState( alias: RoomAlias? = RoomAlias("#spacename:element.io"), avatarUrl: String? = null, memberCount: Long = 100, - isTombstoned: Boolean = false, showRolesAndPermissions: Boolean = false, showSecurityAndPrivacy: Boolean = false, eventSink: (SpaceSettingsEvents) -> Unit = {}, @@ -36,7 +35,6 @@ fun aSpaceSettingsState( name = name, canonicalAlias = alias, avatarUrl = avatarUrl, - isTombstoned = isTombstoned, memberCount = memberCount, showRolesAndPermissions = showRolesAndPermissions, showSecurityAndPrivacy = showSecurityAndPrivacy, diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt index eaefcf59d5..1775b1e5b1 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt @@ -41,13 +41,14 @@ import io.element.android.libraries.designsystem.theme.components.ListItemStyle import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.ui.strings.CommonStrings @Composable fun SpaceSettingsView( state: SpaceSettingsState, onBackClick: () -> Unit, - onSpaceInfoClick: ()->Unit, + onSpaceInfoClick: () -> Unit, onMembersClick: () -> Unit, onRolesAndPermissionsClick: () -> Unit, onSecurityAndPrivacyClick: () -> Unit, @@ -65,59 +66,74 @@ fun SpaceSettingsView( .padding(padding) .verticalScroll(rememberScrollState()) ) { - Row( - modifier = Modifier - .fillMaxWidth() - .clickable(onClick = onSpaceInfoClick) - .padding(16.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Avatar( - avatarData = AvatarData(state.roomId.value, state.name, state.avatarUrl, AvatarSize.SpaceListItem), - avatarType = AvatarType.Space( - isTombstoned = state.isTombstoned, - ), - contentDescription = state.avatarUrl?.let { stringResource(CommonStrings.a11y_room_avatar) }, - ) - Spacer(Modifier.width(16.dp)) - Column { - Text( - text = state.name, - style = ElementTheme.typography.fontHeadingMdRegular, - color = ElementTheme.colors.textPrimary, - ) - if (state.canonicalAlias != null) { - Text( - text = state.canonicalAlias.value, - style = ElementTheme.typography.fontBodyMdRegular, - color = ElementTheme.colors.textSecondary, - ) - } - } - } - Section(isVisible = state.showSecurityAndPrivacy) { + SpaceInfoSection( + roomId = state.roomId, + name = state.name, + avatarUrl = state.avatarUrl, + canonicalAlias = state.canonicalAlias?.value, + onSpaceInfoClick = onSpaceInfoClick, + ) + Section(isVisible = state.showSecurityAndPrivacy, content = { SecurityAndPrivacyItem( onClick = onSecurityAndPrivacyClick ) - } - Section { + }) + Section(content = { MembersItem(state.memberCount, onClick = onMembersClick) if (state.showRolesAndPermissions) { RolesAndPermissionsItem(onClick = onRolesAndPermissionsClick) } - } - Section { + }) + Section(content = { LeaveSpaceItem( onClick = onLeaveSpaceClick ) - } + }) } } } @Composable -private fun ColumnScope.Section( +private fun SpaceInfoSection( + roomId: RoomId, + name: String, + avatarUrl: String?, + canonicalAlias: String?, + onSpaceInfoClick: () -> Unit, +) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable(onClick = onSpaceInfoClick) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Avatar( + avatarData = AvatarData(roomId.value, name, avatarUrl, AvatarSize.SpaceListItem), + avatarType = AvatarType.Space(), + contentDescription = avatarUrl?.let { stringResource(CommonStrings.a11y_avatar) }, + ) + Spacer(Modifier.width(16.dp)) + Column { + Text( + text = name, + style = ElementTheme.typography.fontHeadingMdRegular, + color = ElementTheme.colors.textPrimary, + ) + if (canonicalAlias != null) { + Text( + text = canonicalAlias, + style = ElementTheme.typography.fontBodyMdRegular, + color = ElementTheme.colors.textSecondary, + ) + } + } + } +} + +@Composable +private fun Section( modifier: Modifier = Modifier, isVisible: Boolean = true, content: @Composable ColumnScope.() -> Unit, From 4a56b13ecc4392c7b61d01d6f0b4b4c27aa0afaf Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 31 Oct 2025 14:55:07 +0100 Subject: [PATCH 017/216] feature(space) : update some strings --- .../impl/src/main/res/values/localazy.xml | 2 +- features/roomdetails/impl/src/main/res/values/localazy.xml | 2 +- .../impl/src/main/res/values/localazy.xml | 2 ++ .../io/element/android/features/space/impl/root/SpaceView.kt | 2 +- .../features/space/impl/settings/SpaceSettingsView.kt | 5 +++-- features/space/impl/src/main/res/values/localazy.xml | 3 +++ libraries/ui-strings/src/main/res/values/localazy.xml | 2 +- tools/localazy/config.json | 3 ++- 8 files changed, 14 insertions(+), 7 deletions(-) diff --git a/features/changeroommemberroles/impl/src/main/res/values/localazy.xml b/features/changeroommemberroles/impl/src/main/res/values/localazy.xml index 456426726a..43f6dc10f8 100644 --- a/features/changeroommemberroles/impl/src/main/res/values/localazy.xml +++ b/features/changeroommemberroles/impl/src/main/res/values/localazy.xml @@ -33,7 +33,7 @@ "Members" "You have unsaved changes." "Save changes?" - "There are no banned users in this room." + "There are no banned users." "%1$d person" "%1$d people" diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml index ce8eb3a7b7..814f352abd 100644 --- a/features/roomdetails/impl/src/main/res/values/localazy.xml +++ b/features/roomdetails/impl/src/main/res/values/localazy.xml @@ -70,7 +70,7 @@ "Room info" "Topic" "Updating room…" - "There are no banned users in this room." + "There are no banned users." "%1$d person" "%1$d people" diff --git a/features/roommembermoderation/impl/src/main/res/values/localazy.xml b/features/roommembermoderation/impl/src/main/res/values/localazy.xml index e3f6071898..3d23c8763a 100644 --- a/features/roommembermoderation/impl/src/main/res/values/localazy.xml +++ b/features/roommembermoderation/impl/src/main/res/values/localazy.xml @@ -4,10 +4,12 @@ "Ban" "They won’t be able to join again if invited." "Are you sure you want to ban this member?" + "They won’t be able to join this space again if invited, but they’ll still keep their memberships of any rooms or subspaces." "Banning %1$s" "Remove" "They will be able to join this room again if invited." "Are you sure you want to remove this member?" + "They will be able to join this space again if invited, and they’ll still keep their memberships of any rooms or subspaces." "View profile" "Remove user" "Remove member and ban from joining in the future?" diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt index f4c415b718..2779ab2687 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt @@ -328,7 +328,7 @@ private fun SpaceViewTopBar( }, text = { Text( - text = stringResource(id = CommonStrings.action_leave), + text = stringResource(id = CommonStrings.action_leave_space), color = ElementTheme.colors.textCriticalPrimary, ) }, diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt index 1775b1e5b1..9689b9e4f1 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.space.impl.R import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize @@ -162,7 +163,7 @@ private fun SecurityAndPrivacyItem( modifier: Modifier = Modifier, ) { ListItem( - headlineContent = { Text("Security & privacy") }, + headlineContent = { Text(stringResource(R.string.screen_space_settings_security_and_privacy)) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Lock())), onClick = onClick, modifier = modifier, @@ -190,7 +191,7 @@ private fun RolesAndPermissionsItem( modifier: Modifier = Modifier, ) { ListItem( - headlineContent = { Text("Roles & permissions") }, + headlineContent = { Text(stringResource(R.string.screen_space_settings_roles_and_permissions)) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Admin())), onClick = onClick, modifier = modifier, diff --git a/features/space/impl/src/main/res/values/localazy.xml b/features/space/impl/src/main/res/values/localazy.xml index c6ced29d41..a4df5e767d 100644 --- a/features/space/impl/src/main/res/values/localazy.xml +++ b/features/space/impl/src/main/res/values/localazy.xml @@ -10,4 +10,7 @@ "You will not be removed from the following room(s) because you\'re the only administrator:" "Leave %1$s?" "You are the only admin for %1$s" + "Leave space" + "Roles & permissions" + "Security & privacy" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 8ca9198cfe..7bf9e8192c 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -95,6 +95,7 @@ "Forgot password?" "Forward" "Go back" + "Go to roles & permissions" "Go to settings" "Ignore" "Invite" @@ -176,7 +177,6 @@ "Advanced settings" "an image" "Analytics" - "Fetching notifications…" "You left the room" "You were logged out of the session" "Appearance" diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 82c273db2e..3039631253 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -210,7 +210,8 @@ { "name" : ":features:space:impl", "includeRegex" : [ - "screen\\.leave_space\\..*" + "screen\\.leave_space\\..*", + "screen\\.space_settings\\..*" ] }, { From 389c2f345233c9035abcad7bd7839006286a1ba9 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 31 Oct 2025 15:10:55 +0100 Subject: [PATCH 018/216] feature(space) : some renaming on Space nodes --- .../features/space/impl/SpaceFlowNode.kt | 12 +++++----- .../space/impl/settings/SpaceSettingsNode.kt | 24 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt index 6684d07952..ba55203be6 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt @@ -106,27 +106,27 @@ class SpaceFlowNode( } NavTarget.Settings -> { val callback = object : SpaceSettingsNode.Callback { - override fun onBackClick() { + override fun closeSettings() { backstack.pop() } - override fun onSpaceInfoClick() { + override fun navigateToSpaceInfo() { //TODO } - override fun onMembersClick() { + override fun navigateToSpaceMembers() { callback.navigateToRoomMemberList() } - override fun onRolesAndPermissionsClick() { + override fun navigateToRolesAndPermissions() { //TODO } - override fun onSecurityAndPrivacyClick() { + override fun navigateToSecurityAndPrivacy() { //TODO } - override fun onLeaveSpaceClick() { + override fun startLeaveSpaceFlow() { backstack.push(NavTarget.Leave) } } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsNode.kt index 77ae924f94..9b81272153 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsNode.kt @@ -29,13 +29,13 @@ class SpaceSettingsNode( private val presenter: SpaceSettingsPresenter, ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { - fun onBackClick() + fun closeSettings() - fun onSpaceInfoClick() - fun onMembersClick() - fun onRolesAndPermissionsClick() - fun onSecurityAndPrivacyClick() - fun onLeaveSpaceClick() + fun navigateToSpaceInfo() + fun navigateToSpaceMembers() + fun navigateToRolesAndPermissions() + fun navigateToSecurityAndPrivacy() + fun startLeaveSpaceFlow() } private val callback = plugins().single() @@ -47,12 +47,12 @@ class SpaceSettingsNode( SpaceSettingsView( state = state, modifier = modifier, - onSpaceInfoClick = callback::onSpaceInfoClick, - onBackClick = callback::onBackClick, - onMembersClick = callback::onMembersClick, - onRolesAndPermissionsClick = callback::onRolesAndPermissionsClick, - onSecurityAndPrivacyClick = callback::onSecurityAndPrivacyClick, - onLeaveSpaceClick = callback::onLeaveSpaceClick, + onSpaceInfoClick = callback::navigateToSpaceInfo, + onBackClick = callback::closeSettings, + onMembersClick = callback::navigateToSpaceMembers, + onRolesAndPermissionsClick = callback::navigateToRolesAndPermissions, + onSecurityAndPrivacyClick = callback::navigateToSecurityAndPrivacy, + onLeaveSpaceClick = callback::startLeaveSpaceFlow, ) } } From fd980cf5bd45c3c76b304ebcce795e2f0316dd41 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 31 Oct 2025 15:32:30 +0100 Subject: [PATCH 019/216] feature(space) : prepare LeaveSpace for navigation to Roles&Permissions --- .../features/space/impl/SpaceFlowNode.kt | 17 +++++++++++++---- .../features/space/impl/leave/LeaveSpaceNode.kt | 11 ++++++++++- .../features/space/impl/leave/LeaveSpaceView.kt | 17 +++++++++++++++-- .../space/impl/settings/SpaceSettingsView.kt | 1 - 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt index ba55203be6..2df48350c6 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt @@ -82,7 +82,16 @@ class SpaceFlowNode( override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { NavTarget.Leave -> { - createNode(buildContext) + val callback = object : LeaveSpaceNode.Callback { + override fun closeLeaveSpaceFlow() { + backstack.pop() + } + + override fun navigateToRolesAndPermissions() { + // TODO + } + } + createNode(buildContext, listOf(callback)) } NavTarget.Root -> { val callback = object : SpaceNode.Callback { @@ -111,7 +120,7 @@ class SpaceFlowNode( } override fun navigateToSpaceInfo() { - //TODO + // TODO } override fun navigateToSpaceMembers() { @@ -119,11 +128,11 @@ class SpaceFlowNode( } override fun navigateToRolesAndPermissions() { - //TODO + // TODO } override fun navigateToSecurityAndPrivacy() { - //TODO + // TODO } override fun startLeaveSpaceFlow() { diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceNode.kt index 215f06bca5..6ba86481fe 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceNode.kt @@ -13,6 +13,7 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin +import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode @@ -29,9 +30,16 @@ class LeaveSpaceNode( room: JoinedRoom, presenterFactory: LeaveSpacePresenter.Factory, ) : Node(buildContext, plugins = plugins) { + interface Callback : Plugin { + fun closeLeaveSpaceFlow() + fun navigateToRolesAndPermissions() + } + private val leaveSpaceHandle = matrixClient.spaceService.getLeaveSpaceHandle(room.roomId) private val presenter: LeaveSpacePresenter = presenterFactory.create(leaveSpaceHandle) + private val callback = plugins().single() + override fun onBuilt() { super.onBuilt() lifecycle.subscribe( @@ -46,7 +54,8 @@ class LeaveSpaceNode( val state = presenter.present() LeaveSpaceView( state = state, - onCancel = ::navigateUp, + onCancel = callback::closeLeaveSpaceFlow, + onRolesAndPermissionsClick = callback::navigateToRolesAndPermissions, modifier = modifier ) } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceView.kt index 7432301f91..6f7a9903df 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceView.kt @@ -69,6 +69,7 @@ import io.element.android.libraries.ui.strings.CommonStrings fun LeaveSpaceView( state: LeaveSpaceState, onCancel: () -> Unit, + onRolesAndPermissionsClick: () -> Unit, modifier: Modifier = Modifier, ) { Scaffold( @@ -130,6 +131,9 @@ fun LeaveSpaceView( state.eventSink(LeaveSpaceEvents.LeaveSpace) }, onCancel = onCancel, + // TODO enable when navigation is ready + showRolesAndPermissionsButton = false, // state.isLastAdmin, + onRolesAndPermissionsClick = onRolesAndPermissionsClick, ) } } @@ -210,6 +214,8 @@ private fun LeaveSpaceButtons( showLeaveButton: Boolean, selectedRoomsCount: Int, onLeaveSpace: () -> Unit, + showRolesAndPermissionsButton: Boolean, + onRolesAndPermissionsClick: () -> Unit, onCancel: () -> Unit, ) { ButtonColumnMolecule( @@ -229,8 +235,14 @@ private fun LeaveSpaceButtons( destructive = true, ) } - // TODO For least admin space, add a button to open the settings. - // See https://www.figma.com/design/kcnHxunG1LDWXsJhaNuiHz/ER-145--Spaces-on-Element-X?node-id=4622-59600 + if (showRolesAndPermissionsButton) { + Button( + text = stringResource(CommonStrings.action_go_to_roles_and_permissions), + onClick = onRolesAndPermissionsClick, + modifier = Modifier.fillMaxWidth(), + leadingIcon = IconSource.Vector(CompoundIcons.Settings()), + ) + } TextButton( modifier = Modifier.fillMaxWidth(), text = stringResource(CommonStrings.action_cancel), @@ -345,5 +357,6 @@ internal fun LeaveSpaceViewPreview( LeaveSpaceView( state = state, onCancel = {}, + onRolesAndPermissionsClick = {}, ) } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt index 9689b9e4f1..fae5bf2f03 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt @@ -90,7 +90,6 @@ fun SpaceSettingsView( onClick = onLeaveSpaceClick ) }) - } } } From 29de5bdea9cab1cee7d3b23174e8caaf39514729 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 31 Oct 2025 15:34:55 +0100 Subject: [PATCH 020/216] feature(space) : some code clean up --- .../io/element/android/features/space/impl/SpaceFlowNode.kt | 4 ++-- .../android/features/space/impl/DefaultSpaceEntryPointTest.kt | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt index 2df48350c6..686729bae5 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt @@ -35,7 +35,7 @@ import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.DependencyInjectionGraphOwner import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.room.BaseRoom +import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.spaces.SpaceService import kotlinx.parcelize.Parcelize @@ -44,7 +44,7 @@ import kotlinx.parcelize.Parcelize class SpaceFlowNode( @Assisted val buildContext: BuildContext, @Assisted plugins: List, - room: BaseRoom, + room: JoinedRoom, spaceService: SpaceService, graphFactory: SpaceFlowGraph.Factory, ) : BaseFlowNode( diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt index 3fd260dd4f..696e80eeea 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt @@ -15,6 +15,8 @@ import io.element.android.features.space.api.SpaceEntryPoint import io.element.android.features.space.impl.di.FakeSpaceFlowGraph import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.room.FakeJoinedRoom +import io.element.android.libraries.matrix.test.room.join.FakeJoinRoom import io.element.android.libraries.matrix.test.spaces.FakeSpaceRoomList import io.element.android.libraries.matrix.test.spaces.FakeSpaceService import io.element.android.tests.testutils.lambda.lambdaError @@ -40,12 +42,12 @@ class DefaultSpaceEntryPointTest { spaceService = FakeSpaceService( spaceRoomListResult = { _: RoomId -> FakeSpaceRoomList(A_ROOM_ID) } ), + room = FakeJoinedRoom(), graphFactory = FakeSpaceFlowGraph.Factory ) } val callback = object : SpaceEntryPoint.Callback { override fun navigateToRoom(roomId: RoomId, viaParameters: List) = lambdaError() - override fun navigateToRoomDetails() = lambdaError() override fun navigateToRoomMemberList() = lambdaError() } val result = entryPoint.createNode( From fa55bfe70fea3504dbcf52e9b272fb122351387b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 31 Oct 2025 19:15:19 +0100 Subject: [PATCH 021/216] Improve code. --- .../DefaultNotificationDrawerManager.kt | 25 +------------------ .../factories/NotificationCreator.kt | 4 ++- 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt index f3ba1c6d84..1677072399 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt @@ -10,10 +10,8 @@ package io.element.android.libraries.push.impl.notifications import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.SingleIn -import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.di.annotations.AppCoroutineScope -import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId @@ -31,7 +29,6 @@ import io.element.android.services.appnavstate.api.NavigationState import io.element.android.services.appnavstate.api.currentSessionId import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -import timber.log.Timber private val loggerTag = LoggerTag("DefaultNotificationDrawerManager", LoggerTag.NotificationLoggerTag) @@ -190,29 +187,9 @@ class DefaultNotificationDrawerManager( // We have an avatar and a display name, use it userFromCache } else { - client.getSafeUserProfile() + client.getUserProfile().getOrNull() ?: MatrixUser(sessionId) } - notificationRenderer.render(currentUser, useCompleteNotificationFormat, notifiableEvents, imageLoader) } } - - private suspend fun MatrixClient.getSafeUserProfile(): MatrixUser { - return tryOrNull( - onException = { Timber.tag(loggerTag.value).e(it, "Unable to retrieve info for user ${sessionId.value}") }, - operation = { - val profile = getUserProfile().getOrNull() - // displayName cannot be empty else NotificationCompat.MessagingStyle() will crash - if (profile?.displayName.isNullOrEmpty()) { - profile?.copy(displayName = sessionId.value) - } else { - profile - } - } - ) ?: MatrixUser( - userId = sessionId, - displayName = sessionId.value, - avatarUrl = null - ) - } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt index 390f6b66d7..37774b4cc7 100755 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt @@ -25,6 +25,7 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.timeline.item.event.EventType import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.ui.model.getBestName import io.element.android.libraries.push.api.notifications.NotificationBitmapLoader import io.element.android.libraries.push.impl.R import io.element.android.libraries.push.impl.notifications.RoomEventGroupInfo @@ -446,7 +447,8 @@ class DefaultNotificationCreator( ): MessagingStyle { return MessagingStyle( Person.Builder() - .setName(user.displayName?.annotateForDebug(50)) + // Note: name cannot be empty else NotificationCompat.MessagingStyle() will crash + .setName(user.getBestName().annotateForDebug(50)) .setIcon(bitmapLoader.getUserIcon(user.avatarUrl, imageLoader)) .setKey(user.userId.value) .build() From 4df040bec1824b3329844533c43e75aa1c47581a Mon Sep 17 00:00:00 2001 From: ElementBot Date: Mon, 3 Nov 2025 14:16:13 +0000 Subject: [PATCH 022/216] Update screenshots --- ...features.space.impl.settings_SpaceSettingsView_Day_0_en.png | 3 +++ ...features.space.impl.settings_SpaceSettingsView_Day_1_en.png | 3 +++ ...features.space.impl.settings_SpaceSettingsView_Day_2_en.png | 3 +++ ...features.space.impl.settings_SpaceSettingsView_Day_3_en.png | 3 +++ ...atures.space.impl.settings_SpaceSettingsView_Night_0_en.png | 3 +++ ...atures.space.impl.settings_SpaceSettingsView_Night_1_en.png | 3 +++ ...atures.space.impl.settings_SpaceSettingsView_Night_2_en.png | 3 +++ ...atures.space.impl.settings_SpaceSettingsView_Night_3_en.png | 3 +++ 8 files changed, 24 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Day_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Day_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Day_3_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Night_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Night_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Night_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Night_3_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Day_0_en.png new file mode 100644 index 0000000000..24c00cb1a6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a573dcac3bab78db152da81c0a1b7803fcf70c2392cc05a20ae533edfde76730 +size 21620 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Day_1_en.png new file mode 100644 index 0000000000..13d9ea54d2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a17f1005bf296f04a2d631c6d19313bc864ebcd6832a788f8fa6909b7562dcb5 +size 17874 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Day_2_en.png new file mode 100644 index 0000000000..f408da9f38 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fc34deb76678be310df0beb512d91fa3b6edf2d2c59a7daf46267f1c8012e12c +size 25422 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Day_3_en.png new file mode 100644 index 0000000000..189eb42ad0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:14d4cabaf98490e35bb75a12c3d37e3c499158b6d6b639084059ee1bc999e831 +size 26018 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Night_0_en.png new file mode 100644 index 0000000000..d163b60818 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b13402b213b595b8a0a632eeb96263db7e8f7cbd3f56d8d235c38abf6df66128 +size 21235 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Night_1_en.png new file mode 100644 index 0000000000..77db74b048 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7031872033a6c12acd7328a5ae0820ac2dfa00340f8a8b6cac746c98004b285c +size 17444 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Night_2_en.png new file mode 100644 index 0000000000..9d45abb1c6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:053e0d74fb1c01e5e434aa48606b5f0d24fb9a950b56555783b618679c4e7ad5 +size 24925 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Night_3_en.png new file mode 100644 index 0000000000..9c36d80cef --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.settings_SpaceSettingsView_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d201e92db03d2d0d1b7d786d472d5d0f3e946ba7b610b9769ceb597b4d827eb9 +size 25467 From 98670478ffcc3c42d09ed269cdce0eac3def856e Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 3 Nov 2025 16:36:58 +0100 Subject: [PATCH 023/216] change(roles and permissions): rename package changeroommemberrole to rolesandpermissions --- features/home/impl/build.gradle.kts | 2 +- .../io/element/android/features/home/impl/HomeFlowNode.kt | 4 ++-- .../api/build.gradle.kts | 2 +- .../api/ChangeRoomMemberRolesEntryPoint.kt | 2 +- .../impl/build.gradle.kts | 4 ++-- .../rolesandpermissions}/impl/RoomMemberListDataSource.kt | 2 +- .../rolesandpermissions/impl/roles}/ChangeRolesEvent.kt | 4 ++-- .../rolesandpermissions/impl/roles}/ChangeRolesNode.kt | 4 ++-- .../rolesandpermissions/impl/roles}/ChangeRolesPresenter.kt | 3 ++- .../rolesandpermissions/impl/roles}/ChangeRolesState.kt | 4 ++-- .../impl/roles}/ChangeRolesStateProvider.kt | 4 ++-- .../rolesandpermissions/impl/roles}/ChangeRolesView.kt | 3 ++- .../impl/roles}/ChangeRoomMemberRolesRootNode.kt | 6 +++--- .../impl/roles}/DefaultChangeRoomMemberRolesEntyPoint.kt | 6 +++--- .../impl/src/main/res/values-be/translations.xml | 0 .../impl/src/main/res/values-bg/translations.xml | 0 .../impl/src/main/res/values-cs/translations.xml | 0 .../impl/src/main/res/values-cy/translations.xml | 0 .../impl/src/main/res/values-da/translations.xml | 0 .../impl/src/main/res/values-de/translations.xml | 0 .../impl/src/main/res/values-el/translations.xml | 0 .../impl/src/main/res/values-es/translations.xml | 0 .../impl/src/main/res/values-et/translations.xml | 0 .../impl/src/main/res/values-eu/translations.xml | 0 .../impl/src/main/res/values-fa/translations.xml | 0 .../impl/src/main/res/values-fi/translations.xml | 0 .../impl/src/main/res/values-fr/translations.xml | 0 .../impl/src/main/res/values-hu/translations.xml | 0 .../impl/src/main/res/values-in/translations.xml | 0 .../impl/src/main/res/values-it/translations.xml | 0 .../impl/src/main/res/values-ka/translations.xml | 0 .../impl/src/main/res/values-ko/translations.xml | 0 .../impl/src/main/res/values-lt/translations.xml | 0 .../impl/src/main/res/values-nb/translations.xml | 0 .../impl/src/main/res/values-nl/translations.xml | 0 .../impl/src/main/res/values-pl/translations.xml | 0 .../impl/src/main/res/values-pt-rBR/translations.xml | 0 .../impl/src/main/res/values-pt/translations.xml | 0 .../impl/src/main/res/values-ro/translations.xml | 0 .../impl/src/main/res/values-ru/translations.xml | 0 .../impl/src/main/res/values-sk/translations.xml | 0 .../impl/src/main/res/values-sv/translations.xml | 0 .../impl/src/main/res/values-tr/translations.xml | 0 .../impl/src/main/res/values-uk/translations.xml | 0 .../impl/src/main/res/values-ur/translations.xml | 0 .../impl/src/main/res/values-uz/translations.xml | 0 .../impl/src/main/res/values-zh-rTW/translations.xml | 0 .../impl/src/main/res/values-zh/translations.xml | 0 .../impl/src/main/res/values/localazy.xml | 0 .../rolesandpermissions/impl/roles}/ChangeRolesNodeTest.kt | 4 ++-- .../impl/roles}/ChangeRolesPresenterTest.kt | 2 +- .../rolesandpermissions/impl/roles}/ChangeRolesViewTest.kt | 2 +- .../roles}/DefaultChangeRoomMemberRolesEntyPointTest.kt | 4 ++-- .../rolesandpermissions/impl/roles}/MembersByRoleTest.kt | 4 ++-- .../test/build.gradle.kts | 4 ++-- .../test/FakeChangeRoomMemberRolesEntryPoint.kt | 6 +++--- features/roomdetails/impl/build.gradle.kts | 4 ++-- .../features/roomdetails/impl/RoomDetailsFlowNode.kt | 4 ++-- .../impl/rolesandpermissions/RolesAndPermissionsFlowNode.kt | 4 ++-- .../roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt | 2 +- 60 files changed, 46 insertions(+), 44 deletions(-) rename features/{changeroommemberroles => rolesandpermissions}/api/build.gradle.kts (87%) rename features/{changeroommemberroles/api/src/main/kotlin/io/element/android/features/changeroommemberroles => rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions}/api/ChangeRoomMemberRolesEntryPoint.kt (94%) rename features/{changeroommemberroles => rolesandpermissions}/impl/build.gradle.kts (90%) rename features/{changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions}/impl/RoomMemberListDataSource.kt (95%) rename features/{changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles}/ChangeRolesEvent.kt (85%) rename features/{changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles}/ChangeRolesNode.kt (93%) rename features/{changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles}/ChangeRolesPresenter.kt (98%) rename features/{changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles}/ChangeRolesState.kt (95%) rename features/{changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles}/ChangeRolesStateProvider.kt (98%) rename features/{changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles}/ChangeRolesView.kt (99%) rename features/{changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles}/ChangeRoomMemberRolesRootNode.kt (91%) rename features/{changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles}/DefaultChangeRoomMemberRolesEntyPoint.kt (81%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-be/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-bg/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-cs/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-cy/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-da/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-de/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-el/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-es/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-et/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-eu/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-fa/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-fi/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-fr/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-hu/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-in/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-it/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-ka/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-ko/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-lt/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-nb/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-nl/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-pl/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-pt-rBR/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-pt/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-ro/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-ru/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-sk/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-sv/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-tr/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-uk/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-ur/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-uz/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-zh-rTW/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values-zh/translations.xml (100%) rename features/{changeroommemberroles => rolesandpermissions}/impl/src/main/res/values/localazy.xml (100%) rename features/{changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl => rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles}/ChangeRolesNodeTest.kt (83%) rename features/{changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl => rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles}/ChangeRolesPresenterTest.kt (99%) rename features/{changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl => rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles}/ChangeRolesViewTest.kt (99%) rename features/{changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl => rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles}/DefaultChangeRoomMemberRolesEntyPointTest.kt (91%) rename features/{changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl => rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles}/MembersByRoleTest.kt (97%) rename features/{changeroommemberroles => rolesandpermissions}/test/build.gradle.kts (75%) rename features/{changeroommemberroles => rolesandpermissions}/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt (73%) diff --git a/features/home/impl/build.gradle.kts b/features/home/impl/build.gradle.kts index 9a29532ef0..1ac8dcec72 100644 --- a/features/home/impl/build.gradle.kts +++ b/features/home/impl/build.gradle.kts @@ -56,7 +56,7 @@ dependencies { implementation(libs.haze) implementation(libs.haze.materials) implementation(projects.features.reportroom.api) - implementation(projects.features.changeroommemberroles.api) + implementation(projects.features.rolesandpermissions.api) implementation(projects.libraries.previewutils) api(projects.features.home.api) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt index 5380c51295..91ef53d9de 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt @@ -28,8 +28,8 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode -import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesEntryPoint -import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType import io.element.android.features.home.api.HomeEntryPoint import io.element.android.features.home.impl.components.RoomListMenuAction import io.element.android.features.home.impl.model.RoomListRoomSummary diff --git a/features/changeroommemberroles/api/build.gradle.kts b/features/rolesandpermissions/api/build.gradle.kts similarity index 87% rename from features/changeroommemberroles/api/build.gradle.kts rename to features/rolesandpermissions/api/build.gradle.kts index 655bd5683e..0ace6032d9 100644 --- a/features/changeroommemberroles/api/build.gradle.kts +++ b/features/rolesandpermissions/api/build.gradle.kts @@ -11,7 +11,7 @@ plugins { } android { - namespace = "io.element.android.features.changeroommemberroles.api" + namespace = "io.element.android.features.rolesandpermissions.api" } dependencies { diff --git a/features/changeroommemberroles/api/src/main/kotlin/io/element/android/features/changeroommemberroles/api/ChangeRoomMemberRolesEntryPoint.kt b/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/ChangeRoomMemberRolesEntryPoint.kt similarity index 94% rename from features/changeroommemberroles/api/src/main/kotlin/io/element/android/features/changeroommemberroles/api/ChangeRoomMemberRolesEntryPoint.kt rename to features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/ChangeRoomMemberRolesEntryPoint.kt index 7905cdc0ae..0d93632699 100644 --- a/features/changeroommemberroles/api/src/main/kotlin/io/element/android/features/changeroommemberroles/api/ChangeRoomMemberRolesEntryPoint.kt +++ b/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/ChangeRoomMemberRolesEntryPoint.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.changeroommemberroles.api +package io.element.android.features.rolesandpermissions.api import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node diff --git a/features/changeroommemberroles/impl/build.gradle.kts b/features/rolesandpermissions/impl/build.gradle.kts similarity index 90% rename from features/changeroommemberroles/impl/build.gradle.kts rename to features/rolesandpermissions/impl/build.gradle.kts index be2bf17979..2cd0960c50 100644 --- a/features/changeroommemberroles/impl/build.gradle.kts +++ b/features/rolesandpermissions/impl/build.gradle.kts @@ -14,7 +14,7 @@ plugins { } android { - namespace = "io.element.android.features.changeroommemberroles.impl" + namespace = "io.element.android.features.rolesandpermissions.impl" testOptions { unitTests { @@ -26,7 +26,7 @@ android { setupDependencyInjection() dependencies { - api(projects.features.changeroommemberroles.api) + api(projects.features.rolesandpermissions.api) implementation(projects.appnav) implementation(projects.libraries.architecture) implementation(projects.libraries.core) diff --git a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/RoomMemberListDataSource.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RoomMemberListDataSource.kt similarity index 95% rename from features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/RoomMemberListDataSource.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RoomMemberListDataSource.kt index a27833122a..523b320ccd 100644 --- a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/RoomMemberListDataSource.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RoomMemberListDataSource.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.changeroommemberroles.impl +package io.element.android.features.rolesandpermissions.impl import dev.zacsweers.metro.Inject import io.element.android.libraries.core.bool.orFalse diff --git a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesEvent.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesEvent.kt similarity index 85% rename from features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesEvent.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesEvent.kt index 56e1c50bcc..df924bc575 100644 --- a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesEvent.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesEvent.kt @@ -1,11 +1,11 @@ /* - * Copyright 2024 New Vector 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. */ -package io.element.android.features.changeroommemberroles.impl +package io.element.android.features.rolesandpermissions.impl.roles import io.element.android.libraries.matrix.api.user.MatrixUser diff --git a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNode.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesNode.kt similarity index 93% rename from features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNode.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesNode.kt index 9edd20b549..915b129c84 100644 --- a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNode.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesNode.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.changeroommemberroles.impl +package io.element.android.features.rolesandpermissions.impl.roles import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -17,7 +17,7 @@ import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode -import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.appyx.launchMolecule import io.element.android.libraries.architecture.inputs diff --git a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesPresenter.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt similarity index 98% rename from features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesPresenter.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt index b6d865a66a..07a0c5a873 100644 --- a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesPresenter.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.changeroommemberroles.impl +package io.element.android.features.rolesandpermissions.impl.roles import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -22,6 +22,7 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.RoomModeration +import io.element.android.features.rolesandpermissions.impl.RoomMemberListDataSource import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers diff --git a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesState.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesState.kt similarity index 95% rename from features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesState.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesState.kt index e0b3e68a9e..17d7b0c3dd 100644 --- a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesState.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesState.kt @@ -1,11 +1,11 @@ /* - * Copyright 2024 New Vector 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. */ -package io.element.android.features.changeroommemberroles.impl +package io.element.android.features.rolesandpermissions.impl.roles import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.designsystem.theme.components.SearchBarResultState diff --git a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesStateProvider.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesStateProvider.kt similarity index 98% rename from features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesStateProvider.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesStateProvider.kt index b54347f73f..d054f459aa 100644 --- a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesStateProvider.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesStateProvider.kt @@ -1,11 +1,11 @@ /* - * Copyright 2024 New Vector 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. */ -package io.element.android.features.changeroommemberroles.impl +package io.element.android.features.rolesandpermissions.impl.changerole import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction diff --git a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesView.kt similarity index 99% rename from features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesView.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesView.kt index f9ebd75ca2..16c6965f18 100644 --- a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesView.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.changeroommemberroles.impl +package io.element.android.features.rolesandpermissions.impl.roles import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility @@ -39,6 +39,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme +import io.element.android.features.rolesandpermissions.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.AsyncIndicator diff --git a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRoomMemberRolesRootNode.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRoomMemberRolesRootNode.kt similarity index 91% rename from features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRoomMemberRolesRootNode.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRoomMemberRolesRootNode.kt index a7556ef8ca..33fbd197ae 100644 --- a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRoomMemberRolesRootNode.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRoomMemberRolesRootNode.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.changeroommemberroles.impl +package io.element.android.features.rolesandpermissions.impl.roles import android.os.Parcelable import androidx.compose.runtime.Composable @@ -20,8 +20,8 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.appnav.di.RoomGraphFactory -import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesEntryPoint -import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs diff --git a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/DefaultChangeRoomMemberRolesEntyPoint.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/DefaultChangeRoomMemberRolesEntyPoint.kt similarity index 81% rename from features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/DefaultChangeRoomMemberRolesEntyPoint.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/DefaultChangeRoomMemberRolesEntyPoint.kt index e76333cda7..62ec729b14 100644 --- a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/DefaultChangeRoomMemberRolesEntyPoint.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/DefaultChangeRoomMemberRolesEntyPoint.kt @@ -5,13 +5,13 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.changeroommemberroles.impl +package io.element.android.features.rolesandpermissions.impl.roles import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import dev.zacsweers.metro.ContributesBinding -import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesEntryPoint -import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.room.JoinedRoom diff --git a/features/changeroommemberroles/impl/src/main/res/values-be/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-be/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-be/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-be/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-bg/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-bg/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-bg/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-bg/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-cs/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-cs/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-cs/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-cs/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-cy/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-cy/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-cy/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-cy/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-da/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-da/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-da/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-da/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-de/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-de/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-de/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-de/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-el/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-el/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-el/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-el/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-es/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-es/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-es/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-es/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-et/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-et/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-et/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-et/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-eu/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-eu/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-eu/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-eu/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-fa/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-fa/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-fa/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-fa/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-fi/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-fi/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-fi/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-fi/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-fr/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-fr/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-fr/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-fr/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-hu/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-hu/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-hu/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-hu/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-in/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-in/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-in/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-in/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-it/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-it/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-it/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-it/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-ka/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-ka/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-ka/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-ka/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-ko/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-ko/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-ko/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-ko/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-lt/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-lt/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-lt/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-lt/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-nb/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-nb/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-nb/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-nb/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-nl/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-nl/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-nl/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-nl/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-pl/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-pl/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-pl/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-pl/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-pt-rBR/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-pt-rBR/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-pt-rBR/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-pt-rBR/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-pt/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-pt/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-pt/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-pt/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-ro/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-ro/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-ro/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-ro/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-ru/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-ru/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-ru/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-ru/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-sk/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-sk/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-sk/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-sk/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-sv/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-sv/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-sv/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-sv/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-tr/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-tr/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-tr/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-tr/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-uk/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-uk/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-uk/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-uk/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-ur/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-ur/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-ur/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-ur/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-uz/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-uz/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-uz/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-uz/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-zh-rTW/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-zh-rTW/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-zh-rTW/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-zh-rTW/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values-zh/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-zh/translations.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values-zh/translations.xml rename to features/rolesandpermissions/impl/src/main/res/values-zh/translations.xml diff --git a/features/changeroommemberroles/impl/src/main/res/values/localazy.xml b/features/rolesandpermissions/impl/src/main/res/values/localazy.xml similarity index 100% rename from features/changeroommemberroles/impl/src/main/res/values/localazy.xml rename to features/rolesandpermissions/impl/src/main/res/values/localazy.xml diff --git a/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNodeTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesNodeTest.kt similarity index 83% rename from features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNodeTest.kt rename to features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesNodeTest.kt index 3da57d3380..e8a470b0ef 100644 --- a/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNodeTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesNodeTest.kt @@ -5,10 +5,10 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.changeroommemberroles.impl +package io.element.android.features.rolesandpermissions.impl.roles import com.google.common.truth.Truth.assertThat -import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType import io.element.android.libraries.matrix.api.room.RoomMember import org.junit.Test diff --git a/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesPresenterTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenterTest.kt similarity index 99% rename from features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesPresenterTest.kt rename to features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenterTest.kt index a5e4cfaccb..6bcad2c4cc 100644 --- a/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesPresenterTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenterTest.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.changeroommemberroles.impl +package io.element.android.features.rolesandpermissions.impl.roles import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow diff --git a/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesViewTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesViewTest.kt similarity index 99% rename from features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesViewTest.kt rename to features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesViewTest.kt index 19d9cadfa8..dc26741587 100644 --- a/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesViewTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesViewTest.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.changeroommemberroles.impl +package io.element.android.features.rolesandpermissions.impl.roles import androidx.activity.ComponentActivity import androidx.compose.ui.test.junit4.AndroidComposeTestRule diff --git a/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/DefaultChangeRoomMemberRolesEntyPointTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/DefaultChangeRoomMemberRolesEntyPointTest.kt similarity index 91% rename from features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/DefaultChangeRoomMemberRolesEntyPointTest.kt rename to features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/DefaultChangeRoomMemberRolesEntyPointTest.kt index 84e53c0cb6..3612479418 100644 --- a/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/DefaultChangeRoomMemberRolesEntyPointTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/DefaultChangeRoomMemberRolesEntyPointTest.kt @@ -5,12 +5,12 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.changeroommemberroles.impl +package io.element.android.features.rolesandpermissions.impl.roles import androidx.test.ext.junit.runners.AndroidJUnit4 import com.bumble.appyx.core.modality.BuildContext import com.google.common.truth.Truth.assertThat -import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.tests.testutils.node.TestParentNode import kotlinx.coroutines.test.runTest diff --git a/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/MembersByRoleTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/MembersByRoleTest.kt similarity index 97% rename from features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/MembersByRoleTest.kt rename to features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/MembersByRoleTest.kt index a2e134f807..101a0e2869 100644 --- a/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/MembersByRoleTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/MembersByRoleTest.kt @@ -1,11 +1,11 @@ /* - * Copyright 2024 New Vector 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. */ -package io.element.android.features.changeroommemberroles.impl +package io.element.android.features.rolesandpermissions.impl.roles import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.room.RoomMember diff --git a/features/changeroommemberroles/test/build.gradle.kts b/features/rolesandpermissions/test/build.gradle.kts similarity index 75% rename from features/changeroommemberroles/test/build.gradle.kts rename to features/rolesandpermissions/test/build.gradle.kts index 4d85d83c90..a5385b29d7 100644 --- a/features/changeroommemberroles/test/build.gradle.kts +++ b/features/rolesandpermissions/test/build.gradle.kts @@ -10,11 +10,11 @@ plugins { } android { - namespace = "io.element.android.features.changeroommemberroles.test" + namespace = "io.element.android.features.rolesandpermissions.test" } dependencies { - implementation(projects.features.changeroommemberroles.api) + implementation(projects.features.rolesandpermissions.api) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) implementation(projects.tests.testutils) diff --git a/features/changeroommemberroles/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt b/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt similarity index 73% rename from features/changeroommemberroles/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt rename to features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt index 16b404ab5e..3e3e5b1a43 100644 --- a/features/changeroommemberroles/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt +++ b/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt @@ -5,12 +5,12 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.changeroommemberroles.test +package io.element.android.features.rolesandpermissions.test import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesEntryPoint -import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.tests.testutils.lambda.lambdaError diff --git a/features/roomdetails/impl/build.gradle.kts b/features/roomdetails/impl/build.gradle.kts index 896fc8dbdc..881510ff28 100644 --- a/features/roomdetails/impl/build.gradle.kts +++ b/features/roomdetails/impl/build.gradle.kts @@ -56,7 +56,7 @@ dependencies { implementation(projects.features.verifysession.api) implementation(projects.features.reportroom.api) implementation(projects.features.roommembermoderation.api) - implementation(projects.features.changeroommemberroles.api) + implementation(projects.features.rolesandpermissions.api) implementation(projects.features.invitepeople.api) testCommonDependencies(libs, true) @@ -69,7 +69,7 @@ dependencies { testImplementation(projects.libraries.usersearch.test) testImplementation(projects.libraries.featureflag.test) testImplementation(projects.features.call.test) - testImplementation(projects.features.changeroommemberroles.test) + testImplementation(projects.features.rolesandpermissions.test) testImplementation(projects.features.knockrequests.test) testImplementation(projects.features.messages.test) testImplementation(projects.features.poll.test) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index d31adaae8d..06e4818284 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -26,8 +26,8 @@ import io.element.android.annotations.ContributesNode import io.element.android.appconfig.LearnMoreConfig import io.element.android.features.call.api.CallType import io.element.android.features.call.api.ElementCallEntryPoint -import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesEntryPoint -import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType import io.element.android.features.knockrequests.api.list.KnockRequestsListEntryPoint import io.element.android.features.messages.api.MessagesEntryPoint import io.element.android.features.poll.api.history.PollHistoryEntryPoint diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsFlowNode.kt index b2b7d46780..80ef19a903 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsFlowNode.kt @@ -20,8 +20,8 @@ import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode -import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesEntryPoint -import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType import io.element.android.features.roomdetails.impl.rolesandpermissions.permissions.ChangeRoomPermissionsNode import io.element.android.features.roomdetails.impl.rolesandpermissions.permissions.ChangeRoomPermissionsSection import io.element.android.libraries.architecture.BackstackView diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt index ebe92332e8..ad939734df 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt @@ -12,7 +12,7 @@ import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat import io.element.android.features.call.test.FakeElementCallEntryPoint -import io.element.android.features.changeroommemberroles.test.FakeChangeRoomMemberRolesEntryPoint +import io.element.android.features.rolesandpermissions.test.FakeChangeRoomMemberRolesEntryPoint import io.element.android.features.knockrequests.test.FakeKnockRequestsListEntryPoint import io.element.android.features.messages.test.FakeMessagesEntryPoint import io.element.android.features.poll.test.history.FakePollHistoryEntryPoint From 499502b5afe83dceb3090323f24efb4cd8c6ba61 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 3 Nov 2025 18:29:51 +0100 Subject: [PATCH 024/216] change(roles and permissions): import codefrom roomdetails module --- .../features/home/impl/HomeFlowNode.kt | 5 +++-- .../api/RolesAndPermissionsEntryPoint.kt | 12 ++++++++++ .../DefaultRolesAndPermissionsEntryPoint.kt | 22 +++++++++++++++++++ .../impl}/RolesAndPermissionsFlowNode.kt | 9 ++++---- .../impl/analytics/AnalyticUtils.kt | 2 +- .../permissions/ChangeRoomPermissionsEvent.kt | 2 +- .../permissions/ChangeRoomPermissionsNode.kt | 2 +- .../ChangeRoomPermissionsPresenter.kt | 4 ++-- .../permissions/ChangeRoomPermissionsState.kt | 2 +- .../ChangeRoomPermissionsStateProvider.kt | 2 +- .../permissions/ChangeRoomPermissionsView.kt | 2 +- .../impl/roles/ChangeRolesStateProvider.kt | 2 +- .../roles/ChangeRoomMemberRolesRootNode.kt | 1 + .../impl/root}/RolesAndPermissionsEvents.kt | 2 +- .../impl/root}/RolesAndPermissionsNode.kt | 2 +- .../root}/RolesAndPermissionsPresenter.kt | 2 +- .../impl/root}/RolesAndPermissionsState.kt | 2 +- .../root}/RolesAndPermissionsStateProvider.kt | 2 +- .../impl/root}/RolesAndPermissionsView.kt | 2 +- .../FakeChangeRoomMemberRolesEntryPoint.kt | 4 ++-- .../roomdetails/impl/RoomDetailsFlowNode.kt | 9 ++++---- 21 files changed, 65 insertions(+), 27 deletions(-) create mode 100644 features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/RolesAndPermissionsEntryPoint.kt create mode 100644 features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/DefaultRolesAndPermissionsEntryPoint.kt rename features/{roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl}/RolesAndPermissionsFlowNode.kt (93%) rename features/{roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions}/impl/analytics/AnalyticUtils.kt (97%) rename features/{roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl}/permissions/ChangeRoomPermissionsEvent.kt (87%) rename features/{roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl}/permissions/ChangeRoomPermissionsNode.kt (95%) rename features/{roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl}/permissions/ChangeRoomPermissionsPresenter.kt (97%) rename features/{roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl}/permissions/ChangeRoomPermissionsState.kt (91%) rename features/{roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl}/permissions/ChangeRoomPermissionsStateProvider.kt (97%) rename features/{roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl}/permissions/ChangeRoomPermissionsView.kt (99%) rename features/{roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root}/RolesAndPermissionsEvents.kt (88%) rename features/{roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root}/RolesAndPermissionsNode.kt (97%) rename features/{roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root}/RolesAndPermissionsPresenter.kt (98%) rename features/{roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root}/RolesAndPermissionsState.kt (88%) rename features/{roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root}/RolesAndPermissionsStateProvider.kt (97%) rename features/{roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions => rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root}/RolesAndPermissionsView.kt (99%) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt index 91ef53d9de..cf1d9e829d 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt @@ -28,8 +28,6 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode -import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint -import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType import io.element.android.features.home.api.HomeEntryPoint import io.element.android.features.home.impl.components.RoomListMenuAction import io.element.android.features.home.impl.model.RoomListRoomSummary @@ -40,6 +38,9 @@ import io.element.android.features.invite.api.declineandblock.DeclineInviteAndBl import io.element.android.features.leaveroom.api.LeaveRoomRenderer import io.element.android.features.logout.api.direct.DirectLogoutView import io.element.android.features.reportroom.api.ReportRoomEntryPoint +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType +import io.element.android.features.rolesandpermissions.api.RolesAndPermissionsEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.appyx.launchMolecule diff --git a/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/RolesAndPermissionsEntryPoint.kt b/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/RolesAndPermissionsEntryPoint.kt new file mode 100644 index 0000000000..4ed1fa5c90 --- /dev/null +++ b/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/RolesAndPermissionsEntryPoint.kt @@ -0,0 +1,12 @@ +/* + * 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. + */ + +package io.element.android.features.rolesandpermissions.api + +import io.element.android.libraries.architecture.SimpleFeatureEntryPoint + +fun interface RolesAndPermissionsEntryPoint : SimpleFeatureEntryPoint diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/DefaultRolesAndPermissionsEntryPoint.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/DefaultRolesAndPermissionsEntryPoint.kt new file mode 100644 index 0000000000..a889b8266c --- /dev/null +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/DefaultRolesAndPermissionsEntryPoint.kt @@ -0,0 +1,22 @@ +/* + * 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. + */ + +package io.element.android.features.rolesandpermissions.impl + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import dev.zacsweers.metro.ContributesBinding +import io.element.android.features.rolesandpermissions.api.RolesAndPermissionsEntryPoint +import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.di.RoomScope + +@ContributesBinding(RoomScope::class) +class DefaultRolesAndPermissionsEntryPoint : RolesAndPermissionsEntryPoint { + override fun createNode(parentNode: Node, buildContext: BuildContext): Node { + return parentNode.createNode(buildContext) + } +} diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsFlowNode.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt similarity index 93% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsFlowNode.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt index 80ef19a903..36e4d233ca 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsFlowNode.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.rolesandpermissions +package io.element.android.features.rolesandpermissions.impl import android.os.Parcelable import androidx.compose.runtime.Composable @@ -22,8 +22,9 @@ import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType -import io.element.android.features.roomdetails.impl.rolesandpermissions.permissions.ChangeRoomPermissionsNode -import io.element.android.features.roomdetails.impl.rolesandpermissions.permissions.ChangeRoomPermissionsSection +import io.element.android.features.rolesandpermissions.impl.permissions.ChangeRoomPermissionsNode +import io.element.android.features.rolesandpermissions.impl.permissions.ChangeRoomPermissionsSection +import io.element.android.features.rolesandpermissions.impl.root.RolesAndPermissionsNode import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode @@ -37,8 +38,8 @@ import kotlinx.parcelize.Parcelize class RolesAndPermissionsFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, - private val changeRoomMemberRolesEntryPoint: ChangeRoomMemberRolesEntryPoint, private val joinedRoom: JoinedRoom, + private val changeRoomMemberRolesEntryPoint: ChangeRoomMemberRolesEntryPoint, ) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.AdminSettings, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/analytics/AnalyticUtils.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/analytics/AnalyticUtils.kt similarity index 97% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/analytics/AnalyticUtils.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/analytics/AnalyticUtils.kt index 09d57af034..a609b79087 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/analytics/AnalyticUtils.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/analytics/AnalyticUtils.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.analytics +package io.element.android.features.rolesandpermissions.impl.analytics import im.vector.app.features.analytics.plan.RoomModeration import io.element.android.libraries.matrix.api.room.RoomMember diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsEvent.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsEvent.kt similarity index 87% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsEvent.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsEvent.kt index a4c49c7ac3..f17960c790 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsEvent.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsEvent.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.rolesandpermissions.permissions +package io.element.android.features.rolesandpermissions.impl.permissions import io.element.android.libraries.matrix.api.room.RoomMember diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsNode.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsNode.kt similarity index 95% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsNode.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsNode.kt index cebcc56e7f..c0c7d016dd 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsNode.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsNode.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.rolesandpermissions.permissions +package io.element.android.features.rolesandpermissions.impl.permissions import android.os.Parcelable import androidx.compose.runtime.Composable diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsPresenter.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt similarity index 97% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsPresenter.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt index ec90df1d5e..ce59aa1907 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsPresenter.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.rolesandpermissions.permissions +package io.element.android.features.rolesandpermissions.impl.permissions import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -18,7 +18,7 @@ import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject -import io.element.android.features.roomdetails.impl.analytics.trackPermissionChangeAnalytics +import io.element.android.features.rolesandpermissions.impl.analytics.trackPermissionChangeAnalytics import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.room.JoinedRoom diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsState.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt similarity index 91% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsState.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt index 5e7b77560a..da106c888b 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsState.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.rolesandpermissions.permissions +package io.element.android.features.rolesandpermissions.impl.permissions import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStateProvider.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStateProvider.kt similarity index 97% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStateProvider.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStateProvider.kt index 07d3455a90..484caa6dbe 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsStateProvider.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStateProvider.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.rolesandpermissions.permissions +package io.element.android.features.rolesandpermissions.impl.permissions import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt similarity index 99% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsView.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt index 6acf4935dc..52c4e254dd 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.rolesandpermissions.permissions +package io.element.android.features.rolesandpermissions.impl.permissions import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.fillMaxSize diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesStateProvider.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesStateProvider.kt index d054f459aa..5b0cadd024 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesStateProvider.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesStateProvider.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.rolesandpermissions.impl.changerole +package io.element.android.features.rolesandpermissions.impl.roles import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRoomMemberRolesRootNode.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRoomMemberRolesRootNode.kt index 33fbd197ae..0f588667c6 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRoomMemberRolesRootNode.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRoomMemberRolesRootNode.kt @@ -21,6 +21,7 @@ import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.appnav.di.RoomGraphFactory import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint +import io.element.android.features.rolesandpermissions.api.RolesAndPermissionsEntryPoint import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.createNode diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsEvents.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsEvents.kt similarity index 88% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsEvents.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsEvents.kt index 8b98f78d97..3864f7997c 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsEvents.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsEvents.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.rolesandpermissions +package io.element.android.features.rolesandpermissions.impl.root import io.element.android.libraries.matrix.api.room.RoomMember diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsNode.kt similarity index 97% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsNode.kt index 29394398d3..f5c7b2e7c2 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsNode.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.rolesandpermissions +package io.element.android.features.rolesandpermissions.impl.root import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsPresenter.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsPresenter.kt similarity index 98% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsPresenter.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsPresenter.kt index 2ad4c84028..4f8bf2d201 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsPresenter.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsPresenter.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.rolesandpermissions +package io.element.android.features.rolesandpermissions.impl.root import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsState.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsState.kt similarity index 88% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsState.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsState.kt index 24f645a309..8be6fcaf31 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsState.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsState.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.rolesandpermissions +package io.element.android.features.rolesandpermissions.impl.root import io.element.android.libraries.architecture.AsyncAction diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsStateProvider.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsStateProvider.kt similarity index 97% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsStateProvider.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsStateProvider.kt index 211a16d7d1..d134c6e222 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsStateProvider.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsStateProvider.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.rolesandpermissions +package io.element.android.features.rolesandpermissions.impl.root import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt similarity index 99% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsView.kt rename to features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt index b581554a92..11341a75ef 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.rolesandpermissions +package io.element.android.features.rolesandpermissions.impl.root import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding diff --git a/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt b/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt index 3e3e5b1a43..a2f9ac0253 100644 --- a/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt +++ b/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt @@ -9,12 +9,12 @@ package io.element.android.features.rolesandpermissions.test import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint +import io.element.android.features.rolesandpermissions.api.RolesAndPermissionsEntryPoint import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.tests.testutils.lambda.lambdaError -class FakeChangeRoomMemberRolesEntryPoint : ChangeRoomMemberRolesEntryPoint { +class FakeChangeRoomMemberRolesEntryPoint : RolesAndPermissionsEntryPoint { override fun createNode( parentNode: Node, buildContext: BuildContext, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index 06e4818284..20e783fb6b 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -26,19 +26,19 @@ import io.element.android.annotations.ContributesNode import io.element.android.appconfig.LearnMoreConfig import io.element.android.features.call.api.CallType import io.element.android.features.call.api.ElementCallEntryPoint -import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint -import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType import io.element.android.features.knockrequests.api.list.KnockRequestsListEntryPoint import io.element.android.features.messages.api.MessagesEntryPoint import io.element.android.features.poll.api.history.PollHistoryEntryPoint import io.element.android.features.reportroom.api.ReportRoomEntryPoint +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint +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.roomdetails.impl.rolesandpermissions.RolesAndPermissionsFlowNode import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyFlowNode import io.element.android.features.userprofile.shared.UserProfileNodeHelper import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint @@ -82,6 +82,7 @@ class RoomDetailsFlowNode( private val outgoingVerificationEntryPoint: OutgoingVerificationEntryPoint, private val reportRoomEntryPoint: ReportRoomEntryPoint, private val changeRoomMemberRolesEntryPoint: ChangeRoomMemberRolesEntryPoint, + private val rolesAndPermissionsEntryPoint: RolesAndPermissionsEntryPoint, ) : BaseFlowNode( backstack = BackStack( initialElement = plugins.filterIsInstance().first().initialElement.toNavTarget(), @@ -343,7 +344,7 @@ class RoomDetailsFlowNode( } is NavTarget.AdminSettings -> { - createNode(buildContext) + rolesAndPermissionsEntryPoint.createNode(this, buildContext) } NavTarget.PinnedMessagesList -> { val params = MessagesEntryPoint.Params( From ec207a548bdbd6cb39c81fd7069b850671584c9e Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 3 Nov 2025 18:35:18 +0100 Subject: [PATCH 025/216] change(roles and permissions): fix localazy strings --- .../impl/permissions/ChangeRoomPermissionsView.kt | 2 +- .../rolesandpermissions/impl/root/RolesAndPermissionsView.kt | 2 +- tools/localazy/config.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt index 52c4e254dd..6275dbf73e 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt @@ -17,7 +17,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.compound.tokens.generated.CompoundIcons -import io.element.android.features.roomdetails.impl.R +import io.element.android.features.rolesandpermissions.impl.R import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.designsystem.components.button.BackButton diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt index 11341a75ef..3ee3a409de 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt @@ -20,7 +20,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons -import io.element.android.features.roomdetails.impl.R +import io.element.android.features.rolesandpermissions.impl.R import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.designsystem.components.async.AsyncActionView diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 82c273db2e..456de15c83 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -366,7 +366,7 @@ ] }, { - "name" : ":features:changeroommemberroles:impl", + "name" : ":features:rolesandpermissions:impl", "includeRegex" : [ "screen_room_change_.*", "screen_room_roles_.*", From 2eec5f8a9afd82cacaf66b563923e0cb9b4481d3 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 3 Nov 2025 20:27:37 +0100 Subject: [PATCH 026/216] quality: fix import in test --- .../android/features/space/impl/DefaultSpaceEntryPointTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt index 696e80eeea..319d1eeeff 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt @@ -16,7 +16,6 @@ import io.element.android.features.space.impl.di.FakeSpaceFlowGraph import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.room.FakeJoinedRoom -import io.element.android.libraries.matrix.test.room.join.FakeJoinRoom import io.element.android.libraries.matrix.test.spaces.FakeSpaceRoomList import io.element.android.libraries.matrix.test.spaces.FakeSpaceService import io.element.android.tests.testutils.lambda.lambdaError From e557ceb702283f02170ac8dbf5eb3e677ab2d762 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 3 Nov 2025 21:40:37 +0100 Subject: [PATCH 027/216] change(roles and permissions): update change permission design --- .../impl/RolesAndPermissionsFlowNode.kt | 20 +--- .../permissions/ChangeRoomPermissionsEvent.kt | 4 +- .../permissions/ChangeRoomPermissionsNode.kt | 20 +--- .../ChangeRoomPermissionsPresenter.kt | 54 ++++++----- .../permissions/ChangeRoomPermissionsState.kt | 58 +++++++++++- .../ChangeRoomPermissionsStateProvider.kt | 24 ++--- .../permissions/ChangeRoomPermissionsView.kt | 91 ++++++------------- .../impl/root/RolesAndPermissionsNode.kt | 9 +- .../impl/root/RolesAndPermissionsView.kt | 19 +--- .../preferences/PreferenceDropdown.kt | 13 +++ 10 files changed, 145 insertions(+), 167 deletions(-) diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt index 36e4d233ca..81ef3b3b1e 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt @@ -23,7 +23,6 @@ import io.element.android.annotations.ContributesNode import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType import io.element.android.features.rolesandpermissions.impl.permissions.ChangeRoomPermissionsNode -import io.element.android.features.rolesandpermissions.impl.permissions.ChangeRoomPermissionsSection import io.element.android.features.rolesandpermissions.impl.root.RolesAndPermissionsNode import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode @@ -59,7 +58,7 @@ class RolesAndPermissionsFlowNode( data object ModeratorList : NavTarget @Parcelize - data class ChangeRoomPermissions(val section: ChangeRoomPermissionsSection) : NavTarget + data object ChangeRoomPermissions: NavTarget } override fun onBuilt() { @@ -84,17 +83,10 @@ class RolesAndPermissionsFlowNode( backstack.push(NavTarget.ModeratorList) } - override fun openEditRoomDetailsPermissions() { - backstack.push(NavTarget.ChangeRoomPermissions(ChangeRoomPermissionsSection.RoomDetails)) + override fun openEditPermissions() { + backstack.push(NavTarget.ChangeRoomPermissions) } - override fun openMessagesAndContentPermissions() { - backstack.push(NavTarget.ChangeRoomPermissions(ChangeRoomPermissionsSection.MessagesAndContent)) - } - - override fun openModerationPermissions() { - backstack.push(NavTarget.ChangeRoomPermissions(ChangeRoomPermissionsSection.MembershipModeration)) - } } createNode( buildContext = buildContext, @@ -118,11 +110,7 @@ class RolesAndPermissionsFlowNode( ) } is NavTarget.ChangeRoomPermissions -> { - val inputs = ChangeRoomPermissionsNode.Inputs(navTarget.section) - createNode( - buildContext = buildContext, - plugins = listOf(inputs), - ) + createNode(buildContext = buildContext) } } } diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsEvent.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsEvent.kt index f17960c790..ee2984205d 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsEvent.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsEvent.kt @@ -7,10 +7,8 @@ package io.element.android.features.rolesandpermissions.impl.permissions -import io.element.android.libraries.matrix.api.room.RoomMember - interface ChangeRoomPermissionsEvent { - data class ChangeMinimumRoleForAction(val action: RoomPermissionType, val role: RoomMember.Role) : ChangeRoomPermissionsEvent + data class ChangeMinimumRoleForAction(val action: RoomPermissionType, val role: SelectableRole) : ChangeRoomPermissionsEvent data object Save : ChangeRoomPermissionsEvent data object Exit : ChangeRoomPermissionsEvent data object ResetPendingActions : ChangeRoomPermissionsEvent diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsNode.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsNode.kt index c0c7d016dd..b8261abd89 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsNode.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsNode.kt @@ -7,7 +7,6 @@ package io.element.android.features.rolesandpermissions.impl.permissions -import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext @@ -16,25 +15,15 @@ import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode -import io.element.android.libraries.architecture.NodeInputs -import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.RoomScope -import kotlinx.parcelize.Parcelize @ContributesNode(RoomScope::class) @AssistedInject class ChangeRoomPermissionsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, - presenterFactory: ChangeRoomPermissionsPresenter.Factory, + private val presenter: ChangeRoomPermissionsPresenter, ) : Node(buildContext, plugins = plugins) { - @Parcelize - data class Inputs( - val section: ChangeRoomPermissionsSection, - ) : NodeInputs, Parcelable - - private val inputs: Inputs = inputs() - private val presenter = presenterFactory.create(inputs.section) @Composable override fun View(modifier: Modifier) { @@ -46,10 +35,3 @@ class ChangeRoomPermissionsNode( ) } } - -@Parcelize -enum class ChangeRoomPermissionsSection : Parcelable { - RoomDetails, - MessagesAndContent, - MembershipModeration, -} diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt index ce59aa1907..e073a48171 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt @@ -15,50 +15,50 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue -import dev.zacsweers.metro.Assisted -import dev.zacsweers.metro.AssistedFactory -import dev.zacsweers.metro.AssistedInject +import dev.zacsweers.metro.Inject import io.element.android.features.rolesandpermissions.impl.analytics.trackPermissionChangeAnalytics import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.room.JoinedRoom +import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues import io.element.android.services.analytics.api.AnalyticsService -import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentMapOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -@AssistedInject +@Inject class ChangeRoomPermissionsPresenter( - @Assisted private val section: ChangeRoomPermissionsSection, private val room: JoinedRoom, private val analyticsService: AnalyticsService, ) : Presenter { companion object { - internal fun itemsForSection(section: ChangeRoomPermissionsSection) = when (section) { - ChangeRoomPermissionsSection.RoomDetails -> persistentListOf( + private fun itemsForSection(section: RoomPermissionsSection) = when (section) { + RoomPermissionsSection.RoomDetails -> persistentListOf( RoomPermissionType.ROOM_NAME, RoomPermissionType.ROOM_AVATAR, RoomPermissionType.ROOM_TOPIC, ) - ChangeRoomPermissionsSection.MessagesAndContent -> persistentListOf( + RoomPermissionsSection.MessagesAndContent -> persistentListOf( RoomPermissionType.SEND_EVENTS, RoomPermissionType.REDACT_EVENTS, ) - ChangeRoomPermissionsSection.MembershipModeration -> persistentListOf( + RoomPermissionsSection.MembershipModeration -> persistentListOf( RoomPermissionType.INVITE, RoomPermissionType.KICK, RoomPermissionType.BAN, ) } - } - @AssistedFactory - interface Factory { - fun create(section: ChangeRoomPermissionsSection): ChangeRoomPermissionsPresenter + + internal fun buildItems(forSpace: Boolean) = persistentMapOf( + RoomPermissionsSection.RoomDetails to itemsForSection(RoomPermissionsSection.RoomDetails), + RoomPermissionsSection.MessagesAndContent to itemsForSection(RoomPermissionsSection.MessagesAndContent), + RoomPermissionsSection.MembershipModeration to itemsForSection(RoomPermissionsSection.MembershipModeration), + ) } - private val items: ImmutableList = itemsForSection(section) + private val items = buildItems(forSpace = room.info().isSpace) private var initialPermissions by mutableStateOf(null) private var currentPermissions by mutableStateOf(null) @@ -80,15 +80,20 @@ class ChangeRoomPermissionsPresenter( fun handleEvent(event: ChangeRoomPermissionsEvent) { when (event) { is ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction -> { + val powerLevel = when (event.role) { + SelectableRole.Admin -> RoomMember.Role.Admin.powerLevel + SelectableRole.Moderator -> RoomMember.Role.Moderator.powerLevel + SelectableRole.Everyone -> RoomMember.Role.User.powerLevel + } currentPermissions = when (event.action) { - RoomPermissionType.BAN -> currentPermissions?.copy(ban = event.role.powerLevel) - RoomPermissionType.INVITE -> currentPermissions?.copy(invite = event.role.powerLevel) - RoomPermissionType.KICK -> currentPermissions?.copy(kick = event.role.powerLevel) - RoomPermissionType.SEND_EVENTS -> currentPermissions?.copy(sendEvents = event.role.powerLevel) - RoomPermissionType.REDACT_EVENTS -> currentPermissions?.copy(redactEvents = event.role.powerLevel) - RoomPermissionType.ROOM_NAME -> currentPermissions?.copy(roomName = event.role.powerLevel) - RoomPermissionType.ROOM_AVATAR -> currentPermissions?.copy(roomAvatar = event.role.powerLevel) - RoomPermissionType.ROOM_TOPIC -> currentPermissions?.copy(roomTopic = event.role.powerLevel) + RoomPermissionType.BAN -> currentPermissions?.copy(ban = powerLevel) + RoomPermissionType.INVITE -> currentPermissions?.copy(invite = powerLevel) + RoomPermissionType.KICK -> currentPermissions?.copy(kick = powerLevel) + RoomPermissionType.SEND_EVENTS -> currentPermissions?.copy(sendEvents = powerLevel) + RoomPermissionType.REDACT_EVENTS -> currentPermissions?.copy(redactEvents = powerLevel) + RoomPermissionType.ROOM_NAME -> currentPermissions?.copy(roomName = powerLevel) + RoomPermissionType.ROOM_AVATAR -> currentPermissions?.copy(roomAvatar = powerLevel) + RoomPermissionType.ROOM_TOPIC -> currentPermissions?.copy(roomTopic = powerLevel) } } is ChangeRoomPermissionsEvent.Save -> coroutineScope.save() @@ -106,9 +111,8 @@ class ChangeRoomPermissionsPresenter( } } return ChangeRoomPermissionsState( - section = section, currentPermissions = currentPermissions, - items = items, + itemsBySection = items, hasChanges = hasChanges, saveAction = saveAction, confirmExitAction = confirmExitAction, diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt index da106c888b..e91dd9fcb6 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt @@ -7,19 +7,71 @@ package io.element.android.features.rolesandpermissions.impl.permissions +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.ui.res.stringResource +import io.element.android.features.rolesandpermissions.impl.R import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.designsystem.components.preferences.DropdownOption +import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.ImmutableMap data class ChangeRoomPermissionsState( - val section: ChangeRoomPermissionsSection, val currentPermissions: RoomPowerLevelsValues?, - val items: ImmutableList, + val itemsBySection: ImmutableMap>, val hasChanges: Boolean, val saveAction: AsyncAction, val confirmExitAction: AsyncAction, val eventSink: (ChangeRoomPermissionsEvent) -> Unit, -) +){ + fun selectedRoleForType(type: RoomPermissionType): SelectableRole? { + if(currentPermissions == null) return null + val role = when (type) { + RoomPermissionType.BAN -> RoomMember.Role.forPowerLevel(currentPermissions.ban) + RoomPermissionType.INVITE -> RoomMember.Role.forPowerLevel(currentPermissions.invite) + RoomPermissionType.KICK -> RoomMember.Role.forPowerLevel(currentPermissions.kick) + RoomPermissionType.SEND_EVENTS -> RoomMember.Role.forPowerLevel(currentPermissions.sendEvents) + RoomPermissionType.REDACT_EVENTS -> RoomMember.Role.forPowerLevel(currentPermissions.redactEvents) + RoomPermissionType.ROOM_NAME -> RoomMember.Role.forPowerLevel(currentPermissions.roomName) + RoomPermissionType.ROOM_AVATAR -> RoomMember.Role.forPowerLevel(currentPermissions.roomAvatar) + RoomPermissionType.ROOM_TOPIC -> RoomMember.Role.forPowerLevel(currentPermissions.roomTopic) + } + return when(role){ + is RoomMember.Role.Owner, + RoomMember.Role.Admin -> SelectableRole.Admin + RoomMember.Role.Moderator -> SelectableRole.Moderator + RoomMember.Role.User -> SelectableRole.Everyone + } + } + +} + +enum class RoomPermissionsSection { + RoomDetails, + MessagesAndContent, + MembershipModeration, +} + +enum class SelectableRole: DropdownOption { + Admin { + @Composable + @ReadOnlyComposable + override fun getText(): String = stringResource(R.string.screen_room_member_list_role_administrator) + }, + Moderator { + @Composable + @ReadOnlyComposable + override fun getText(): String = stringResource(R.string.screen_room_member_list_role_moderator) + }, + Everyone { + @Composable + @ReadOnlyComposable + override fun getText(): String = stringResource(R.string.screen_room_change_permissions_everyone) + } +} + enum class RoomPermissionType { BAN, diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStateProvider.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStateProvider.kt index 484caa6dbe..2196c4aa46 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStateProvider.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStateProvider.kt @@ -11,41 +11,33 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues -import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableMap class ChangeRoomPermissionsStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( - aChangeRoomPermissionsState(section = ChangeRoomPermissionsSection.RoomDetails), - aChangeRoomPermissionsState(section = ChangeRoomPermissionsSection.MessagesAndContent), - aChangeRoomPermissionsState(section = ChangeRoomPermissionsSection.MembershipModeration), - aChangeRoomPermissionsState(section = ChangeRoomPermissionsSection.RoomDetails, hasChanges = true), - aChangeRoomPermissionsState(section = ChangeRoomPermissionsSection.RoomDetails, hasChanges = true, saveAction = AsyncAction.Loading), + aChangeRoomPermissionsState(), + aChangeRoomPermissionsState(hasChanges = true), + aChangeRoomPermissionsState(hasChanges = true, saveAction = AsyncAction.Loading), aChangeRoomPermissionsState( - section = ChangeRoomPermissionsSection.RoomDetails, hasChanges = true, saveAction = AsyncAction.Failure(IllegalStateException("Failed to save changes")) ), - aChangeRoomPermissionsState( - section = ChangeRoomPermissionsSection.RoomDetails, - hasChanges = true, - confirmExitAction = AsyncAction.ConfirmingNoParams, - ), + aChangeRoomPermissionsState(hasChanges = true, confirmExitAction = AsyncAction.ConfirmingNoParams), ) } internal fun aChangeRoomPermissionsState( - section: ChangeRoomPermissionsSection, currentPermissions: RoomPowerLevelsValues = previewPermissions(), - items: List = ChangeRoomPermissionsPresenter.itemsForSection(section), + itemsBySection: Map> = ChangeRoomPermissionsPresenter.buildItems(false), hasChanges: Boolean = false, saveAction: AsyncAction = AsyncAction.Uninitialized, confirmExitAction: AsyncAction = AsyncAction.Uninitialized, eventSink: (ChangeRoomPermissionsEvent) -> Unit = {}, ) = ChangeRoomPermissionsState( - section = section, currentPermissions = currentPermissions, - items = items.toImmutableList(), + itemsBySection = itemsBySection.toImmutableMap(), hasChanges = hasChanges, saveAction = saveAction, confirmExitAction = confirmExitAction, diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt index 6275dbf73e..85a75f9f35 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt @@ -23,6 +23,7 @@ import io.element.android.libraries.designsystem.components.async.AsyncActionVie import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.components.list.ListItemContent +import io.element.android.libraries.designsystem.components.preferences.PreferenceDropdown import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.IconSource @@ -36,6 +37,7 @@ import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.toImmutableList @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -50,13 +52,8 @@ fun ChangeRoomPermissionsView( Scaffold( modifier = modifier, topBar = { - val title = when (state.section) { - ChangeRoomPermissionsSection.RoomDetails -> stringResource(R.string.screen_room_change_permissions_room_details) - ChangeRoomPermissionsSection.MessagesAndContent -> stringResource(R.string.screen_room_change_permissions_messages_and_content) - ChangeRoomPermissionsSection.MembershipModeration -> stringResource(R.string.screen_room_change_permissions_member_moderation) - } TopAppBar( - titleStr = title, + titleStr = stringResource(R.string.screen_room_roles_and_permissions_permissions_header), navigationIcon = { BackButton(onClick = { state.eventSink(ChangeRoomPermissionsEvent.Exit) }) }, @@ -75,29 +72,25 @@ fun ChangeRoomPermissionsView( .padding(padding) .fillMaxSize() ) { - for ((index, permissionItem) in state.items.withIndex()) { + state.itemsBySection.onEachIndexed { index, (section, items) -> item { - ListSectionHeader(titleForSection(item = permissionItem), hasDivider = index > 0) - SelectRoleItem( - permissionsItem = permissionItem, - role = RoomMember.Role.Admin, - currentPermissions = state.currentPermissions - ) { item, role -> - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(item, role)) - } - SelectRoleItem( - permissionsItem = permissionItem, - role = RoomMember.Role.Moderator, - currentPermissions = state.currentPermissions - ) { item, role -> - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(item, role)) - } - SelectRoleItem( - permissionsItem = permissionItem, - role = RoomMember.Role.User, - currentPermissions = state.currentPermissions - ) { item, role -> - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(item, role)) + ListSectionHeader(titleForSection(section), hasDivider = index>0) + } + for (permissionType in items) { + item { + PreferenceDropdown( + title = titleForType(permissionType), + selectedOption = state.selectedRoleForType(permissionType), + options = SelectableRole.entries.toImmutableList(), + onSelectOption = { role -> + state.eventSink( + ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction( + action = permissionType, + role = role + ) + ) + } + ) } } } @@ -126,47 +119,15 @@ fun ChangeRoomPermissionsView( onErrorDismiss = {}, ) } - @Composable -private fun SelectRoleItem( - permissionsItem: RoomPermissionType, - role: RoomMember.Role, - currentPermissions: RoomPowerLevelsValues?, - onClick: (RoomPermissionType, RoomMember.Role) -> Unit -) { - val title = when (role) { - RoomMember.Role.Admin -> stringResource(R.string.screen_room_change_permissions_administrators) - RoomMember.Role.Moderator -> stringResource(R.string.screen_room_change_permissions_moderators) - RoomMember.Role.User -> stringResource(R.string.screen_room_change_permissions_everyone) - else -> error("Unsupported role selected: $role") - } - ListItem( - headlineContent = { Text(text = title) }, - trailingContent = if (currentPermissions?.isSelected(permissionsItem, role).orFalse()) { - ListItemContent.Icon(IconSource.Vector(CompoundIcons.Check())) - } else { - null - }, - style = ListItemStyle.Primary, - onClick = { onClick(permissionsItem, role) }, - ) -} - -private fun RoomPowerLevelsValues.isSelected(item: RoomPermissionType, role: RoomMember.Role): Boolean { - return when (item) { - RoomPermissionType.BAN -> RoomMember.Role.forPowerLevel(ban) == role - RoomPermissionType.INVITE -> RoomMember.Role.forPowerLevel(invite) == role - RoomPermissionType.KICK -> RoomMember.Role.forPowerLevel(kick) == role - RoomPermissionType.SEND_EVENTS -> RoomMember.Role.forPowerLevel(sendEvents) == role - RoomPermissionType.REDACT_EVENTS -> RoomMember.Role.forPowerLevel(redactEvents) == role - RoomPermissionType.ROOM_NAME -> RoomMember.Role.forPowerLevel(roomName) == role - RoomPermissionType.ROOM_AVATAR -> RoomMember.Role.forPowerLevel(roomAvatar) == role - RoomPermissionType.ROOM_TOPIC -> RoomMember.Role.forPowerLevel(roomTopic) == role - } +private fun titleForSection(section: RoomPermissionsSection): String = when (section) { + RoomPermissionsSection.RoomDetails -> stringResource(R.string.screen_room_change_permissions_room_details) + RoomPermissionsSection.MessagesAndContent -> stringResource(R.string.screen_room_change_permissions_messages_and_content) + RoomPermissionsSection.MembershipModeration -> stringResource(R.string.screen_room_change_permissions_member_moderation) } @Composable -private fun titleForSection(item: RoomPermissionType): String = when (item) { +private fun titleForType(type: RoomPermissionType): String = when (type) { RoomPermissionType.INVITE -> stringResource(R.string.screen_room_change_permissions_invite_people) RoomPermissionType.KICK -> stringResource(R.string.screen_room_change_permissions_remove_people) RoomPermissionType.BAN -> stringResource(R.string.screen_room_change_permissions_ban_people) diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsNode.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsNode.kt index f5c7b2e7c2..da06fd1eae 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsNode.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsNode.kt @@ -39,9 +39,8 @@ class RolesAndPermissionsNode( interface Callback : Plugin, RolesAndPermissionsNavigator { override fun openAdminList() override fun openModeratorList() - override fun openEditRoomDetailsPermissions() - override fun openMessagesAndContentPermissions() - override fun openModerationPermissions() + override fun openEditPermissions() + override fun onBackClick() {} } @@ -85,7 +84,5 @@ interface RolesAndPermissionsNavigator { fun onBackClick() {} fun openAdminList() {} fun openModeratorList() {} - fun openEditRoomDetailsPermissions() {} - fun openMessagesAndContentPermissions() {} - fun openModerationPermissions() {} + fun openEditPermissions() {} } diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt index 3ee3a409de..87b03fef92 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt @@ -78,25 +78,16 @@ fun RolesAndPermissionsView( leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Edit())) ) } - ListSectionHeader(title = stringResource(R.string.screen_room_roles_and_permissions_permissions_header), hasDivider = true) + HorizontalDivider() ListItem( - headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_room_details)) }, - leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Info())), - onClick = { rolesAndPermissionsNavigator.openEditRoomDetailsPermissions() }, - ) - ListItem( - headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_messages_and_content)) }, - leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Chat())), - onClick = { rolesAndPermissionsNavigator.openMessagesAndContentPermissions() }, - ) - ListItem( - headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_member_moderation)) }, - leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.User())), - onClick = { rolesAndPermissionsNavigator.openModerationPermissions() }, + headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_permissions_header)) }, + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Settings())), + onClick = { rolesAndPermissionsNavigator.openEditPermissions() }, ) HorizontalDivider() ListItem( headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_reset)) }, + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Delete())), onClick = { state.eventSink(RolesAndPermissionsEvents.ResetPermissions) }, style = ListItemStyle.Destructive, ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDropdown.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDropdown.kt index 7be47338ca..ebac860c7e 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDropdown.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDropdown.kt @@ -10,9 +10,11 @@ package io.element.android.libraries.designsystem.components.preferences import androidx.annotation.DrawableRes +import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.size import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults @@ -26,6 +28,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.list.ListItemContent @@ -33,6 +36,7 @@ import io.element.android.libraries.designsystem.components.preferences.componen import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem +import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.ListItem import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.toEnabledColor @@ -144,6 +148,15 @@ private fun DropdownTrailingContent( style = ElementTheme.typography.fontBodyMdRegular ) }, + trailingIcon = { + if(option == selectedOption){ + Icon( + imageVector = CompoundIcons.Check(), + contentDescription = null, + tint = ElementTheme.colors.iconSuccessPrimary, + ) + } + }, onClick = { onSelectOption(option) onExpandedChange(false) From be807f4b5c644d4f9e446c4ade81538faa7f105b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 4 Nov 2025 15:43:23 +0100 Subject: [PATCH 028/216] Revert "NotificationDataFactory: improve API" This reverts commit 7d7ea5d67c35bc7996b71fedb5b562fbc1710c9d. # Conflicts: # libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt --- .../notifications/NotificationDataFactory.kt | 32 ++++++++++++------- .../notifications/NotificationRenderer.kt | 16 +++------- .../NotificationDataFactoryTest.kt | 15 ++++++--- .../fake/FakeNotificationDataFactory.kt | 20 +++++++----- 4 files changed, 46 insertions(+), 37 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactory.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactory.kt index ab8406f1af..33b2df410c 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactory.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactory.kt @@ -28,26 +28,30 @@ import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiab import io.element.android.services.toolbox.api.strings.StringProvider interface NotificationDataFactory { - suspend fun List.toNotifications( + suspend fun toNotifications( + messages: List, imageLoader: ImageLoader, notificationAccountParams: NotificationAccountParams, ): List @JvmName("toNotificationInvites") @Suppress("INAPPLICABLE_JVM_NAME") - fun List.toNotifications( + fun toNotifications( + invites: List, notificationAccountParams: NotificationAccountParams, ): List @JvmName("toNotificationSimpleEvents") @Suppress("INAPPLICABLE_JVM_NAME") - fun List.toNotifications( + fun toNotifications( + simpleEvents: List, notificationAccountParams: NotificationAccountParams, ): List @JvmName("toNotificationFallbackEvents") @Suppress("INAPPLICABLE_JVM_NAME") - fun List.toNotifications( + fun toNotifications( + fallback: List, notificationAccountParams: NotificationAccountParams, ): List @@ -68,11 +72,12 @@ class DefaultNotificationDataFactory( private val activeNotificationsProvider: ActiveNotificationsProvider, private val stringProvider: StringProvider, ) : NotificationDataFactory { - override suspend fun List.toNotifications( + override suspend fun toNotifications( + messages: List, imageLoader: ImageLoader, notificationAccountParams: NotificationAccountParams, ): List { - val messagesToDisplay = filterNot { it.canNotBeDisplayed() } + val messagesToDisplay = messages.filterNot { it.canNotBeDisplayed() } .groupBy { it.roomId } return messagesToDisplay.flatMap { (roomId, events) -> val roomName = events.lastOrNull()?.roomName ?: roomId.value @@ -109,10 +114,11 @@ class DefaultNotificationDataFactory( @JvmName("toNotificationInvites") @Suppress("INAPPLICABLE_JVM_NAME") - override fun List.toNotifications( + override fun toNotifications( + invites: List, notificationAccountParams: NotificationAccountParams, ): List { - return map { event -> + return invites.map { event -> OneShotNotification( tag = event.roomId.value, notification = notificationCreator.createRoomInvitationNotification(notificationAccountParams, event), @@ -125,10 +131,11 @@ class DefaultNotificationDataFactory( @JvmName("toNotificationSimpleEvents") @Suppress("INAPPLICABLE_JVM_NAME") - override fun List.toNotifications( + override fun toNotifications( + simpleEvents: List, notificationAccountParams: NotificationAccountParams, ): List { - return map { event -> + return simpleEvents.map { event -> OneShotNotification( tag = event.eventId.value, notification = notificationCreator.createSimpleEventNotification(notificationAccountParams, event), @@ -141,10 +148,11 @@ class DefaultNotificationDataFactory( @JvmName("toNotificationFallbackEvents") @Suppress("INAPPLICABLE_JVM_NAME") - override fun List.toNotifications( + override fun toNotifications( + fallback: List, notificationAccountParams: NotificationAccountParams, ): List { - return map { event -> + return fallback.map { event -> OneShotNotification( tag = event.eventId.value, notification = notificationCreator.createFallbackNotification(notificationAccountParams, event), diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt index 9f24ccecb0..0cfe083caf 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt @@ -51,18 +51,10 @@ class NotificationRenderer( showSessionId = numberOfAccounts > 1, ) val groupedEvents = eventsToProcess.groupByType() - val roomNotifications = with(notificationDataFactory) { - groupedEvents.roomEvents.toNotifications(imageLoader, notificationAccountParams) - } - val invitationNotifications = with(notificationDataFactory) { - groupedEvents.invitationEvents.toNotifications(notificationAccountParams) - } - val simpleNotifications = with(notificationDataFactory) { - groupedEvents.simpleEvents.toNotifications(notificationAccountParams) - } - val fallbackNotifications = with(notificationDataFactory) { - groupedEvents.fallbackEvents.toNotifications(notificationAccountParams) - } + val roomNotifications = notificationDataFactory.toNotifications(groupedEvents.roomEvents, imageLoader, notificationAccountParams) + val invitationNotifications = notificationDataFactory.toNotifications(groupedEvents.invitationEvents, notificationAccountParams) + val simpleNotifications = notificationDataFactory.toNotifications(groupedEvents.simpleEvents, notificationAccountParams) + val fallbackNotifications = notificationDataFactory.toNotifications(groupedEvents.fallbackEvents, notificationAccountParams) val summaryNotification = notificationDataFactory.createSummaryNotification( roomNotifications = roomNotifications, invitationNotifications = invitationNotifications, diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt index 50e0b5dfb8..6971fcbc41 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt @@ -55,7 +55,9 @@ class NotificationDataFactoryTest { aNotificationAccountParams(), AN_INVITATION_EVENT, ) - val result = listOf(AN_INVITATION_EVENT).toNotifications(aNotificationAccountParams()) + val roomInvitation = listOf(AN_INVITATION_EVENT) + val result = toNotifications(roomInvitation, aNotificationAccountParams()) + assertThat(result).isEqualTo( listOf( OneShotNotification( @@ -75,7 +77,7 @@ class NotificationDataFactoryTest { aNotificationAccountParams(), AN_INVITATION_EVENT, ) - val result = listOf(A_SIMPLE_EVENT).toNotifications(aNotificationAccountParams()) + val result = toNotifications(listOf(A_SIMPLE_EVENT), aNotificationAccountParams()) assertThat(result).containsExactly( OneShotNotification( notification = expectedNotification, @@ -109,7 +111,8 @@ class NotificationDataFactoryTest { threadId = null, ) val fakeImageLoader = FakeImageLoader() - val result = listOf(A_MESSAGE_EVENT).toNotifications( + val result = toNotifications( + messages = listOf(A_MESSAGE_EVENT), notificationAccountParams = aNotificationAccountParams( user = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), ), @@ -125,7 +128,8 @@ class NotificationDataFactoryTest { fun `given a room with only redacted events when mapping to notification then is Empty`() = testWith(notificationDataFactory) { val redactedRoom = A_MESSAGE_EVENT.copy(isRedacted = true) val fakeImageLoader = FakeImageLoader() - val result = listOf(redactedRoom).toNotifications( + val result = toNotifications( + messages = listOf(redactedRoom), notificationAccountParams = aNotificationAccountParams( user = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), ), @@ -164,7 +168,8 @@ class NotificationDataFactoryTest { ) val fakeImageLoader = FakeImageLoader() - val result = roomWithRedactedMessage.toNotifications( + val result = toNotifications( + messages = roomWithRedactedMessage, notificationAccountParams = aNotificationAccountParams( user = MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), ), diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationDataFactory.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationDataFactory.kt index 89326f5ad9..a897dbfc09 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationDataFactory.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationDataFactory.kt @@ -40,35 +40,39 @@ class FakeNotificationDataFactory( var fallbackEventToNotificationsResult: LambdaOneParamRecorder, List> = lambdaRecorder { _ -> emptyList() }, ) : NotificationDataFactory { - override suspend fun List.toNotifications( + override suspend fun toNotifications( + messages: List, imageLoader: ImageLoader, notificationAccountParams: NotificationAccountParams, ): List { - return messageEventToNotificationsResult(this, imageLoader, notificationAccountParams) + return messageEventToNotificationsResult(messages, imageLoader, notificationAccountParams) } @JvmName("toNotificationInvites") @Suppress("INAPPLICABLE_JVM_NAME") - override fun List.toNotifications( + override fun toNotifications( + invites: List, notificationAccountParams: NotificationAccountParams, ): List { - return inviteToNotificationsResult(this) + return inviteToNotificationsResult(invites) } @JvmName("toNotificationSimpleEvents") @Suppress("INAPPLICABLE_JVM_NAME") - override fun List.toNotifications( + override fun toNotifications( + simpleEvents: List, notificationAccountParams: NotificationAccountParams, ): List { - return simpleEventToNotificationsResult(this) + return simpleEventToNotificationsResult(simpleEvents) } @JvmName("toNotificationFallbackEvents") @Suppress("INAPPLICABLE_JVM_NAME") - override fun List.toNotifications( + override fun toNotifications( + fallback: List, notificationAccountParams: NotificationAccountParams, ): List { - return fallbackEventToNotificationsResult(this) + return fallbackEventToNotificationsResult(fallback) } override fun createSummaryNotification( From d968d5aff0d347b9a5fb359ce6045e23c6264e22 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 4 Nov 2025 15:45:08 +0100 Subject: [PATCH 029/216] Remove unused property --- .../impl/notifications/DefaultNotificationDrawerManager.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt index 1677072399..6d52cbfe13 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt @@ -10,7 +10,6 @@ package io.element.android.libraries.push.impl.notifications import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.SingleIn -import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.di.annotations.AppCoroutineScope import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.core.EventId @@ -30,8 +29,6 @@ import io.element.android.services.appnavstate.api.currentSessionId import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -private val loggerTag = LoggerTag("DefaultNotificationDrawerManager", LoggerTag.NotificationLoggerTag) - /** * This class receives notification events as they arrive from the PushHandler calling [onNotifiableEventReceived] and * organise them in order to display them in the notification drawer. From d94e24740474b3247edeb74a8729f004f3f302d7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 2 Apr 2025 14:04:21 +0200 Subject: [PATCH 030/216] Extract SaveChangesDialog to its own file --- .../impl/ChangeRolesView.kt | 5 +-- .../components/dialogs/SaveChangesDialog.kt | 39 +++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SaveChangesDialog.kt diff --git a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesView.kt b/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesView.kt index f9ebd75ca2..7468fd81a3 100644 --- a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesView.kt +++ b/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesView.kt @@ -50,6 +50,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog +import io.element.android.libraries.designsystem.components.dialogs.SaveChangesDialog import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Checkbox @@ -182,9 +183,7 @@ fun ChangeRolesView( confirmationDialog = { confirming -> when (confirming) { is AsyncAction.ConfirmingCancellation -> { - ConfirmationDialog( - title = stringResource(CommonStrings.dialog_unsaved_changes_title), - content = stringResource(CommonStrings.dialog_unsaved_changes_description_android), + SaveChangesDialog( onSubmitClick = { state.eventSink(ChangeRolesEvent.Exit) }, onDismiss = { state.eventSink(ChangeRolesEvent.CloseDialog) } ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SaveChangesDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SaveChangesDialog.kt new file mode 100644 index 0000000000..e630636894 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SaveChangesDialog.kt @@ -0,0 +1,39 @@ +/* + * 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. + */ + +package io.element.android.libraries.designsystem.components.dialogs + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.ui.strings.CommonStrings + +@Composable +fun SaveChangesDialog( + onSubmitClick: () -> Unit, + onDismiss: () -> Unit, + modifier: Modifier = Modifier, + title: String = stringResource(CommonStrings.dialog_unsaved_changes_title), + content: String = stringResource(CommonStrings.dialog_unsaved_changes_description_android), +) = ConfirmationDialog( + modifier = modifier, + title = title, + content = content, + onSubmitClick = onSubmitClick, + onDismiss = onDismiss, +) + +@PreviewsDayNight +@Composable +internal fun SaveChangeDialogPreview() = ElementPreview { + SaveChangesDialog( + onSubmitClick = {}, + onDismiss = {} + ) +} From d1d729f426c8b1de4dc744dd12dec107f5f1b323 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 2 Apr 2025 14:06:10 +0200 Subject: [PATCH 031/216] Use SaveChangesDialog --- .../android/features/poll/impl/create/CreatePollView.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt index ce6a8682b6..3bb8c459e9 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt @@ -35,6 +35,7 @@ import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.poll.impl.R import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog +import io.element.android.libraries.designsystem.components.dialogs.SaveChangesDialog import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -63,8 +64,7 @@ fun CreatePollView( val navBack = { state.eventSink(CreatePollEvents.ConfirmNavBack) } BackHandler(onBack = navBack) if (state.showBackConfirmation) { - ConfirmationDialog( - content = stringResource(id = R.string.screen_create_poll_cancel_confirmation_content_android), + SaveChangesDialog( onSubmitClick = { state.eventSink(CreatePollEvents.NavBack) }, onDismiss = { state.eventSink(CreatePollEvents.HideConfirmation) } ) From 6499a629cc172205ae39ed1113bc24dc5c3bf31c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 2 Apr 2025 14:45:09 +0200 Subject: [PATCH 032/216] Use SaveChangesDialog --- .../features/roomdetails/impl/edit/RoomDetailsEditView.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt index 89b7812cb2..b1e39508bd 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt @@ -37,7 +37,7 @@ import io.element.android.libraries.designsystem.components.async.AsyncActionVie import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.components.button.BackButton -import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog +import io.element.android.libraries.designsystem.components.dialogs.SaveChangesDialog import io.element.android.libraries.designsystem.modifiers.clearFocusOnTap import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -152,9 +152,7 @@ fun RoomDetailsEditView( }, confirmationDialog = { if (state.saveAction == AsyncAction.ConfirmingCancellation) { - ConfirmationDialog( - title = stringResource(CommonStrings.dialog_unsaved_changes_title), - content = stringResource(CommonStrings.dialog_unsaved_changes_description_android), + SaveChangesDialog( onSubmitClick = { state.eventSink(RoomDetailsEditEvents.OnBackPress) }, onDismiss = { state.eventSink(RoomDetailsEditEvents.CloseDialog) } ) From af12ff3c9aea3e9e3a997a0ed01760af80e7831d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 4 Nov 2025 16:22:16 +0100 Subject: [PATCH 033/216] Use new SessionStore API --- .../libraries/push/impl/notifications/NotificationRenderer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt index 0cfe083caf..5da7389125 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt @@ -44,7 +44,7 @@ class NotificationRenderer( ) { val color = enterpriseService.brandColorsFlow(currentUser.userId).first()?.toArgb() ?: NotificationConfig.NOTIFICATION_ACCENT_COLOR - val numberOfAccounts = sessionStore.getAllSessions().size + val numberOfAccounts = sessionStore.numberOfSessions() val notificationAccountParams = NotificationAccountParams( user = currentUser, color = color, From 9f16ec87adf2a1dc07f9d994c71ac1bd32b22b92 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 4 Nov 2025 15:31:36 +0000 Subject: [PATCH 034/216] Update screenshots --- .../features.poll.impl.create_CreatePollView_Day_2_en.png | 4 ++-- .../features.poll.impl.create_CreatePollView_Night_2_en.png | 4 ++-- ...ignsystem.components.dialogs_SaveChangeDialog_Day_0_en.png | 3 +++ ...nsystem.components.dialogs_SaveChangeDialog_Night_0_en.png | 3 +++ 4 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/libraries.designsystem.components.dialogs_SaveChangeDialog_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.designsystem.components.dialogs_SaveChangeDialog_Night_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.poll.impl.create_CreatePollView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.poll.impl.create_CreatePollView_Day_2_en.png index 4a2a7a2a97..12e42e376c 100644 --- a/tests/uitests/src/test/snapshots/images/features.poll.impl.create_CreatePollView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.poll.impl.create_CreatePollView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:550ea0bbefea8fc1af9aed6bf9b8238547aba6d9bd670035b22c42e5e1788812 -size 39281 +oid sha256:657d1e0eff5254eb3d36a64212cca38ec129c240cbd61b8cfdeb8399a00a5251 +size 39103 diff --git a/tests/uitests/src/test/snapshots/images/features.poll.impl.create_CreatePollView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.poll.impl.create_CreatePollView_Night_2_en.png index f1cbbadd81..5d4488f53a 100644 --- a/tests/uitests/src/test/snapshots/images/features.poll.impl.create_CreatePollView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.poll.impl.create_CreatePollView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b511e657048cf668b48a135119ffce4449c99ea4c90065744d61a34502e75508 -size 36703 +oid sha256:3407623ce83d0c3f710ea45d693823d6125b1bc625c34e2922916110d8a2a442 +size 36717 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.dialogs_SaveChangeDialog_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.dialogs_SaveChangeDialog_Day_0_en.png new file mode 100644 index 0000000000..090d6dce7d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.dialogs_SaveChangeDialog_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d89a6c9a49b2b0e4fa37e219cec8e0dbe90ed8ca6e71f0ec5e8788076f91526 +size 23679 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.dialogs_SaveChangeDialog_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.dialogs_SaveChangeDialog_Night_0_en.png new file mode 100644 index 0000000000..3963c95375 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.dialogs_SaveChangeDialog_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e496c8d67a678a7d4c729a3368c145afa080252dd68708b1ba95d98706cd614c +size 22265 From 3de8618c64e82b55e99e33333074f4b36431f63d Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 5 Nov 2025 10:16:51 +0100 Subject: [PATCH 035/216] design: update DropdownMenu to better match figma --- .../theme/components/DropdownMenu.kt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenu.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenu.kt index 6dc681b0e1..77a8c514a5 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenu.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenu.kt @@ -10,8 +10,10 @@ package io.element.android.libraries.designsystem.theme.components import androidx.compose.foundation.background import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.window.PopupProperties @@ -24,22 +26,24 @@ fun DropdownMenu( expanded: Boolean, onDismissRequest: () -> Unit, modifier: Modifier = Modifier, - // By default add a 16.dp offset to the menu - offset: DpOffset = DpOffset(x = 16.dp, y = 0.dp), + offset: DpOffset = DpOffset(x = 0.dp, y = 0.dp), properties: PopupProperties = PopupProperties(focusable = true), + minWidth: Dp = DropdownMenuDefaults.minWidth, content: @Composable ColumnScope.() -> Unit ) { - // Note: the internal shape corner radius should be 8dp, but there is a 4p value hardcoded in the internal Surface component androidx.compose.material3.DropdownMenu( expanded = expanded, onDismissRequest = onDismissRequest, modifier = modifier - .background(color = ElementTheme.colors.bgCanvasDefault) - .widthIn(min = minMenuWidth), + .background(color = ElementTheme.colors.bgCanvasDefaultLevel1) + .widthIn(min = minWidth), + shape = RoundedCornerShape(8.dp), offset = offset, properties = properties, content = content ) } -private val minMenuWidth = 200.dp +object DropdownMenuDefaults { + val minWidth = 200.dp +} From 8b60c8309ceacb454338822531c6c4779cb9d05a Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 5 Nov 2025 10:21:09 +0100 Subject: [PATCH 036/216] design: PreferenceDropdown now uses DropdownMenu --- .../preferences/PreferenceDropdown.kt | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDropdown.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDropdown.kt index ebac860c7e..84b108aa7f 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDropdown.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDropdown.kt @@ -10,14 +10,10 @@ package io.element.android.libraries.designsystem.components.preferences import androidx.annotation.DrawableRes -import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.size import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ExposedDropdownMenuBox -import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.MenuAnchorType import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -35,6 +31,7 @@ import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.preferences.components.preferenceIcon import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup +import io.element.android.libraries.designsystem.theme.components.DropdownMenu import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.ListItem @@ -118,27 +115,25 @@ private fun DropdownTrailingContent( onSelectOption: (T) -> Unit, modifier: Modifier = Modifier, ) { - ExposedDropdownMenuBox( - expanded = expanded, - onExpandedChange = onExpandedChange, + Row( modifier = modifier, + verticalAlignment = Alignment.CenterVertically, ) { - Row( - modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable), - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - text = selectedOption?.getText().orEmpty(), - maxLines = 1, - style = ElementTheme.typography.fontBodyMdRegular, - color = ElementTheme.colors.textSecondary, - ) - ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) - } - ExposedDropdownMenu( + Text( + text = selectedOption?.getText().orEmpty(), + maxLines = 1, + style = ElementTheme.typography.fontBodyMdRegular, + color = ElementTheme.colors.textSecondary, + ) + Icon( + imageVector = CompoundIcons.ChevronDown(), + contentDescription = null, + tint = ElementTheme.colors.iconSecondary, + ) + DropdownMenu( expanded = expanded, + minWidth = 0.dp, onDismissRequest = { onExpandedChange(false) }, - matchTextFieldWidth = false, ) { options.forEach { option -> DropdownMenuItem( @@ -149,11 +144,11 @@ private fun DropdownTrailingContent( ) }, trailingIcon = { - if(option == selectedOption){ + if (option == selectedOption) { Icon( imageVector = CompoundIcons.Check(), contentDescription = null, - tint = ElementTheme.colors.iconSuccessPrimary, + tint = ElementTheme.colors.iconAccentPrimary, ) } }, From 1d79ab88bd4e4522ffc56ec0935192225ab1cc3d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 5 Nov 2025 11:00:13 +0100 Subject: [PATCH 037/216] Do not use the bastDescription but the cation for image/video/sticker because else the filename will be rendered in the notification and for media we do not want that. Also fixes the issue when images is not rendered on some system and so they can be empty notification. Closes #3945 --- .../DefaultNotifiableEventResolver.kt | 58 +++++++++++++++---- .../factories/NotificationCreator.kt | 50 +++++++++------- 2 files changed, 76 insertions(+), 32 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt index 481d354e8d..5e397c94d9 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt @@ -8,7 +8,10 @@ package io.element.android.libraries.push.impl.notifications import android.content.Context +import android.graphics.ImageDecoder import android.net.Uri +import android.os.Build +import androidx.core.app.NotificationCompat import androidx.core.content.FileProvider import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding @@ -138,7 +141,13 @@ class DefaultNotifiableEventResolver( is NotificationContent.MessageLike.RoomMessage -> { val showMediaPreview = client.mediaPreviewService.getMediaPreviewValue() == MediaPreviewValue.On val senderDisambiguatedDisplayName = getDisambiguatedDisplayName(content.senderId) - val messageBody = descriptionFromMessageContent(content, senderDisambiguatedDisplayName) + val imageMimeType = if (showMediaPreview) content.getImageMimetype() else null + val imageUriString = imageMimeType?.let { content.fetchImageIfPresent(client, imageMimeType)?.toString() } + val messageBody = descriptionFromMessageContent( + content = content, + senderDisambiguatedDisplayName = senderDisambiguatedDisplayName, + hasImageUri = imageUriString != null, + ) val notifiableMessageEvent = buildNotifiableMessageEvent( sessionId = userId, senderId = content.senderId, @@ -149,8 +158,8 @@ class DefaultNotifiableEventResolver( timestamp = this.timestamp, senderDisambiguatedDisplayName = senderDisambiguatedDisplayName, body = messageBody, - imageUriString = if (showMediaPreview) content.fetchImageIfPresent(client)?.toString() else null, - imageMimeType = if (showMediaPreview) content.getImageMimetype() else null, + imageUriString = imageUriString, + imageMimeType = imageMimeType.takeIf { imageUriString != null }, roomName = roomDisplayName, roomIsDm = isDm, roomAvatarPath = roomAvatarUrl, @@ -299,13 +308,18 @@ class DefaultNotifiableEventResolver( private fun descriptionFromMessageContent( content: NotificationContent.MessageLike.RoomMessage, senderDisambiguatedDisplayName: String, - ): String { + hasImageUri: Boolean, + ): String? { return when (val messageType = content.messageType) { is AudioMessageType -> messageType.bestDescription is VoiceMessageType -> stringProvider.getString(CommonStrings.common_voice_message) is EmoteMessageType -> "* $senderDisambiguatedDisplayName ${messageType.body}" is FileMessageType -> messageType.bestDescription - is ImageMessageType -> messageType.bestDescription + is ImageMessageType -> if (hasImageUri) { + messageType.caption + } else { + messageType.bestDescription + } is StickerMessageType -> messageType.bestDescription is NoticeMessageType -> messageType.body is TextMessageType -> messageType.toPlainText(permalinkParser = permalinkParser) @@ -326,14 +340,34 @@ class DefaultNotifiableEventResolver( } } - private suspend fun NotificationContent.MessageLike.RoomMessage.fetchImageIfPresent(client: MatrixClient): Uri? { + /** + * Fetch the image for message type, only if the mime type is supported, as recommended + * per [NotificationCompat.MessagingStyle.Message.setData] documentation. + * Then convert to a [Uri] accessible to the Notification Service. + */ + private suspend fun NotificationContent.MessageLike.RoomMessage.fetchImageIfPresent( + client: MatrixClient, + mimeType: String, + ): Uri? { val fileResult = when (val messageType = messageType) { - is ImageMessageType -> notificationMediaRepoFactory.create(client) - .getMediaFile( - mediaSource = messageType.source, - mimeType = messageType.info?.mimetype, - filename = messageType.filename, - ) + is ImageMessageType -> { + val isMimeTypeSupported = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + ImageDecoder.isMimeTypeSupported(mimeType) + } else { + // Assume it's supported on old systems... + true + } + if (isMimeTypeSupported) { + notificationMediaRepoFactory.create(client).getMediaFile( + mediaSource = messageType.source, + mimeType = messageType.info?.mimetype, + filename = messageType.filename, + ) + } else { + Timber.tag(loggerTag.value).d("Mime type $mimeType not supported by the system") + null + } + } is VideoMessageType -> null // Use the thumbnail here? else -> null } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt index efe54bd3d9..0dccdb46c2 100755 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt @@ -431,27 +431,37 @@ class DefaultNotificationCreator( senderPerson ) else -> { - val message = MessagingStyle.Message( - event.body?.annotateForDebug(71), - event.timestamp, - senderPerson - ).also { message -> - event.imageUri?.let { - message.setData(event.imageMimeType ?: "image/", it) - } - message.extras.putString(MESSAGE_EVENT_ID, event.eventId.value) - } - addMessage(message) - - // Add additional message for captions - if (event.imageUri != null && event.body != null) { - addMessage( - MessagingStyle.Message( - event.body, - event.timestamp, - senderPerson, - ) + if (event.imageMimeType != null && event.imageUri != null) { + // Image case + val message = MessagingStyle.Message( + // This text will not be rendered, but some systems does not render the image + // if the text is null + stringProvider.getString(CommonStrings.common_image), + event.timestamp, + senderPerson, ) + .setData(event.imageMimeType, event.imageUri) + message.extras.putString(MESSAGE_EVENT_ID, event.eventId.value) + addMessage(message) + // Add additional message for captions + if (event.body != null) { + addMessage( + MessagingStyle.Message( + event.body.annotateForDebug(72), + event.timestamp, + senderPerson, + ) + ) + } + } else { + // Text case + val message = MessagingStyle.Message( + event.body?.annotateForDebug(71), + event.timestamp, + senderPerson + ) + message.extras.putString(MESSAGE_EVENT_ID, event.eventId.value) + addMessage(message) } } } From cf742b42d0e20c4c7b1840fa2263e49a86acc87d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 5 Nov 2025 11:47:51 +0100 Subject: [PATCH 038/216] Fix test. --- .../DefaultNotificationDrawerManagerTest.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt index ff484df96f..0489b72dd3 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt @@ -12,7 +12,6 @@ import androidx.compose.ui.graphics.Color import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService -import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID @@ -118,8 +117,11 @@ class DefaultNotificationDrawerManagerTest { } @Test - fun `when MatrixClient has no cached user name a fallback one is used to render the notification`() = runTest { - val matrixClient = FakeMatrixClient(userDisplayName = null) + fun `when MatrixClient has no cached user name and avatar, the profile is loaded to render the notification`() = runTest { + val matrixClient = FakeMatrixClient( + userDisplayName = null, + userAvatarUrl = null, + ) val matrixClientProvider = FakeMatrixClientProvider(getClient = { Result.success(matrixClient) }) val messageCreator = FakeRoomGroupMessageCreator() val defaultNotificationDrawerManager = createDefaultNotificationDrawerManager( @@ -153,7 +155,7 @@ class DefaultNotificationDrawerManagerTest { any(), ), listOf( - value(aNotificationAccountParams(user = aMatrixUser(id = A_SESSION_ID.value, displayName = A_SESSION_ID.value))), + value(aNotificationAccountParams(user = aMatrixUser(id = A_SESSION_ID.value, displayName = ""))), any(), any(), any(), @@ -161,7 +163,7 @@ class DefaultNotificationDrawerManagerTest { any(), ), listOf( - value(aNotificationAccountParams(user = aMatrixUser(id = A_SESSION_ID.value, displayName = A_SESSION_ID.value, avatarUrl = AN_AVATAR_URL))), + value(aNotificationAccountParams(user = aMatrixUser(id = A_SESSION_ID.value, displayName = null, avatarUrl = null))), any(), any(), any(), From 28c6c1083c42596cd0fc6e4be8287a6102593614 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 5 Nov 2025 11:53:18 +0100 Subject: [PATCH 039/216] Setting version for the release 25.11.2 --- plugins/src/main/kotlin/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 819554ae55..d82e203e9b 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -44,7 +44,7 @@ private const val versionMonth = 11 * Release number in the month. Value must be in [0,99]. * Do not update this value. it is updated by the release script. */ -private const val versionReleaseNumber = 1 +private const val versionReleaseNumber = 2 object Versions { /** From 19d4c70aa1304b557ce4f0775c04147665c75e7f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 5 Nov 2025 11:53:37 +0100 Subject: [PATCH 040/216] Adding fastlane file for version 25.11.2 --- fastlane/metadata/android/en-US/changelogs/202511020.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/202511020.txt diff --git a/fastlane/metadata/android/en-US/changelogs/202511020.txt b/fastlane/metadata/android/en-US/changelogs/202511020.txt new file mode 100644 index 0000000000..a4b397f1bb --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/202511020.txt @@ -0,0 +1,2 @@ +Main changes in this version: bug fixes and improvements. +Full changelog: https://github.com/element-hq/element-x-android/releases \ No newline at end of file From a9958fec6d40b06d9bee687bafcb3828e8251c35 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 5 Nov 2025 12:48:47 +0100 Subject: [PATCH 041/216] Changelog for version 25.11.2 --- CHANGES.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 1ac0cd1124..28e774a8ff 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,86 @@ +Changes in Element X v25.11.2 +============================= + + + +## What's Changed +### ✨ Features +* Enable access to security and privacy by @bmarty in https://github.com/element-hq/element-x-android/pull/5566 +* Add ability to forward a media from the media viewer and the gallery by @bmarty in https://github.com/element-hq/element-x-android/pull/5622 +* Split notifications for messages in threads by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5595 +### 🙌 Improvements +* Enable `SyncNotificationsWithWorkManager` in nightly and debug builds by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5573 +* Confirm exit without saving change in room details edit screen by @bmarty in https://github.com/element-hq/element-x-android/pull/5618 +* Space : add view members entry by @ganfra in https://github.com/element-hq/element-x-android/pull/5619 +* Update notification sound by @bmarty in https://github.com/element-hq/element-x-android/pull/5667 +* Use the new notification sound only on debug and nightly build by @bmarty in https://github.com/element-hq/element-x-android/pull/5673 +* Make sure we know the session verification state before showing the options to verify the session by @bmarty in https://github.com/element-hq/element-x-android/pull/5677 +### 🐛 Bugfixes +* Improve how brand color is applied. by @bmarty in https://github.com/element-hq/element-x-android/pull/5584 +* Improve wellknown retrieval API by @bmarty in https://github.com/element-hq/element-x-android/pull/5587 +* Clearing the room list search clears the search term too by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5603 +* Delete pin code only when the last session is deleted by @bmarty in https://github.com/element-hq/element-x-android/pull/5600 +* Fix issues with WorkManager on Android 12 and below by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5606 +* Fix marking a room as read re-instantiates its timeline by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5628 +* Display only valid emojis in recent emoji list by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5612 +* Fix navigation issue. by @bmarty in https://github.com/element-hq/element-x-android/pull/5666 +* Fix forward events from media viewer from pinned media timeline by @bmarty in https://github.com/element-hq/element-x-android/pull/5669 +* Try fixing 'Timeline Event object has already been destroyed' by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5675 +* Use the SDK Client to check whether a homeserver is compatible by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5664 +### 🗣 Translations +* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/5610 +* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/5662 +### 🧱 Build +* Remove `@Inject`, not necessary anymore when class is annotated with `@ContributesBinding` by @bmarty in https://github.com/element-hq/element-x-android/pull/5589 +* Upgrade ktlint to 1.7.1 and ensure Renovate will upgrade the version by @bmarty in https://github.com/element-hq/element-x-android/pull/5638 +* Improve architecture around Nodes by @bmarty in https://github.com/element-hq/element-x-android/pull/5641 +* Move dependencies block out of the android block. by @bmarty in https://github.com/element-hq/element-x-android/pull/5674 +* Always use the handleEvent(s) function the same way. by @bmarty in https://github.com/element-hq/element-x-android/pull/5672 +### Dependency upgrades +* fix(deps): update metro to v0.7.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5576 +* fix(deps): update dependencyanalysis to v3.2.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5577 +* fix(deps): update dependency io.sentry:sentry-android to v8.24.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5586 +* fix(deps): update dependency androidx.work:work-runtime-ktx to v2.11.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5590 +* fix(deps): update dependency com.posthog:posthog-android to v3.25.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5594 +* fix(deps): update dependency com.google.crypto.tink:tink-android to v1.19.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5572 +* Update plugin sonarqube to v7.0.1.6134 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5605 +* fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v25.10.28 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5620 +* fix(deps): update dependencyanalysis to v3.3.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5602 +* fix(deps): update dependency com.github.matrix-org:matrix-analytics-events to v0.29.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5621 +* fix(deps): update dependencyanalysis to v3.4.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5624 +* fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v25.10.29 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5625 +* fix(deps): update dependency io.sentry:sentry-android to v8.25.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5629 +* fix(deps): update dependencyanalysis to v3.4.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5642 +* fix(deps): update dependency com.squareup.okhttp3:okhttp-bom to v5.3.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5644 +* chore(deps): update danger/danger-js action to v13.0.5 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5652 +* fix(deps): update dependency com.google.firebase:firebase-bom to v34.5.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5643 +* fix(deps): update firebaseappdistribution to v5.2.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5640 +* fix(deps): update metro to v0.7.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5663 +* fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v25.10.31 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5657 +* Update GitHub Artifact Actions (major) by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5609 +* Update dependency io.element.android:element-call-embedded to v0.16.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5598 +* Update roborazzi to v1.51.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5676 +* fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v25.11.4 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5681 +* fix(deps): update metro to v0.7.4 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5683 +### Others +* Improve code around Element .well-known configuration by @bmarty in https://github.com/element-hq/element-x-android/pull/5565 +* misc: display offline banner for all LoggedIn screens by @ganfra in https://github.com/element-hq/element-x-android/pull/5574 +* Remove icon preview duplicate by @bmarty in https://github.com/element-hq/element-x-android/pull/5588 +* Remove application navigation state usage in the push module by @bmarty in https://github.com/element-hq/element-x-android/pull/5596 +* Design : update Home TopBar and RoomList Filters by @ganfra in https://github.com/element-hq/element-x-android/pull/5599 +* Add missing tests on the analytic modules by @bmarty in https://github.com/element-hq/element-x-android/pull/5604 +* design(space): let SpaceRoomItemView divider be full width by @ganfra in https://github.com/element-hq/element-x-android/pull/5597 +* Update notification style by @bmarty in https://github.com/element-hq/element-x-android/pull/5607 +* Improve how data is handled for the WorkManager. by @bmarty in https://github.com/element-hq/element-x-android/pull/5592 +* Revert "Make sure declining a call stops observing the ringing call state" by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5615 +* Misc : space flow inject room by @ganfra in https://github.com/element-hq/element-x-android/pull/5614 +* Enable `SyncNotificationsWithWorkManager` by default in release mode apps too by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5646 +* Revert "Update notification sound" by @bmarty in https://github.com/element-hq/element-x-android/pull/5671 +* Introduce new query to count accounts by @bmarty in https://github.com/element-hq/element-x-android/pull/5678 + + +**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v25.11.0...v25.11.2 + Changes in Element X v25.11.0 ============================= From 42b8dc33f2ebbf0c45be3ef0db739cfa2d0dd31a Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 5 Nov 2025 14:42:34 +0100 Subject: [PATCH 042/216] change(roles and permissions): improve the flow --- .../features/home/impl/HomeFlowNode.kt | 7 +- .../api/ChangeRoomMemberRolesEntryPoint.kt | 4 +- .../impl/RolesAndPermissionsFlowNode.kt | 77 +++++++++++-------- .../permissions/ChangeRoomPermissionsNode.kt | 9 ++- .../permissions/ChangeRoomPermissionsView.kt | 20 ++--- .../impl/roles/ChangeRolesNode.kt | 9 ++- .../impl/roles/ChangeRolesPresenter.kt | 11 ++- .../impl/roles/ChangeRolesView.kt | 19 +---- .../roles/ChangeRoomMemberRolesRootNode.kt | 5 +- .../impl/roles/ChangeRolesViewTest.kt | 3 - .../FakeChangeRoomMemberRolesEntryPoint.kt | 6 +- .../roomdetails/impl/RoomDetailsFlowNode.kt | 6 +- .../impl/DefaultRoomDetailsEntryPointTest.kt | 2 +- 13 files changed, 93 insertions(+), 85 deletions(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt index cf1d9e829d..037204bac6 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt @@ -40,7 +40,6 @@ import io.element.android.features.logout.api.direct.DirectLogoutView import io.element.android.features.reportroom.api.ReportRoomEntryPoint import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType -import io.element.android.features.rolesandpermissions.api.RolesAndPermissionsEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.appyx.launchMolecule @@ -94,10 +93,12 @@ class HomeFlowNode( changeRoomMemberRolesNode: ChangeRoomMemberRolesEntryPoint.NodeProxy, -> commonLifecycle.coroutineScope.launch { - changeRoomMemberRolesNode.waitForRoleChanged() + val isNewOwnerSelected = changeRoomMemberRolesNode.waitForCompletion() withContext(NonCancellable) { backstack.pop() - onNewOwnersSelected(changeRoomMemberRolesNode.roomId) + if(isNewOwnerSelected) { + onNewOwnersSelected(changeRoomMemberRolesNode.roomId) + } } } } diff --git a/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/ChangeRoomMemberRolesEntryPoint.kt b/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/ChangeRoomMemberRolesEntryPoint.kt index 0d93632699..52f0c7a7ba 100644 --- a/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/ChangeRoomMemberRolesEntryPoint.kt +++ b/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/ChangeRoomMemberRolesEntryPoint.kt @@ -24,11 +24,11 @@ fun interface ChangeRoomMemberRolesEntryPoint : FeatureEntryPoint { interface NodeProxy { val roomId: RoomId - suspend fun waitForRoleChanged() + suspend fun waitForCompletion(): Boolean } } -enum class ChangeRoomMemberRolesListType : NodeInputs { +enum class ChangeRoomMemberRolesListType { SelectNewOwnersWhenLeaving, Admins, Moderators diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt index 81ef3b3b1e..0c18a3c7a5 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt @@ -8,8 +8,11 @@ package io.element.android.features.rolesandpermissions.impl import android.os.Parcelable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.lifecycle.coroutineScope import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node @@ -20,15 +23,18 @@ import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode -import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType import io.element.android.features.rolesandpermissions.impl.permissions.ChangeRoomPermissionsNode +import io.element.android.features.rolesandpermissions.impl.roles.ChangeRolesNode import io.element.android.features.rolesandpermissions.impl.root.RolesAndPermissionsNode import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.designsystem.components.async.AsyncIndicator +import io.element.android.libraries.designsystem.components.async.AsyncIndicatorHost +import io.element.android.libraries.designsystem.components.async.AsyncIndicatorState import io.element.android.libraries.di.RoomScope -import io.element.android.libraries.matrix.api.room.JoinedRoom +import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize @@ -37,11 +43,9 @@ import kotlinx.parcelize.Parcelize class RolesAndPermissionsFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, - private val joinedRoom: JoinedRoom, - private val changeRoomMemberRolesEntryPoint: ChangeRoomMemberRolesEntryPoint, ) : BaseFlowNode( backstack = BackStack( - initialElement = NavTarget.AdminSettings, + initialElement = NavTarget.Root, savedStateMap = buildContext.savedStateMap, ), buildContext = buildContext, @@ -49,38 +53,49 @@ class RolesAndPermissionsFlowNode( ) { sealed interface NavTarget : Parcelable { @Parcelize - data object AdminSettings : NavTarget + data object Root : NavTarget @Parcelize - data object AdminList : NavTarget + data object ChangeAdmins : NavTarget @Parcelize - data object ModeratorList : NavTarget + data object ChangeModerators : NavTarget @Parcelize data object ChangeRoomPermissions: NavTarget } + private val asyncIndicatorState = AsyncIndicatorState() + override fun onBuilt() { super.onBuilt() - whenChildAttached { lifecycle, node: ChangeRoomMemberRolesEntryPoint.NodeProxy -> + whenChildAttached { lifecycle, node: ChangeRolesNode -> lifecycle.coroutineScope.launch { - node.waitForRoleChanged() - backstack.pop() + val changesSaved = node.waitForCompletion() + onChangeComplete(changesSaved) + } + } + } + + private fun onChangeComplete(changesSaved: Boolean) { + backstack.pop() + if (changesSaved) { + asyncIndicatorState.enqueue(durationMs = AsyncIndicator.DURATION_SHORT) { + AsyncIndicator.Custom(text = stringResource(CommonStrings.common_saved_changes)) } } } override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { - is NavTarget.AdminSettings -> { + is NavTarget.Root -> { val callback = object : RolesAndPermissionsNode.Callback { override fun openAdminList() { - backstack.push(NavTarget.AdminList) + backstack.push(NavTarget.ChangeAdmins) } override fun openModeratorList() { - backstack.push(NavTarget.ModeratorList) + backstack.push(NavTarget.ChangeModerators) } override fun openEditPermissions() { @@ -93,30 +108,32 @@ class RolesAndPermissionsFlowNode( plugins = listOf(callback), ) } - is NavTarget.AdminList -> { - changeRoomMemberRolesEntryPoint.createNode( - parentNode = this, - buildContext = buildContext, - room = joinedRoom, - listType = ChangeRoomMemberRolesListType.Admins, - ) + is NavTarget.ChangeAdmins -> { + val inputs = ChangeRolesNode.Inputs(ChangeRoomMemberRolesListType.Admins) + createNode(buildContext = buildContext, plugins = listOf(inputs)) } - is NavTarget.ModeratorList -> { - changeRoomMemberRolesEntryPoint.createNode( - parentNode = this, - buildContext = buildContext, - room = joinedRoom, - listType = ChangeRoomMemberRolesListType.Moderators, - ) + is NavTarget.ChangeModerators -> { + val inputs = ChangeRolesNode.Inputs(ChangeRoomMemberRolesListType.Moderators) + createNode(buildContext = buildContext, plugins = listOf(inputs)) } is NavTarget.ChangeRoomPermissions -> { - createNode(buildContext = buildContext) + val callback = object : ChangeRoomPermissionsNode.Callback { + override fun onComplete(changesSaved: Boolean) { + onChangeComplete(changesSaved) + } + } + createNode(buildContext = buildContext, plugins = listOf(callback)) } } } + + @Composable override fun View(modifier: Modifier) { - BackstackView() + Box(modifier = modifier) { + BackstackView() + AsyncIndicatorHost(modifier = Modifier.statusBarsPadding(), asyncIndicatorState) + } } } diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsNode.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsNode.kt index b8261abd89..b53938914d 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsNode.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsNode.kt @@ -15,6 +15,7 @@ import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.RoomScope @ContributesNode(RoomScope::class) @@ -25,13 +26,19 @@ class ChangeRoomPermissionsNode( private val presenter: ChangeRoomPermissionsPresenter, ) : Node(buildContext, plugins = plugins) { + interface Callback : Plugin { + fun onComplete(changesSaved: Boolean) + } + + private val callback: Callback = callback() + @Composable override fun View(modifier: Modifier) { val state = presenter.present() ChangeRoomPermissionsView( modifier = modifier, state = state, - onBackClick = this::navigateUp, + onComplete = callback::onComplete, ) } } diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt index 85a75f9f35..0fc022e979 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt @@ -16,26 +16,17 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter -import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.rolesandpermissions.impl.R -import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog -import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.preferences.PreferenceDropdown import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.theme.components.IconSource -import io.element.android.libraries.designsystem.theme.components.ListItem -import io.element.android.libraries.designsystem.theme.components.ListItemStyle import io.element.android.libraries.designsystem.theme.components.ListSectionHeader import io.element.android.libraries.designsystem.theme.components.Scaffold -import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.matrix.api.room.RoomMember -import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.toImmutableList @@ -43,7 +34,7 @@ import kotlinx.collections.immutable.toImmutableList @Composable fun ChangeRoomPermissionsView( state: ChangeRoomPermissionsState, - onBackClick: () -> Unit, + onComplete: (Boolean) -> Unit, modifier: Modifier = Modifier, ) { BackHandler { @@ -74,7 +65,7 @@ fun ChangeRoomPermissionsView( ) { state.itemsBySection.onEachIndexed { index, (section, items) -> item { - ListSectionHeader(titleForSection(section), hasDivider = index>0) + ListSectionHeader(titleForSection(section), hasDivider = index > 0) } for (permissionType in items) { item { @@ -99,13 +90,13 @@ fun ChangeRoomPermissionsView( AsyncActionView( async = state.saveAction, - onSuccess = { onBackClick() }, + onSuccess = { onComplete(true) }, onErrorDismiss = { state.eventSink(ChangeRoomPermissionsEvent.ResetPendingActions) } ) AsyncActionView( async = state.confirmExitAction, - onSuccess = { onBackClick() }, + onSuccess = { onComplete(false) }, confirmationDialog = { ConfirmationDialog( title = stringResource(R.string.screen_room_change_role_unsaved_changes_title), @@ -119,6 +110,7 @@ fun ChangeRoomPermissionsView( onErrorDismiss = {}, ) } + @Composable private fun titleForSection(section: RoomPermissionsSection): String = when (section) { RoomPermissionsSection.RoomDetails -> stringResource(R.string.screen_room_change_permissions_room_details) @@ -144,7 +136,7 @@ internal fun ChangeRoomPermissionsViewPreview(@PreviewParameter(ChangeRoomPermis ElementPreview { ChangeRoomPermissionsView( state = state, - onBackClick = {}, + onComplete = {}, ) } } diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesNode.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesNode.kt index 915b129c84..01eb423fb7 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesNode.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesNode.kt @@ -21,6 +21,7 @@ import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRoles import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.appyx.launchMolecule import io.element.android.libraries.architecture.inputs +import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.room.RoomMember import kotlinx.coroutines.flow.first @@ -40,17 +41,17 @@ class ChangeRolesNode( private val presenter = presenterFactory.create(inputs.listType.toRoomMemberRole()) private val stateFlow = launchMolecule { presenter.present() } - suspend fun waitForRoleChanged() { - stateFlow.first { it.savingState.isSuccess() } + suspend fun waitForCompletion(): Boolean { + val successState = stateFlow.first { it.savingState.isSuccess() } + return successState.savingState.dataOrNull().orFalse() } @Composable override fun View(modifier: Modifier) { val state by stateFlow.collectAsState() ChangeRolesView( - modifier = modifier, state = state, - navigateUp = this::navigateUp, + modifier = modifier, ) } } diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt index 07a0c5a873..fb97080221 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt @@ -27,6 +27,7 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.designsystem.theme.components.SearchBarResultState +import io.element.android.libraries.di.annotations.SessionCoroutineScope 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.RoomMember @@ -41,10 +42,12 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext @AssistedInject class ChangeRolesPresenter( @@ -214,9 +217,13 @@ class ChangeRolesPresenter( saveState.value = AsyncAction.Failure(it) } .onSuccess { - saveState.value = AsyncAction.Success(true) // Asynchronously reload the room members - launch { room.updateMembers() } + launch { + withContext(NonCancellable) { + room.updateMembers() + } + } + saveState.value = AsyncAction.Success(true) } } } diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesView.kt index 16c6965f18..b74f7e2067 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesView.kt @@ -29,8 +29,6 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -42,7 +40,6 @@ import io.element.android.compound.theme.ElementTheme import io.element.android.features.rolesandpermissions.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.AsyncIndicator import io.element.android.libraries.designsystem.components.async.AsyncIndicatorHost import io.element.android.libraries.designsystem.components.async.rememberAsyncIndicatorState import io.element.android.libraries.designsystem.components.avatar.Avatar @@ -77,10 +74,8 @@ import kotlinx.collections.immutable.ImmutableList @Composable fun ChangeRolesView( state: ChangeRolesState, - navigateUp: () -> Unit, modifier: Modifier = Modifier, ) { - val latestNavigateUp by rememberUpdatedState(newValue = navigateUp) BackHandler(enabled = !state.isSearchActive) { state.eventSink(ChangeRolesEvent.Exit) } @@ -168,18 +163,9 @@ fun ChangeRolesView( val asyncIndicatorState = rememberAsyncIndicatorState() AsyncIndicatorHost(modifier = Modifier.statusBarsPadding(), asyncIndicatorState) - AsyncActionView( async = state.savingState, - onSuccess = { changeSaved -> - if (changeSaved) { - asyncIndicatorState.enqueue(durationMs = AsyncIndicator.DURATION_SHORT) { - AsyncIndicator.Custom(text = stringResource(CommonStrings.common_saved_changes)) - } - } else { - latestNavigateUp() - } - }, + onSuccess = {}, confirmationDialog = { confirming -> when (confirming) { is AsyncAction.ConfirmingCancellation -> { @@ -416,8 +402,7 @@ private fun MemberRow( internal fun ChangeRolesViewPreview(@PreviewParameter(ChangeRolesStateProvider::class) state: ChangeRolesState) { ElementPreview { ChangeRolesView( - state = state, - navigateUp = {}, + state = state ) } } diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRoomMemberRolesRootNode.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRoomMemberRolesRootNode.kt index 0f588667c6..aad691809f 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRoomMemberRolesRootNode.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRoomMemberRolesRootNode.kt @@ -21,7 +21,6 @@ import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.appnav.di.RoomGraphFactory import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint -import io.element.android.features.rolesandpermissions.api.RolesAndPermissionsEntryPoint import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.createNode @@ -71,7 +70,7 @@ class ChangeRoomMemberRolesRootNode( override val roomId: RoomId = inputs.joinedRoom.roomId - override suspend fun waitForRoleChanged() { - waitForChildAttached().waitForRoleChanged() + override suspend fun waitForCompletion(): Boolean { + return waitForChildAttached().waitForCompletion() } } diff --git a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesViewTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesViewTest.kt index dc26741587..d0e6d120bd 100644 --- a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesViewTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesViewTest.kt @@ -23,7 +23,6 @@ import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.toMatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUserList import io.element.android.libraries.ui.strings.CommonStrings -import io.element.android.tests.testutils.EnsureNeverCalled import io.element.android.tests.testutils.EnsureNeverCalledWithParam import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn @@ -306,12 +305,10 @@ class ChangeRolesViewTest { private fun AndroidComposeTestRule.setChangeRolesContent( state: ChangeRolesState, - onBackClick: () -> Unit = EnsureNeverCalled(), ) { setContent { ChangeRolesView( state = state, - navigateUp = onBackClick, ) } } diff --git a/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt b/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt index a2f9ac0253..de447cef3c 100644 --- a/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt +++ b/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt @@ -5,16 +5,16 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.rolesandpermissions.test +package io.element.android.features.changeroommemberroles.test import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import io.element.android.features.rolesandpermissions.api.RolesAndPermissionsEntryPoint +import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.tests.testutils.lambda.lambdaError -class FakeChangeRoomMemberRolesEntryPoint : RolesAndPermissionsEntryPoint { +class FakeChangeRoomMemberRolesEntryPoint : ChangeRoomMemberRolesEntryPoint { override fun createNode( parentNode: Node, buildContext: BuildContext, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index 20e783fb6b..39c555be20 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -157,10 +157,12 @@ class RoomDetailsFlowNode( changeRoomMemberRolesNode: ChangeRoomMemberRolesEntryPoint.NodeProxy, -> commonLifecycle.coroutineScope.launch { - changeRoomMemberRolesNode.waitForRoleChanged() + val isNewOwnerSelected = changeRoomMemberRolesNode.waitForCompletion() withContext(NonCancellable) { backstack.pop() - roomDetailsNode.onNewOwnersSelected() + if (isNewOwnerSelected) { + roomDetailsNode.onNewOwnersSelected() + } } } } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt index ad939734df..ebe92332e8 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt @@ -12,7 +12,7 @@ import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat import io.element.android.features.call.test.FakeElementCallEntryPoint -import io.element.android.features.rolesandpermissions.test.FakeChangeRoomMemberRolesEntryPoint +import io.element.android.features.changeroommemberroles.test.FakeChangeRoomMemberRolesEntryPoint import io.element.android.features.knockrequests.test.FakeKnockRequestsListEntryPoint import io.element.android.features.messages.test.FakeMessagesEntryPoint import io.element.android.features.poll.test.history.FakePollHistoryEntryPoint From fe7bc4fd9d32e057c68e99c1375943dbb4007ee2 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 5 Nov 2025 15:31:48 +0100 Subject: [PATCH 043/216] change(roles and permissions): clean code --- .../features/home/impl/HomeFlowNode.kt | 2 +- .../api/ChangeRoomMemberRolesEntryPoint.kt | 1 - .../impl/RolesAndPermissionsFlowNode.kt | 5 +--- .../permissions/ChangeRoomPermissionsNode.kt | 1 - .../ChangeRoomPermissionsPresenter.kt | 24 ++++++++++++------- .../permissions/ChangeRoomPermissionsState.kt | 10 ++++---- .../impl/roles/ChangeRolesPresenter.kt | 1 - .../preferences/PreferenceDropdown.kt | 1 - 8 files changed, 22 insertions(+), 23 deletions(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt index 037204bac6..553a60a185 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt @@ -96,7 +96,7 @@ class HomeFlowNode( val isNewOwnerSelected = changeRoomMemberRolesNode.waitForCompletion() withContext(NonCancellable) { backstack.pop() - if(isNewOwnerSelected) { + if (isNewOwnerSelected) { onNewOwnersSelected(changeRoomMemberRolesNode.roomId) } } diff --git a/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/ChangeRoomMemberRolesEntryPoint.kt b/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/ChangeRoomMemberRolesEntryPoint.kt index 52f0c7a7ba..5bc4bbfbdd 100644 --- a/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/ChangeRoomMemberRolesEntryPoint.kt +++ b/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/ChangeRoomMemberRolesEntryPoint.kt @@ -10,7 +10,6 @@ package io.element.android.features.rolesandpermissions.api import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import io.element.android.libraries.architecture.FeatureEntryPoint -import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.JoinedRoom diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt index 0c18a3c7a5..17453e4451 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt @@ -62,7 +62,7 @@ class RolesAndPermissionsFlowNode( data object ChangeModerators : NavTarget @Parcelize - data object ChangeRoomPermissions: NavTarget + data object ChangeRoomPermissions : NavTarget } private val asyncIndicatorState = AsyncIndicatorState() @@ -101,7 +101,6 @@ class RolesAndPermissionsFlowNode( override fun openEditPermissions() { backstack.push(NavTarget.ChangeRoomPermissions) } - } createNode( buildContext = buildContext, @@ -127,8 +126,6 @@ class RolesAndPermissionsFlowNode( } } - - @Composable override fun View(modifier: Modifier) { Box(modifier = modifier) { diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsNode.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsNode.kt index b53938914d..81716ab8c4 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsNode.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsNode.kt @@ -25,7 +25,6 @@ class ChangeRoomPermissionsNode( @Assisted plugins: List, private val presenter: ChangeRoomPermissionsPresenter, ) : Node(buildContext, plugins = plugins) { - interface Callback : Plugin { fun onComplete(changesSaved: Boolean) } diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt index e073a48171..a6d4eeefd4 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt @@ -24,7 +24,7 @@ import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues import io.element.android.services.analytics.api.AnalyticsService import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.persistentMapOf +import kotlinx.collections.immutable.toImmutableMap import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -51,14 +51,22 @@ class ChangeRoomPermissionsPresenter( ) } - internal fun buildItems(forSpace: Boolean) = persistentMapOf( - RoomPermissionsSection.RoomDetails to itemsForSection(RoomPermissionsSection.RoomDetails), - RoomPermissionsSection.MessagesAndContent to itemsForSection(RoomPermissionsSection.MessagesAndContent), - RoomPermissionsSection.MembershipModeration to itemsForSection(RoomPermissionsSection.MembershipModeration), - ) + private fun RoomPermissionsSection.shouldShow(isSpace: Boolean): Boolean { + return when (this) { + RoomPermissionsSection.RoomDetails -> true + RoomPermissionsSection.MembershipModeration -> true + RoomPermissionsSection.MessagesAndContent -> !isSpace + } + } + + internal fun buildItems(isSpace: Boolean) = + RoomPermissionsSection.entries + .filter { section -> section.shouldShow(isSpace) } + .associateWith { itemsForSection(it) } + .toImmutableMap() } - private val items = buildItems(forSpace = room.info().isSpace) + private val itemsBySection = buildItems(isSpace = room.info().isSpace) private var initialPermissions by mutableStateOf(null) private var currentPermissions by mutableStateOf(null) @@ -112,7 +120,7 @@ class ChangeRoomPermissionsPresenter( } return ChangeRoomPermissionsState( currentPermissions = currentPermissions, - itemsBySection = items, + itemsBySection = itemsBySection, hasChanges = hasChanges, saveAction = saveAction, confirmExitAction = confirmExitAction, diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt index e91dd9fcb6..b1032e5fac 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt @@ -25,9 +25,9 @@ data class ChangeRoomPermissionsState( val saveAction: AsyncAction, val confirmExitAction: AsyncAction, val eventSink: (ChangeRoomPermissionsEvent) -> Unit, -){ +) { fun selectedRoleForType(type: RoomPermissionType): SelectableRole? { - if(currentPermissions == null) return null + if (currentPermissions == null) return null val role = when (type) { RoomPermissionType.BAN -> RoomMember.Role.forPowerLevel(currentPermissions.ban) RoomPermissionType.INVITE -> RoomMember.Role.forPowerLevel(currentPermissions.invite) @@ -38,14 +38,13 @@ data class ChangeRoomPermissionsState( RoomPermissionType.ROOM_AVATAR -> RoomMember.Role.forPowerLevel(currentPermissions.roomAvatar) RoomPermissionType.ROOM_TOPIC -> RoomMember.Role.forPowerLevel(currentPermissions.roomTopic) } - return when(role){ + return when (role) { is RoomMember.Role.Owner, RoomMember.Role.Admin -> SelectableRole.Admin RoomMember.Role.Moderator -> SelectableRole.Moderator RoomMember.Role.User -> SelectableRole.Everyone } } - } enum class RoomPermissionsSection { @@ -54,7 +53,7 @@ enum class RoomPermissionsSection { MembershipModeration, } -enum class SelectableRole: DropdownOption { +enum class SelectableRole : DropdownOption { Admin { @Composable @ReadOnlyComposable @@ -72,7 +71,6 @@ enum class SelectableRole: DropdownOption { } } - enum class RoomPermissionType { BAN, INVITE, diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt index fb97080221..e2049336a2 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt @@ -27,7 +27,6 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.designsystem.theme.components.SearchBarResultState -import io.element.android.libraries.di.annotations.SessionCoroutineScope 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.RoomMember diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDropdown.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDropdown.kt index 84b108aa7f..389dc6c15b 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDropdown.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDropdown.kt @@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MenuAnchorType import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf From 8dec6602bfbd9ff8bc592f656f1bfa6a12a2c2a0 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 5 Nov 2025 15:42:59 +0100 Subject: [PATCH 044/216] quality: use callback() method in nodes --- .../android/features/space/impl/leave/LeaveSpaceNode.kt | 4 ++-- .../android/features/space/impl/settings/SpaceSettingsNode.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceNode.kt index 6ba86481fe..6eaa5d4e4a 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceNode.kt @@ -13,11 +13,11 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.space.impl.di.SpaceFlowScope +import io.element.android.libraries.architecture.callback import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.room.JoinedRoom @@ -38,7 +38,7 @@ class LeaveSpaceNode( private val leaveSpaceHandle = matrixClient.spaceService.getLeaveSpaceHandle(room.roomId) private val presenter: LeaveSpacePresenter = presenterFactory.create(leaveSpaceHandle) - private val callback = plugins().single() + private val callback: Callback = callback() override fun onBuilt() { super.onBuilt() diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsNode.kt index 9b81272153..ea2e07db77 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsNode.kt @@ -14,12 +14,12 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.space.impl.di.SpaceFlowScope import io.element.android.libraries.architecture.appyx.launchMolecule +import io.element.android.libraries.architecture.callback @ContributesNode(SpaceFlowScope::class) @AssistedInject @@ -38,7 +38,7 @@ class SpaceSettingsNode( fun startLeaveSpaceFlow() } - private val callback = plugins().single() + private val callback: Callback = callback() private val stateFlow = launchMolecule { presenter.present() } @Composable From d9ebf876f64f185cf18c63670cc514adfb069be6 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 5 Nov 2025 17:54:02 +0100 Subject: [PATCH 045/216] quality(roles and permissions): fix tests --- .../permissions/ChangeRoomPermissionsView.kt | 1 + .../ChangeRoomPermissionsPresenterTest.kt} | 87 +++++++------------ .../ChangeRoomPermissionsViewTest.kt} | 73 +++++++--------- .../root}/RolesAndPermissionPresenterTest.kt | 2 +- .../impl/root}/RolesAndPermissionsViewTest.kt | 20 ++--- 5 files changed, 70 insertions(+), 113 deletions(-) rename features/{roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeBaseRoomPermissionsPresenterTest.kt => rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenterTest.kt} (79%) rename features/{roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeBaseRoomPermissionsViewTest.kt => rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsViewTest.kt} (66%) rename features/{roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions => rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root}/RolesAndPermissionPresenterTest.kt (98%) rename features/{roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions => rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root}/RolesAndPermissionsViewTest.kt (89%) diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt index 0fc022e979..babe682717 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt @@ -74,6 +74,7 @@ fun ChangeRoomPermissionsView( selectedOption = state.selectedRoleForType(permissionType), options = SelectableRole.entries.toImmutableList(), onSelectOption = { role -> + println("Selected $role for $permissionType") state.eventSink( ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction( action = permissionType, diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeBaseRoomPermissionsPresenterTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenterTest.kt similarity index 79% rename from features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeBaseRoomPermissionsPresenterTest.kt rename to features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenterTest.kt index c59931140a..e19291cd2b 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeBaseRoomPermissionsPresenterTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenterTest.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.rolesandpermissions.permissions +package io.element.android.features.rolesandpermissions.impl.permissions import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow @@ -17,7 +17,6 @@ import im.vector.app.features.analytics.plan.RoomModeration import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.room.RoomMember.Role.Admin import io.element.android.libraries.matrix.api.room.RoomMember.Role.Moderator -import io.element.android.libraries.matrix.api.room.RoomMember.Role.User import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues import io.element.android.libraries.matrix.test.room.FakeBaseRoom import io.element.android.libraries.matrix.test.room.FakeJoinedRoom @@ -26,19 +25,17 @@ import io.element.android.services.analytics.test.FakeAnalyticsService import kotlinx.coroutines.test.runTest import org.junit.Test -class ChangeBaseRoomPermissionsPresenterTest { +class ChangeRoomPermissionsPresenterTest { @Test fun `present - initial state`() = runTest { - val section = ChangeRoomPermissionsSection.RoomDetails - val presenter = createChangeRoomPermissionsPresenter(section = section) + val presenter = createChangeRoomPermissionsPresenter() moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { // Initial state, no permissions loaded awaitItem().run { - assertThat(this.section).isEqualTo(section) assertThat(this.currentPermissions).isNull() - assertThat(this.items).isNotEmpty() + assertThat(this.itemsBySection).isNotEmpty() assertThat(this.hasChanges).isFalse() assertThat(this.saveAction).isEqualTo(AsyncAction.Uninitialized) assertThat(this.confirmExitAction).isEqualTo(AsyncAction.Uninitialized) @@ -50,42 +47,22 @@ class ChangeBaseRoomPermissionsPresenterTest { } @Test - fun `present - RoomDetails section contains the right items`() = runTest { - val section = ChangeRoomPermissionsSection.RoomDetails - val presenter = createChangeRoomPermissionsPresenter(section = section) + fun `present - items by section are correct for room`() = runTest { + val presenter = createChangeRoomPermissionsPresenter() moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - assertThat(awaitUpdatedItem().items).containsExactly( + val itemsBySection = awaitUpdatedItem().itemsBySection + assertThat(itemsBySection[RoomPermissionsSection.RoomDetails]).containsExactly( RoomPermissionType.ROOM_NAME, RoomPermissionType.ROOM_AVATAR, RoomPermissionType.ROOM_TOPIC, ) - } - } - - @Test - fun `present - MessagesAndContent section contains the right items`() = runTest { - val section = ChangeRoomPermissionsSection.MessagesAndContent - val presenter = createChangeRoomPermissionsPresenter(section = section) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - assertThat(awaitUpdatedItem().items).containsExactly( + assertThat(itemsBySection[RoomPermissionsSection.MessagesAndContent]).containsExactly( RoomPermissionType.SEND_EVENTS, RoomPermissionType.REDACT_EVENTS, ) - } - } - - @Test - fun `present - MembershipModeration section contains the right items`() = runTest { - val section = ChangeRoomPermissionsSection.MembershipModeration - val presenter = createChangeRoomPermissionsPresenter(section = section) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - assertThat(awaitUpdatedItem().items).containsExactly( + assertThat(itemsBySection[RoomPermissionsSection.MembershipModeration]).containsExactly( RoomPermissionType.INVITE, RoomPermissionType.KICK, RoomPermissionType.BAN, @@ -103,7 +80,7 @@ class ChangeBaseRoomPermissionsPresenterTest { assertThat(state.currentPermissions?.roomName).isEqualTo(Admin.powerLevel) assertThat(state.hasChanges).isFalse() - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, Moderator)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Moderator)) awaitItem().run { assertThat(currentPermissions?.roomName).isEqualTo(Moderator.powerLevel) @@ -120,18 +97,18 @@ class ChangeBaseRoomPermissionsPresenterTest { }.test { val state = awaitUpdatedItem() - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.INVITE, Moderator)) - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.KICK, Moderator)) - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.BAN, Moderator)) - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.SEND_EVENTS, Moderator)) - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.REDACT_EVENTS, Moderator)) - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, Moderator)) - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_AVATAR, Moderator)) - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_TOPIC, Moderator)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.INVITE, SelectableRole.Moderator)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.KICK, SelectableRole.Moderator)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.BAN, SelectableRole.Moderator)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.SEND_EVENTS, SelectableRole.Moderator)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.REDACT_EVENTS, SelectableRole.Moderator)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Moderator)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_AVATAR, SelectableRole.Moderator)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_TOPIC, SelectableRole.Moderator)) - val items = cancelAndConsumeRemainingEvents() + val itemsBySection = cancelAndConsumeRemainingEvents() - (items.last() as? Event.Item)?.value?.run { + (itemsBySection.last() as? Event.Item)?.value?.run { assertThat(currentPermissions).isEqualTo( RoomPowerLevelsValues( invite = Moderator.powerLevel, @@ -165,14 +142,14 @@ class ChangeBaseRoomPermissionsPresenterTest { assertThat(state.currentPermissions?.roomName).isEqualTo(Admin.powerLevel) assertThat(state.hasChanges).isFalse() - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, Moderator)) - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_AVATAR, Moderator)) - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_TOPIC, Moderator)) - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.SEND_EVENTS, Moderator)) - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.REDACT_EVENTS, User)) - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.KICK, Admin)) - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.BAN, Admin)) - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.INVITE, Admin)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Moderator)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_AVATAR, SelectableRole.Moderator)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_TOPIC, SelectableRole.Moderator)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.SEND_EVENTS, SelectableRole.Moderator)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.REDACT_EVENTS, SelectableRole.Everyone)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.KICK, SelectableRole.Admin)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.BAN, SelectableRole.Admin)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.INVITE, SelectableRole.Admin)) skipItems(7) assertThat(awaitItem().hasChanges).isTrue() @@ -230,7 +207,7 @@ class ChangeBaseRoomPermissionsPresenterTest { assertThat(state.currentPermissions?.roomName).isEqualTo(Admin.powerLevel) assertThat(state.hasChanges).isFalse() - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, Moderator)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Moderator)) assertThat(awaitItem().hasChanges).isTrue() state.eventSink(ChangeRoomPermissionsEvent.Save) @@ -259,7 +236,7 @@ class ChangeBaseRoomPermissionsPresenterTest { presenter.present() }.test { val state = awaitUpdatedItem() - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, Moderator)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Moderator)) assertThat(awaitItem().hasChanges).isTrue() state.eventSink(ChangeRoomPermissionsEvent.Exit) @@ -285,13 +262,11 @@ class ChangeBaseRoomPermissionsPresenterTest { } private fun createChangeRoomPermissionsPresenter( - section: ChangeRoomPermissionsSection = ChangeRoomPermissionsSection.RoomDetails, room: FakeJoinedRoom = FakeJoinedRoom( baseRoom = FakeBaseRoom(powerLevelsResult = { Result.success(defaultPermissions()) }), ), analyticsService: FakeAnalyticsService = FakeAnalyticsService(), ) = ChangeRoomPermissionsPresenter( - section = section, room = room, analyticsService = analyticsService, ) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeBaseRoomPermissionsViewTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsViewTest.kt similarity index 66% rename from features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeBaseRoomPermissionsViewTest.kt rename to features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsViewTest.kt index f4ab1ef1a9..9627d03b7c 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeBaseRoomPermissionsViewTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsViewTest.kt @@ -5,40 +5,40 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.rolesandpermissions.permissions +package io.element.android.features.rolesandpermissions.impl.permissions import androidx.activity.ComponentActivity import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.junit4.createAndroidComposeRule -import androidx.compose.ui.test.onAllNodesWithText -import androidx.compose.ui.test.onFirst -import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.element.android.features.roomdetails.impl.R +import io.element.android.features.rolesandpermissions.impl.R import io.element.android.libraries.architecture.AsyncAction -import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.ui.strings.CommonStrings -import io.element.android.tests.testutils.EnsureNeverCalled +import io.element.android.tests.testutils.EnsureNeverCalledWithParam import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.clickOnFirst -import io.element.android.tests.testutils.ensureCalledOnce +import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.pressBack import io.element.android.tests.testutils.pressBackKey +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentMapOf import org.junit.Rule import org.junit.Test import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) -class ChangeBaseRoomPermissionsViewTest { +class ChangeRoomPermissionsViewTest { @get:Rule val rule = createAndroidComposeRule() @Test fun `click on back icon invokes Exit`() { val recorder = EventsRecorder() rule.setChangeRoomPermissionsRule( - eventsRecorder = recorder, + state = aChangeRoomPermissionsState( + eventSink = recorder + ) ) rule.pressBack() recorder.assertSingle(ChangeRoomPermissionsEvent.Exit) @@ -48,7 +48,9 @@ class ChangeBaseRoomPermissionsViewTest { fun `click on back key invokes Exit`() { val recorder = EventsRecorder() rule.setChangeRoomPermissionsRule( - eventsRecorder = recorder, + state = aChangeRoomPermissionsState( + eventSink = recorder + ) ) rule.pressBackKey() recorder.assertSingle(ChangeRoomPermissionsEvent.Exit) @@ -59,11 +61,9 @@ class ChangeBaseRoomPermissionsViewTest { val recorder = EventsRecorder() rule.setChangeRoomPermissionsRule( state = aChangeRoomPermissionsState( - section = ChangeRoomPermissionsSection.RoomDetails, hasChanges = true, eventSink = recorder, ), - eventsRecorder = recorder, ) rule.pressBackKey() recorder.assertSingle(ChangeRoomPermissionsEvent.Exit) @@ -74,12 +74,10 @@ class ChangeBaseRoomPermissionsViewTest { val recorder = EventsRecorder() rule.setChangeRoomPermissionsRule( state = aChangeRoomPermissionsState( - section = ChangeRoomPermissionsSection.RoomDetails, hasChanges = true, confirmExitAction = AsyncAction.ConfirmingNoParams, eventSink = recorder, ), - eventsRecorder = recorder, ) rule.clickOn(CommonStrings.action_discard) recorder.assertSingle(ChangeRoomPermissionsEvent.Exit) @@ -90,12 +88,10 @@ class ChangeBaseRoomPermissionsViewTest { val recorder = EventsRecorder() rule.setChangeRoomPermissionsRule( state = aChangeRoomPermissionsState( - section = ChangeRoomPermissionsSection.RoomDetails, hasChanges = true, confirmExitAction = AsyncAction.ConfirmingNoParams, eventSink = recorder, ), - eventsRecorder = recorder, ) rule.clickOnFirst(CommonStrings.action_save) recorder.assertSingle(ChangeRoomPermissionsEvent.Save) @@ -105,21 +101,19 @@ class ChangeBaseRoomPermissionsViewTest { fun `click on a role item triggers ChangeRole event`() { val recorder = EventsRecorder() rule.setChangeRoomPermissionsRule( - eventsRecorder = recorder, - ) - val admins = rule.activity.getText(R.string.screen_room_change_permissions_administrators).toString() - val moderators = rule.activity.getText(R.string.screen_room_change_permissions_moderators).toString() - val users = rule.activity.getText(R.string.screen_room_change_permissions_everyone).toString() - rule.onAllNodesWithText(admins).onFirst().performClick() - rule.onAllNodesWithText(moderators).onFirst().performClick() - rule.onAllNodesWithText(users).onFirst().performClick() - recorder.assertList( - listOf( - ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, RoomMember.Role.Admin), - ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, RoomMember.Role.Moderator), - ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, RoomMember.Role.User), + state = aChangeRoomPermissionsState( + itemsBySection = persistentMapOf( + // Makes sure there is only one item to click on + RoomPermissionsSection.RoomDetails to persistentListOf(RoomPermissionType.ROOM_NAME) + ), + eventSink = recorder, ) ) + rule.clickOn(R.string.screen_room_change_permissions_room_name) + rule.clickOn(R.string.screen_room_change_permissions_everyone) + recorder.assertSingle( + ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Everyone), + ) } @Test @@ -127,11 +121,9 @@ class ChangeBaseRoomPermissionsViewTest { val recorder = EventsRecorder() rule.setChangeRoomPermissionsRule( state = aChangeRoomPermissionsState( - section = ChangeRoomPermissionsSection.RoomDetails, hasChanges = true, eventSink = recorder, ), - eventsRecorder = recorder, ) rule.clickOn(CommonStrings.action_save) recorder.assertSingle(ChangeRoomPermissionsEvent.Save) @@ -139,14 +131,13 @@ class ChangeBaseRoomPermissionsViewTest { @Test fun `a successful save exits the screen`() { - ensureCalledOnce { callback -> + ensureCalledOnceWithParam(true) { callback -> rule.setChangeRoomPermissionsRule( state = aChangeRoomPermissionsState( - section = ChangeRoomPermissionsSection.RoomDetails, hasChanges = true, saveAction = AsyncAction.Success(Unit), ), - onBackClick = callback + onComplete = callback ) rule.clickOn(CommonStrings.action_save) } @@ -157,12 +148,10 @@ class ChangeBaseRoomPermissionsViewTest { val recorder = EventsRecorder() rule.setChangeRoomPermissionsRule( state = aChangeRoomPermissionsState( - section = ChangeRoomPermissionsSection.RoomDetails, hasChanges = true, saveAction = AsyncAction.Failure(IllegalStateException("Failed to set room power levels")), eventSink = recorder, ), - eventsRecorder = recorder, ) rule.clickOn(CommonStrings.action_ok) recorder.assertSingle(ChangeRoomPermissionsEvent.ResetPendingActions) @@ -170,17 +159,13 @@ class ChangeBaseRoomPermissionsViewTest { } private fun AndroidComposeTestRule.setChangeRoomPermissionsRule( - eventsRecorder: EventsRecorder = EventsRecorder(expectEvents = false), - state: ChangeRoomPermissionsState = aChangeRoomPermissionsState( - section = ChangeRoomPermissionsSection.RoomDetails, - eventSink = eventsRecorder, - ), - onBackClick: () -> Unit = EnsureNeverCalled(), + state: ChangeRoomPermissionsState = aChangeRoomPermissionsState(), + onComplete: (Boolean) -> Unit = EnsureNeverCalledWithParam(), ) { setContent { ChangeRoomPermissionsView( state = state, - onBackClick = onBackClick, + onComplete = onComplete, ) } } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionPresenterTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionPresenterTest.kt similarity index 98% rename from features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionPresenterTest.kt rename to features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionPresenterTest.kt index 734cf4696a..d5350b05f4 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionPresenterTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionPresenterTest.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.rolesandpermissions +package io.element.android.features.rolesandpermissions.impl.root import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsViewTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsViewTest.kt similarity index 89% rename from features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsViewTest.kt rename to features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsViewTest.kt index 4aa68bc047..c9355d64d5 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsViewTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsViewTest.kt @@ -5,13 +5,13 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.rolesandpermissions +package io.element.android.features.rolesandpermissions.impl.root import androidx.activity.ComponentActivity import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.element.android.features.roomdetails.impl.R +import io.element.android.features.rolesandpermissions.impl.R import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.ui.strings.CommonStrings @@ -83,14 +83,12 @@ class RolesAndPermissionsViewTest { @Test @Config(qualifiers = "h640dp") - fun `tapping on any of the permission items open the change permissions screen`() { - ensureCalledTimes(3) { callback -> + fun `tapping permission item open the change permissions screen`() { + ensureCalledTimes(1) { callback -> rule.setRolesAndPermissionsView( - openPermissionScreens = callback, + openEditPermissions = callback, ) - rule.clickOn(R.string.screen_room_roles_and_permissions_room_details) - rule.clickOn(R.string.screen_room_roles_and_permissions_messages_and_content) - rule.clickOn(R.string.screen_room_roles_and_permissions_member_moderation) + rule.clickOn(R.string.screen_room_roles_and_permissions_permissions_header) } } @@ -184,7 +182,7 @@ private fun AndroidComposeTestRule.setRoles goBack: () -> Unit = EnsureNeverCalled(), openAdminList: () -> Unit = EnsureNeverCalled(), openModeratorList: () -> Unit = EnsureNeverCalled(), - openPermissionScreens: () -> Unit = EnsureNeverCalled(), + openEditPermissions: () -> Unit = EnsureNeverCalled(), ) { setSafeContent { RolesAndPermissionsView( @@ -193,9 +191,7 @@ private fun AndroidComposeTestRule.setRoles override fun onBackClick() = goBack() override fun openAdminList() = openAdminList() override fun openModeratorList() = openModeratorList() - override fun openEditRoomDetailsPermissions() = openPermissionScreens() - override fun openModerationPermissions() = openPermissionScreens() - override fun openMessagesAndContentPermissions() = openPermissionScreens() + override fun openEditPermissions() = openEditPermissions() } ) } From e9cab238be4558d40146ea879cd54672afffd268 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 5 Nov 2025 18:05:11 +0100 Subject: [PATCH 046/216] Add the user certificate if any when creating Matrix Client. --- .../impl/auth/RustHomeServerLoginCompatibilityChecker.kt | 3 +++ .../impl/auth/RustHomeserverLoginCompatibilityCheckerTest.kt | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeServerLoginCompatibilityChecker.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeServerLoginCompatibilityChecker.kt index 8cfaca077b..19d1572be5 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeServerLoginCompatibilityChecker.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeServerLoginCompatibilityChecker.kt @@ -13,17 +13,20 @@ import dev.zacsweers.metro.Inject import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.matrix.api.auth.HomeServerLoginCompatibilityChecker import io.element.android.libraries.matrix.impl.ClientBuilderProvider +import io.element.android.libraries.matrix.impl.certificates.UserCertificatesProvider import timber.log.Timber @ContributesBinding(AppScope::class) @Inject class RustHomeServerLoginCompatibilityChecker( private val clientBuilderProvider: ClientBuilderProvider, + private val userCertificatesProvider: UserCertificatesProvider, ) : HomeServerLoginCompatibilityChecker { override suspend fun check(url: String): Result = runCatchingExceptions { clientBuilderProvider.provide() .inMemoryStore() .serverNameOrHomeserverUrl(url) + .addRootCertificates(userCertificatesProvider.provides()) .build() .use { it.homeserverLoginDetails() diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeserverLoginCompatibilityCheckerTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeserverLoginCompatibilityCheckerTest.kt index 4557d47f5b..f24a4bc958 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeserverLoginCompatibilityCheckerTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeserverLoginCompatibilityCheckerTest.kt @@ -49,6 +49,7 @@ class RustHomeserverLoginCompatibilityCheckerTest { FakeFfiClientBuilder { FakeFfiClient(homeserverLoginDetailsResult = result) } - } + }, + userCertificatesProvider = FakeUserCertificatesProvider(), ) } From d9695ae6a0b16362ad7c0b6aedeeb80c0c734ab6 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 5 Nov 2025 21:42:51 +0100 Subject: [PATCH 047/216] change(roles and permissions): change title for space --- .../impl/permissions/ChangeRoomPermissionsPresenter.kt | 4 +++- .../impl/permissions/ChangeRoomPermissionsState.kt | 1 + .../impl/permissions/ChangeRoomPermissionsView.kt | 8 ++++---- .../impl/src/main/res/values/localazy.xml | 7 ++++--- .../roomdetails/impl/src/main/res/values/localazy.xml | 6 +++--- tools/localazy/config.json | 1 + 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt index 02966c79a3..b36d1c0bc9 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt @@ -35,6 +35,7 @@ class ChangeRoomPermissionsPresenter( ) : Presenter { companion object { private fun itemsForSection(section: RoomPermissionsSection) = when (section) { + RoomPermissionsSection.SpaceDetails, RoomPermissionsSection.RoomDetails -> persistentListOf( RoomPermissionType.ROOM_NAME, RoomPermissionType.ROOM_AVATAR, @@ -53,9 +54,10 @@ class ChangeRoomPermissionsPresenter( private fun RoomPermissionsSection.shouldShow(isSpace: Boolean): Boolean { return when (this) { - RoomPermissionsSection.RoomDetails -> true + RoomPermissionsSection.RoomDetails -> !isSpace RoomPermissionsSection.MembershipModeration -> true RoomPermissionsSection.MessagesAndContent -> !isSpace + RoomPermissionsSection.SpaceDetails -> isSpace } } diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt index b1032e5fac..c609b4a135 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt @@ -48,6 +48,7 @@ data class ChangeRoomPermissionsState( } enum class RoomPermissionsSection { + SpaceDetails, RoomDetails, MessagesAndContent, MembershipModeration, diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt index babe682717..165ebd0528 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt @@ -74,7 +74,6 @@ fun ChangeRoomPermissionsView( selectedOption = state.selectedRoleForType(permissionType), options = SelectableRole.entries.toImmutableList(), onSelectOption = { role -> - println("Selected $role for $permissionType") state.eventSink( ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction( action = permissionType, @@ -114,9 +113,10 @@ fun ChangeRoomPermissionsView( @Composable private fun titleForSection(section: RoomPermissionsSection): String = when (section) { - RoomPermissionsSection.RoomDetails -> stringResource(R.string.screen_room_change_permissions_room_details) - RoomPermissionsSection.MessagesAndContent -> stringResource(R.string.screen_room_change_permissions_messages_and_content) - RoomPermissionsSection.MembershipModeration -> stringResource(R.string.screen_room_change_permissions_member_moderation) + RoomPermissionsSection.SpaceDetails -> stringResource(R.string.screen_room_roles_and_permissions_space_details) + RoomPermissionsSection.RoomDetails -> stringResource(R.string.screen_room_roles_and_permissions_room_details) + RoomPermissionsSection.MessagesAndContent -> stringResource(R.string.screen_room_roles_and_permissions_messages_and_content) + RoomPermissionsSection.MembershipModeration -> stringResource(R.string.screen_room_roles_and_permissions_member_moderation) } @Composable diff --git a/features/rolesandpermissions/impl/src/main/res/values/localazy.xml b/features/rolesandpermissions/impl/src/main/res/values/localazy.xml index 43f6dc10f8..56c335f1c1 100644 --- a/features/rolesandpermissions/impl/src/main/res/values/localazy.xml +++ b/features/rolesandpermissions/impl/src/main/res/values/localazy.xml @@ -9,10 +9,10 @@ "Messages and content" "Admins and moderators" "Remove people and decline requests to join" - "Change room avatar" + "Change avatar" "Room details" - "Change room name" - "Change room topic" + "Change name" + "Change topic" "Send messages" "Edit Admins" "You will not be able to undo this action. You are promoting the user to have the same power level as you." @@ -66,5 +66,6 @@ "Reset permissions?" "Roles" "Room details" + "Space details" "Roles and permissions" diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml index 814f352abd..38f3be744f 100644 --- a/features/roomdetails/impl/src/main/res/values/localazy.xml +++ b/features/roomdetails/impl/src/main/res/values/localazy.xml @@ -14,10 +14,10 @@ "Messages and content" "Admins and moderators" "Remove people and decline requests to join" - "Change room avatar" + "Change avatar" "Room details" - "Change room name" - "Change room topic" + "Change name" + "Change topic" "Send messages" "Edit Admins" "You will not be able to undo this action. You are promoting the user to have the same power level as you." diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 4f57a9a822..39d63484a9 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -371,6 +371,7 @@ "includeRegex" : [ "screen_room_change_.*", "screen_room_roles_.*", + "screen\\.room_roles_and_permissions\\..*", "screen_room_member_list.*" ] } From 111b4f8b1b90f40365f96555c5597668c11f951d Mon Sep 17 00:00:00 2001 From: ElementBot Date: Wed, 5 Nov 2025 20:58:34 +0000 Subject: [PATCH 048/216] Update screenshots --- ...es.changeroommemberroles.impl_ChangeRolesView_Day_9_en.png | 3 --- ....changeroommemberroles.impl_ChangeRolesView_Night_9_en.png | 3 --- ...references.impl.advanced_AdvancedSettingsViewDark_0_en.png | 4 ++-- ...references.impl.advanced_AdvancedSettingsViewDark_1_en.png | 4 ++-- ...references.impl.advanced_AdvancedSettingsViewDark_2_en.png | 4 ++-- ...references.impl.advanced_AdvancedSettingsViewDark_3_en.png | 4 ++-- ...references.impl.advanced_AdvancedSettingsViewDark_4_en.png | 4 ++-- ...references.impl.advanced_AdvancedSettingsViewDark_5_en.png | 4 ++-- ...references.impl.advanced_AdvancedSettingsViewDark_6_en.png | 4 ++-- ...references.impl.advanced_AdvancedSettingsViewDark_7_en.png | 4 ++-- ...references.impl.advanced_AdvancedSettingsViewDark_8_en.png | 4 ++-- ...eferences.impl.advanced_AdvancedSettingsViewLight_0_en.png | 4 ++-- ...eferences.impl.advanced_AdvancedSettingsViewLight_1_en.png | 4 ++-- ...eferences.impl.advanced_AdvancedSettingsViewLight_2_en.png | 4 ++-- ...eferences.impl.advanced_AdvancedSettingsViewLight_3_en.png | 4 ++-- ...eferences.impl.advanced_AdvancedSettingsViewLight_4_en.png | 4 ++-- ...eferences.impl.advanced_AdvancedSettingsViewLight_5_en.png | 4 ++-- ...eferences.impl.advanced_AdvancedSettingsViewLight_6_en.png | 4 ++-- ...eferences.impl.advanced_AdvancedSettingsViewLight_7_en.png | 4 ++-- ...eferences.impl.advanced_AdvancedSettingsViewLight_8_en.png | 4 ++-- ...ns.impl.permissions_ChangeRoomPermissionsView_Day_0_en.png | 3 +++ ...ns.impl.permissions_ChangeRoomPermissionsView_Day_1_en.png | 3 +++ ...ns.impl.permissions_ChangeRoomPermissionsView_Day_2_en.png | 3 +++ ...ns.impl.permissions_ChangeRoomPermissionsView_Day_3_en.png | 3 +++ ...ns.impl.permissions_ChangeRoomPermissionsView_Day_4_en.png | 3 +++ ....impl.permissions_ChangeRoomPermissionsView_Night_0_en.png | 3 +++ ....impl.permissions_ChangeRoomPermissionsView_Night_1_en.png | 3 +++ ....impl.permissions_ChangeRoomPermissionsView_Night_2_en.png | 3 +++ ....impl.permissions_ChangeRoomPermissionsView_Night_3_en.png | 3 +++ ....impl.permissions_ChangeRoomPermissionsView_Night_4_en.png | 3 +++ ...lesandpermissions.impl.roles_ChangeRolesView_Day_0_en.png} | 0 ...esandpermissions.impl.roles_ChangeRolesView_Day_10_en.png} | 0 ...esandpermissions.impl.roles_ChangeRolesView_Day_11_en.png} | 0 ...esandpermissions.impl.roles_ChangeRolesView_Day_12_en.png} | 0 ...lesandpermissions.impl.roles_ChangeRolesView_Day_1_en.png} | 0 ...lesandpermissions.impl.roles_ChangeRolesView_Day_2_en.png} | 0 ...lesandpermissions.impl.roles_ChangeRolesView_Day_3_en.png} | 0 ...lesandpermissions.impl.roles_ChangeRolesView_Day_4_en.png} | 0 ...lesandpermissions.impl.roles_ChangeRolesView_Day_5_en.png} | 0 ...lesandpermissions.impl.roles_ChangeRolesView_Day_6_en.png} | 0 ...lesandpermissions.impl.roles_ChangeRolesView_Day_7_en.png} | 0 ...lesandpermissions.impl.roles_ChangeRolesView_Day_8_en.png} | 0 ...olesandpermissions.impl.roles_ChangeRolesView_Day_9_en.png | 3 +++ ...sandpermissions.impl.roles_ChangeRolesView_Night_0_en.png} | 0 ...andpermissions.impl.roles_ChangeRolesView_Night_10_en.png} | 0 ...andpermissions.impl.roles_ChangeRolesView_Night_11_en.png} | 0 ...andpermissions.impl.roles_ChangeRolesView_Night_12_en.png} | 0 ...sandpermissions.impl.roles_ChangeRolesView_Night_1_en.png} | 0 ...sandpermissions.impl.roles_ChangeRolesView_Night_2_en.png} | 0 ...sandpermissions.impl.roles_ChangeRolesView_Night_3_en.png} | 0 ...sandpermissions.impl.roles_ChangeRolesView_Night_4_en.png} | 0 ...sandpermissions.impl.roles_ChangeRolesView_Night_5_en.png} | 0 ...sandpermissions.impl.roles_ChangeRolesView_Night_6_en.png} | 0 ...sandpermissions.impl.roles_ChangeRolesView_Night_7_en.png} | 0 ...sandpermissions.impl.roles_ChangeRolesView_Night_8_en.png} | 0 ...esandpermissions.impl.roles_ChangeRolesView_Night_9_en.png | 3 +++ ...ions.impl.roles_PendingMemberRowWithLongName_Day_0_en.png} | 0 ...ns.impl.roles_PendingMemberRowWithLongName_Night_0_en.png} | 0 ...permissions.impl.root_RolesAndPermissionsView_Day_0_en.png | 3 +++ ...permissions.impl.root_RolesAndPermissionsView_Day_1_en.png | 3 +++ ...permissions.impl.root_RolesAndPermissionsView_Day_2_en.png | 3 +++ ...permissions.impl.root_RolesAndPermissionsView_Day_3_en.png | 3 +++ ...permissions.impl.root_RolesAndPermissionsView_Day_4_en.png | 3 +++ ...permissions.impl.root_RolesAndPermissionsView_Day_5_en.png | 3 +++ ...permissions.impl.root_RolesAndPermissionsView_Day_6_en.png | 3 +++ ...permissions.impl.root_RolesAndPermissionsView_Day_7_en.png | 3 +++ ...permissions.impl.root_RolesAndPermissionsView_Day_8_en.png | 3 +++ ...rmissions.impl.root_RolesAndPermissionsView_Night_0_en.png | 3 +++ ...rmissions.impl.root_RolesAndPermissionsView_Night_1_en.png | 3 +++ ...rmissions.impl.root_RolesAndPermissionsView_Night_2_en.png | 3 +++ ...rmissions.impl.root_RolesAndPermissionsView_Night_3_en.png | 3 +++ ...rmissions.impl.root_RolesAndPermissionsView_Night_4_en.png | 3 +++ ...rmissions.impl.root_RolesAndPermissionsView_Night_5_en.png | 3 +++ ...rmissions.impl.root_RolesAndPermissionsView_Night_6_en.png | 3 +++ ...rmissions.impl.root_RolesAndPermissionsView_Night_7_en.png | 3 +++ ...rmissions.impl.root_RolesAndPermissionsView_Night_8_en.png | 3 +++ ...issions.permissions_ChangeRoomPermissionsView_Day_0_en.png | 3 --- ...issions.permissions_ChangeRoomPermissionsView_Day_1_en.png | 3 --- ...issions.permissions_ChangeRoomPermissionsView_Day_2_en.png | 3 --- ...issions.permissions_ChangeRoomPermissionsView_Day_3_en.png | 3 --- ...issions.permissions_ChangeRoomPermissionsView_Day_4_en.png | 3 --- ...issions.permissions_ChangeRoomPermissionsView_Day_5_en.png | 3 --- ...issions.permissions_ChangeRoomPermissionsView_Day_6_en.png | 3 --- ...sions.permissions_ChangeRoomPermissionsView_Night_0_en.png | 3 --- ...sions.permissions_ChangeRoomPermissionsView_Night_1_en.png | 3 --- ...sions.permissions_ChangeRoomPermissionsView_Night_2_en.png | 3 --- ...sions.permissions_ChangeRoomPermissionsView_Night_3_en.png | 3 --- ...sions.permissions_ChangeRoomPermissionsView_Night_4_en.png | 3 --- ...sions.permissions_ChangeRoomPermissionsView_Night_5_en.png | 3 --- ...sions.permissions_ChangeRoomPermissionsView_Night_6_en.png | 3 --- ...l.rolesandpermissions_RolesAndPermissionsView_Day_0_en.png | 3 --- ...l.rolesandpermissions_RolesAndPermissionsView_Day_1_en.png | 3 --- ...l.rolesandpermissions_RolesAndPermissionsView_Day_2_en.png | 3 --- ...l.rolesandpermissions_RolesAndPermissionsView_Day_3_en.png | 3 --- ...l.rolesandpermissions_RolesAndPermissionsView_Day_4_en.png | 3 --- ...l.rolesandpermissions_RolesAndPermissionsView_Day_5_en.png | 3 --- ...l.rolesandpermissions_RolesAndPermissionsView_Day_6_en.png | 3 --- ...l.rolesandpermissions_RolesAndPermissionsView_Day_7_en.png | 3 --- ...l.rolesandpermissions_RolesAndPermissionsView_Day_8_en.png | 3 --- ...rolesandpermissions_RolesAndPermissionsView_Night_0_en.png | 3 --- ...rolesandpermissions_RolesAndPermissionsView_Night_1_en.png | 3 --- ...rolesandpermissions_RolesAndPermissionsView_Night_2_en.png | 3 --- ...rolesandpermissions_RolesAndPermissionsView_Night_3_en.png | 3 --- ...rolesandpermissions_RolesAndPermissionsView_Night_4_en.png | 3 --- ...rolesandpermissions_RolesAndPermissionsView_Night_5_en.png | 3 --- ...rolesandpermissions_RolesAndPermissionsView_Night_6_en.png | 3 --- ...rolesandpermissions_RolesAndPermissionsView_Night_7_en.png | 3 --- ...rolesandpermissions_RolesAndPermissionsView_Night_8_en.png | 3 --- ...mponents.preferences_PreferenceDropdown_Preferences_en.png | 4 ++-- 109 files changed, 128 insertions(+), 140 deletions(-) delete mode 100644 tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_9_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_9_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en.png rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Day_0_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Day_10_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Day_11_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Day_12_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Day_1_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Day_2_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Day_3_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Day_4_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Day_5_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Day_5_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Day_6_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Day_7_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Day_8_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en.png} (100%) create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en.png rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Night_0_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Night_10_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Night_11_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Night_12_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Night_1_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Night_2_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Night_3_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Night_4_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Night_5_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Night_5_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Night_6_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Night_7_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_ChangeRolesView_Night_8_en.png => features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en.png} (100%) create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en.png rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_PendingMemberRowWithLongName_Day_0_en.png => features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.changeroommemberroles.impl_PendingMemberRowWithLongName_Night_0_en.png => features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en.png} (100%) create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_0_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_1_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_2_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_3_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_4_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_5_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_6_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_0_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_1_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_2_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_3_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_4_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_5_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_6_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_8_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_0_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_1_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_3_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_4_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_5_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_6_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_7_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_8_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_9_en.png deleted file mode 100644 index 9be427d4b6..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_9_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b4885040583de68748dc10cf85e0f395f7ae8a0ef08d8aeed14826fafec22990 -size 65024 diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_9_en.png deleted file mode 100644 index ccb2d31d48..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_9_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ac14dd774fca2924f599bd831d4fcad9931011174108bae5da1bcf8a5db6c4cf -size 64688 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en.png index 7bfb23035b..b4caf353fa 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d20f0962fb92b64b0e8a507f2c30428d0349c176d1453ed423a6c447d350459e -size 47267 +oid sha256:5116af838229fd1d17eb6d63ffdde82e90b75ff01000f8bf6f83edb982555443 +size 47358 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en.png index d76c93c920..d14d977afe 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d39dbdbed4a1260170fc1a148860533320413efb367a5d31bc4111086378529c -size 47157 +oid sha256:4d2f0f54ecb417dcc7ebb2e125b61e9e01f682dd7fea0eabb729c243387b6004 +size 47247 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en.png index 9915cd5435..7791c3dada 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ed87edce4fc8ea22a2ba6d666bbaf21b14a359e2a3eec828e2edede23d5d1b1e -size 47146 +oid sha256:5ef498d98d519dfe496622ec3ec7289ef5e636bb4c8ce4512cde1997fba3247d +size 47237 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en.png index 49d0d774af..d0b1a44c01 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d446944a0408921659ef954036211fe341e8b0529b6e0c7ecc315998e14b8f43 -size 47136 +oid sha256:665aa1c40622f38b0abe12d763c260a07011f7c32e47442d44477d2fead32e6c +size 47224 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en.png index 1f32e3cb20..3abb98e008 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:61706045ca364252d30d9a36d276b83aeee5e4be2552fbd7a97be9378ebc3a37 -size 46979 +oid sha256:6df36eb76909634d7ef59d9fa44edc1aa1f87412b8524ec2a9f3c26554e89065 +size 47069 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en.png index 6a6f712306..a45ca7d865 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23a53698059b50b46bcbb6cedd9434aaffa77db460d7495b9306c809d90d2126 -size 47270 +oid sha256:64e6997a02c70f17866e90f19cea2878844738748ce9ad60f85fbf04a3f35d8a +size 47360 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en.png index 0f40386837..dee1f01854 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:779879fc4b976584305eca916e42bd3cf1b6c08bf9e857bf842ec4cd537d7c3a -size 46924 +oid sha256:eae7e14242722e0b65e6bfe02f0d8c54e4581113c19888acfdbfd84b260ba53a +size 47013 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en.png index a1b6e813b0..6e7bf498c1 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ec601dec7eb57e457ddb351fbe8f966b8f51e3dde12f3e7130e8229f4ed3079 -size 46559 +oid sha256:56d937d008aa6fea8b4dadc12eb313f217b15b2d7458d5d3b4b0af77918988fc +size 46648 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en.png index 8934c11f27..a293d0038e 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c647eb273c63b6e28f91a87d9e85f00c83d7f0cb6748965cd0c1391cac7fe918 -size 52991 +oid sha256:b1c44eb7d614c5db5c48a75cf483ee9364aa2113dffd79faf116835a21519b79 +size 53087 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en.png index f7d1a84a24..2de7d822fa 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ec2983e926cff79f13fa0851c4c7dd6c4fd0433f19c774136cef5cdd0ea8b52 -size 48932 +oid sha256:cd31762cef1c2d84318d3fcb63f08c3b544368c59643d6ea54174e9ca6e7191b +size 48985 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en.png index d742e90a11..167e63caf4 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c382988ddf0643e1c6e78aee175fed9232302a4bd6d10e078c3fdd74d471904b -size 48813 +oid sha256:7789e460d69fa17b7a412a456930f2383c0ebb84a7a603c7443f0e7b66f2be0e +size 48866 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en.png index eb2d20f165..96a27a4fc8 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b68b299ef3917fb201712817b056a3c328d9f0c4cee5e0216de49d1ee962ebe5 -size 48830 +oid sha256:2fddfcdd50bf43eec8b0c5739067609aede15b6541de75282274803464d5ffc4 +size 48885 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en.png index dc166a004a..056e1a8e21 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26ef88a68a260fa9096651badadd93c4ce69d150add269cbdfd303b3289de629 -size 48814 +oid sha256:85c09edfa3db27e686dceb7aa21e427a25cdc4860669cb716a820b8ebf8a255e +size 48868 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en.png index 319eb1d0b0..fdfaa42df9 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c56cbb0fbc5aadecac6cdd23fee7ee93e18843c7923b0d73dbc90468c29a217 -size 48735 +oid sha256:d694ba80e6faeb81a195064e59d3febe4630df718124e7ee842ba1aad2634117 +size 48789 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en.png index 19460ca25f..2e3b8e8deb 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76c25bf891f2d649c3822448d288cc77a1f50626d58fe381268df89b346dee6a -size 48932 +oid sha256:4b556d0d99e4e93a736e25f79df51bd02f465192496463d0480081105de1077c +size 48987 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en.png index e992029a8b..5c4df596af 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3314c594d699f376b5f06fd7da21ce318b5fee331c2e104ce04bbee59b5ba4fc -size 48753 +oid sha256:8e285f7f4435053db05f95dee11bf3c46f4649c1f6a2f6e18ad91e652f0725ac +size 48809 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en.png index 5e995f6e3f..1bddc35de9 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:97111fd3e96fc9007b271a9ecbb3571034ea79fc7998b12bc854fcc43b6d31b4 -size 48438 +oid sha256:607061d3f293b5d575b236cd24f8d13b3d2db6dbd76e21710b698ccdee38541b +size 48492 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en.png index 6a161b9137..859e412c51 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:704dba07bf3c49e8ba0f2cf62368c2ead4f27e6708572b4c38b5fffc89b89c26 -size 55181 +oid sha256:adfc7ec5289fbd2bcdc3b6be31b1644165f24bd422957bb038b381b57d309e98 +size 55242 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en.png new file mode 100644 index 0000000000..abe319f714 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9043007191252fac22ab58a635e0c59d96edcba741fccf9bb9812889eb15ca24 +size 60259 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en.png new file mode 100644 index 0000000000..282b3fc89e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cadaea0794b81e28b03c84b4331d2dd11df6cff63344e1e2561de9e3a1450c23 +size 60178 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en.png new file mode 100644 index 0000000000..5823a4adec --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f55bfa9823ab3480ed846e137ed7e1fa1f3a0011ef8034458ac2baa4261cd7e8 +size 52084 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en.png new file mode 100644 index 0000000000..ca5dd4e9c0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5ce4f5ac003767c68a89465e1eb396f7d5fb67f2157a4e00a56591820666644 +size 49522 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en.png new file mode 100644 index 0000000000..a14ec8aad7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c5692d76f33a622452727fc4b1d1438cb608038f1cb0b6ba86a7b8e98f143db +size 56335 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en.png new file mode 100644 index 0000000000..c5b0cafa86 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:644b0d4d7c9ac619fa277086071a35d08f24408816a73b5293acde325d06373b +size 58568 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en.png new file mode 100644 index 0000000000..70aa7e518e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a62487bdcd023634fbb7a81642821dbca4b813798aae1dfaa4d5b852d7e03fca +size 58470 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en.png new file mode 100644 index 0000000000..8f56a177e4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:08d28320336e02d0db2d4eb5b20793879d54e52f3dbb287f6399efcad5dbc47e +size 50565 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en.png new file mode 100644 index 0000000000..a0a2e1a701 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64bc6edeffdcec9381c9049383d72ed4f5fe785de3f0b24d9f7a32466a26f24b +size 47347 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en.png new file mode 100644 index 0000000000..b4dd57cfa6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:45da36f66abead151d7634c2c554862db03948eede77adf1f397fead1429e67e +size 54151 diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_0_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_10_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_11_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_12_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_12_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_1_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_2_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_3_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_4_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_5_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_5_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_5_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_6_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_7_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Day_8_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en.png new file mode 100644 index 0000000000..d1b21ebee2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:393bf3509a833f7af78a57e546ce6d35ca3bef4d4523ff12af657d1c573b87c5 +size 61190 diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_0_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_10_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_11_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_12_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_12_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_1_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_2_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_3_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_4_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_5_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_5_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_5_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_6_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_7_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_ChangeRolesView_Night_8_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en.png new file mode 100644 index 0000000000..f55e0ac7a6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:37a79b2f5d6aedaef2dec74defd0f2a17af49239058a606f0785e82e56d833ec +size 61887 diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_PendingMemberRowWithLongName_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_PendingMemberRowWithLongName_Day_0_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_PendingMemberRowWithLongName_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.changeroommemberroles.impl_PendingMemberRowWithLongName_Night_0_en.png rename to tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en.png new file mode 100644 index 0000000000..7ba2742dab --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b64f4c2c68338cd9acc6e2644b41eb3d30463e6ee5da2eba0d93b1ff2340a563 +size 28791 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en.png new file mode 100644 index 0000000000..56f241f10c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6be26ff8ed8f9907fbab1778a0b112c649e8bd8059f0ae407b52634f9fe4b136 +size 30630 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en.png new file mode 100644 index 0000000000..83a633fc78 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d57be3aa2c919a957f99a6b8da269fae5791dd5ffea4dd2113e91ee82796c35 +size 59091 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en.png new file mode 100644 index 0000000000..fe7577ec55 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f55f5714eb073ad4649ff9b5d2924239db6063701cdca61941a970a5f7f78197 +size 28038 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en.png new file mode 100644 index 0000000000..29e8c35dfe --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6dcb28cfda314fa41d86ae47c6e9e7275798515b735cf4ebfb51c26d4412b4ad +size 27047 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en.png new file mode 100644 index 0000000000..d581dc2637 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f7e2b3d4d2aa0349db9641afd230cf3c532f11bd05c2fd821657a5665190324f +size 37175 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en.png new file mode 100644 index 0000000000..fe7577ec55 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f55f5714eb073ad4649ff9b5d2924239db6063701cdca61941a970a5f7f78197 +size 28038 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en.png new file mode 100644 index 0000000000..cba28447f4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0b069df8c405ef8556a0cbe9a68fae9788ba55eaf940a11c1b20b1732d79114 +size 27344 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en.png new file mode 100644 index 0000000000..16aae512d1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bfc46b428fcd6950911089d1f09a7cb6f6aa5009d2ce39f4b00ce706a3a41fb3 +size 27175 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en.png new file mode 100644 index 0000000000..58c89081f0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:320d0da30157930114f60d10a1da565780f9869340dc9f0b71b70552e9b8bd0c +size 27990 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en.png new file mode 100644 index 0000000000..d69a5bfb78 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c0c78fbc29259d21b5f66d86bb943153d4a1f35409785629306a672234493dfd +size 29741 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en.png new file mode 100644 index 0000000000..ceff3d7f25 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e8141af3945e72cbdedd38062d9606c4a1646fd75ce169cf5aec9369968404d +size 56721 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en.png new file mode 100644 index 0000000000..367f6cbce4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af38118f74d11e03a7cc6776aa64d9955b9d494a38f21396393eea0b7e133f83 +size 26464 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en.png new file mode 100644 index 0000000000..2fc5472b0d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c42bb6f9615803253f395759dc6d86c940131fec23f6c6f1d83cef9375452cd9 +size 25071 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en.png new file mode 100644 index 0000000000..e0a8bfadda --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75747b889fd518a514c5058b86676c89c68ff9557505b39ee9ea04b62a83b8ab +size 34616 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en.png new file mode 100644 index 0000000000..367f6cbce4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af38118f74d11e03a7cc6776aa64d9955b9d494a38f21396393eea0b7e133f83 +size 26464 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en.png new file mode 100644 index 0000000000..dec47a2fcb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c82ffeae6eb17e27996357e1538ade2b962ecb6b6ccda8a6e5535a8b98f991c +size 25386 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en.png new file mode 100644 index 0000000000..9e823c8497 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a653512dc37cbe52f5623278358b643e9c8e6c706a1a3f6999a579b6d8ceecc +size 26423 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_0_en.png deleted file mode 100644 index de3f3a93b9..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:80f4f24de2db8c0b1462a6dd77cb138132dc37aa1cc000508d0cf66129fa486f -size 39962 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_1_en.png deleted file mode 100644 index 9762549b31..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_1_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:80b6d0cef1f4709ab0f519c3168d9e0f9b7a7ffff8f7d4a8fbc5b10653b33042 -size 36279 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_2_en.png deleted file mode 100644 index 863e6bdf1c..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_2_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:13095117980b3b3dde8df1b2404bad43187d17cdcaf51805cf45baa684bb5086 -size 46677 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_3_en.png deleted file mode 100644 index d07b5f214e..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_3_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0e32a7a1ec92aa005734e877ff995bc40420710e80eea350ffbc1530095ad48c -size 39857 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_4_en.png deleted file mode 100644 index 42da0d9e8c..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_4_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3e89506e845b0a9d80831b1f65fa3793aee784d5a02ab69241a26384d9d43251 -size 36292 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_5_en.png deleted file mode 100644 index df10b8c6e2..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_5_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:79b02cbdef9a555911ba7e775642b08473f271a92e045d4f7c141ccfe63170ca -size 37204 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_6_en.png deleted file mode 100644 index 6107455bd9..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_6_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:785820fd67e137c5347060cf4d07dda5dc24e2349c8bb2a418895fc6ce9069b1 -size 44079 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_0_en.png deleted file mode 100644 index f2691ad84f..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:61e3c25edb23657021113a1201408200f85f0b2dcba9e96208a7125027cf642f -size 38699 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_1_en.png deleted file mode 100644 index 5e779061e0..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_1_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:11738c6de6751b37430db66d377478d38b7ac241c14d7bb2ab8b48206908fbda -size 35175 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_2_en.png deleted file mode 100644 index 58226e7852..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_2_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8a15405c5998265da00876bac43ab2d70fca74fab916d2014207b46997b0fed5 -size 45030 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_3_en.png deleted file mode 100644 index 2d15a73061..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_3_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f524740b14ffe3b5a70983b42c308cdcd1d7bfe7dbd0738aed3e79dba4673bbe -size 38559 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_4_en.png deleted file mode 100644 index 2fc07b3421..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_4_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c25628ffa6457a573b8678ab95ee7bb80315ba8a43d20a16e3d7c03baab7ea9f -size 35201 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_5_en.png deleted file mode 100644 index 56520adc2a..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_5_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ab0970def368ef59ecfbc1b7cec7e0dcdd8156a6f642701acf9dd54527080568 -size 35440 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_6_en.png deleted file mode 100644 index 7faaed8c07..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_6_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:742d98c87eaff31e460614f15ec4a5f03797669378099b5be3e0785320d0a0d1 -size 42171 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_en.png deleted file mode 100644 index 1acc4da150..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f1129fe5ebc6cebbba907ca15e92b33d5ee645c431d0022758bfc5d6e28bf69e -size 41003 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_en.png deleted file mode 100644 index e906d843c7..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fe1c01cc7c9ea9f50a0413505b516a86831416416c82488a9339785c89b3e0d3 -size 42769 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en.png deleted file mode 100644 index cf46f94231..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d08941b66dd884cb3eb73fea0ccbf535ab2b42a46fb1ac83d220832e42b486a6 -size 59083 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_en.png deleted file mode 100644 index dccc6a1f32..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:09ca684dc0f03aa68e658c4f1c780dde6d8c1739255899936f320595098c2c80 -size 39542 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_en.png deleted file mode 100644 index 97b5326743..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e71878e1dd445a58b3d79db5863ec4449729bf0bb1549c09848923fb9bf052d9 -size 38390 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_en.png deleted file mode 100644 index d25877daf2..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:558ba83131a9d79e32e0c5566a170e1c62b5bccbb4ad23f9bc58e271ea3f89e9 -size 48462 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_en.png deleted file mode 100644 index dccc6a1f32..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:09ca684dc0f03aa68e658c4f1c780dde6d8c1739255899936f320595098c2c80 -size 39542 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_en.png deleted file mode 100644 index b1912920cb..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:77f4b8e6ec426104ebe81308b2cf0ad111e3fbfa07c2086a2199ab8e9e952e8a -size 38719 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_8_en.png deleted file mode 100644 index 4793c85c21..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_8_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9000726c2e4e6115bb18f09193d84d4ab1948e70e35f27d0bfa514be96584510 -size 39123 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_0_en.png deleted file mode 100644 index aee278ece4..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d009d8ede606e8cd4bc150a292aac5c58c38195b188412a55df686ef74738e08 -size 39747 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_1_en.png deleted file mode 100644 index 62e2a03cf7..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_1_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fa618f575a11ed7c8521d754f54f41745ede3f2f9f95b242d9f04f641446c581 -size 41500 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en.png deleted file mode 100644 index e6fe537947..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b0063c4f776093e3c53beb164bd21d5ca0c32a865b10e848826a6f7859267f00 -size 56716 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_3_en.png deleted file mode 100644 index 147635d0f9..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_3_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e47c40c3173a80230c88f7e242f0236e6de3ed255552eed2d54a71fb136aa842 -size 37809 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_4_en.png deleted file mode 100644 index 98e04f83fe..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_4_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0ae08d2f3f6bc8b5364d3b9ea3584c21efe3180001d0babf209c6ef3fbf41e9a -size 35871 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_5_en.png deleted file mode 100644 index 5a7c8850fd..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_5_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:01fe728028fd86ccaf8ff7b08eb0b1b1e21ad336da97f39d818304e769cd65aa -size 45374 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_6_en.png deleted file mode 100644 index 147635d0f9..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_6_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e47c40c3173a80230c88f7e242f0236e6de3ed255552eed2d54a71fb136aa842 -size 37809 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_7_en.png deleted file mode 100644 index c2c1b2c9f2..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_7_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a7ae5371560ae6a92f0fb6537d8359c1bc40a6c43e3f12fd365019cbb2debe72 -size 36210 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_8_en.png deleted file mode 100644 index 1d42c39f20..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_8_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a5c945af810f3bde63d99284b97b127a10b7ec222a560a406284b4dac2b0e3f9 -size 38022 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.preferences_PreferenceDropdown_Preferences_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.preferences_PreferenceDropdown_Preferences_en.png index be32cb24a3..9e7bb7a63e 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.preferences_PreferenceDropdown_Preferences_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.preferences_PreferenceDropdown_Preferences_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f02ee83a49656553dd59648442bf4d558f6257fff3b30b768673d531cbe10150 -size 30928 +oid sha256:9af7f91767070914af81742b8f9d9a85c68e5aa818d19b7981527eb7e4360ec4 +size 31366 From aec9fd59e795509fd2c5642812ea7b4945f6a46c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 6 Nov 2025 09:02:28 +0100 Subject: [PATCH 049/216] fix(deps): update dependency androidx.core:core-splashscreen to v1.2.0 (#5687) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5cfc639d67..a3f6eed13c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -103,7 +103,7 @@ androidx_recyclerview = "androidx.recyclerview:recyclerview:1.4.0" androidx_browser = "androidx.browser:browser:1.9.0" androidx_lifecycle_runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle" } androidx_lifecycle_process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle" } -androidx_splash = "androidx.core:core-splashscreen:1.0.1" +androidx_splash = "androidx.core:core-splashscreen:1.2.0" androidx_media3_exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" } androidx_media3_ui = { module = "androidx.media3:media3-ui", version.ref = "media3" } androidx_media3_transformer = { module = "androidx.media3:media3-transformer", version.ref = "media3" } From 06226c1052c7f7fc744348d7d7c59ed9f280ffeb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 6 Nov 2025 09:33:43 +0100 Subject: [PATCH 050/216] Rename preview. --- .../designsystem/components/dialogs/SaveChangesDialog.kt | 2 +- ...ignsystem.components.dialogs_SaveChangesDialog_Day_0_en.png} | 0 ...nsystem.components.dialogs_SaveChangesDialog_Night_0_en.png} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.components.dialogs_SaveChangeDialog_Day_0_en.png => libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.designsystem.components.dialogs_SaveChangeDialog_Night_0_en.png => libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en.png} (100%) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SaveChangesDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SaveChangesDialog.kt index e630636894..f970904cc0 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SaveChangesDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SaveChangesDialog.kt @@ -31,7 +31,7 @@ fun SaveChangesDialog( @PreviewsDayNight @Composable -internal fun SaveChangeDialogPreview() = ElementPreview { +internal fun SaveChangesDialogPreview() = ElementPreview { SaveChangesDialog( onSubmitClick = {}, onDismiss = {} diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.dialogs_SaveChangeDialog_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.dialogs_SaveChangeDialog_Day_0_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.dialogs_SaveChangeDialog_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.designsystem.components.dialogs_SaveChangeDialog_Night_0_en.png rename to tests/uitests/src/test/snapshots/images/libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en.png From 91c7b597cea0dc74b68dd50f4385dbd3676f2720 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 6 Nov 2025 10:50:46 +0100 Subject: [PATCH 051/216] design: improve PreferenceDropdown --- .../components/preferences/PreferenceDropdown.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDropdown.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDropdown.kt index 389dc6c15b..fd76ef4c28 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDropdown.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceDropdown.kt @@ -10,8 +10,10 @@ package io.element.android.libraries.designsystem.components.preferences import androidx.annotation.DrawableRes +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable @@ -22,6 +24,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme @@ -87,6 +91,7 @@ fun PreferenceDropdown( onSelectOption = onSelectOption, expanded = isDropdownExpanded, onExpandedChange = { isDropdownExpanded = it }, + modifier = Modifier.fillMaxSize(0.3f) ) } ), @@ -117,12 +122,16 @@ private fun DropdownTrailingContent( Row( modifier = modifier, verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.End, ) { Text( text = selectedOption?.getText().orEmpty(), maxLines = 1, style = ElementTheme.typography.fontBodyMdRegular, color = ElementTheme.colors.textSecondary, + overflow = TextOverflow.Ellipsis, + textAlign = TextAlign.End, + modifier = Modifier.weight(1f), ) Icon( imageVector = CompoundIcons.ChevronDown(), From 910bac9c4e03e546e437f4ac62aea41cd1328ef4 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 6 Nov 2025 10:58:51 +0100 Subject: [PATCH 052/216] misc: introduce @RoomCoroutineScope --- .../di/annotations/RoomCoroutineScope.kt | 18 +++++++++++++ .../libraries/matrix/impl/di/RoomModule.kt | 26 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 libraries/di/src/main/kotlin/io/element/android/libraries/di/annotations/RoomCoroutineScope.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/RoomModule.kt diff --git a/libraries/di/src/main/kotlin/io/element/android/libraries/di/annotations/RoomCoroutineScope.kt b/libraries/di/src/main/kotlin/io/element/android/libraries/di/annotations/RoomCoroutineScope.kt new file mode 100644 index 0000000000..eee5115df9 --- /dev/null +++ b/libraries/di/src/main/kotlin/io/element/android/libraries/di/annotations/RoomCoroutineScope.kt @@ -0,0 +1,18 @@ +/* + * Copyright 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. + */ + +package io.element.android.libraries.di.annotations + +import dev.zacsweers.metro.Qualifier + +/** + * Qualifies a [CoroutineScope] object which represents the base coroutine scope to use for an active room. + */ +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +@Qualifier +annotation class RoomCoroutineScope diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/RoomModule.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/RoomModule.kt new file mode 100644 index 0000000000..9fd9f7eade --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/RoomModule.kt @@ -0,0 +1,26 @@ +/* + * 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. + */ + +package io.element.android.libraries.matrix.impl.di + +import dev.zacsweers.metro.BindingContainer +import dev.zacsweers.metro.ContributesTo +import dev.zacsweers.metro.Provides +import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.di.annotations.RoomCoroutineScope +import io.element.android.libraries.matrix.api.room.BaseRoom +import kotlinx.coroutines.CoroutineScope + +@BindingContainer +@ContributesTo(RoomScope::class) +object RoomModule { + @RoomCoroutineScope + @Provides + fun providesSessionCoroutineScope(room: BaseRoom): CoroutineScope { + return room.roomCoroutineScope + } +} From fbe7476ba7d3756565e690c8f36e57989f43c084 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 6 Nov 2025 10:59:24 +0100 Subject: [PATCH 053/216] change(roles and permissions): use @RoomCoroutineScope instead of local scope --- .../impl/roles/ChangeRolesPresenter.kt | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt index e2049336a2..87a659f7ce 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt @@ -15,7 +15,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.produceState import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted @@ -27,6 +26,7 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.designsystem.theme.components.SearchBarResultState +import io.element.android.libraries.di.annotations.RoomCoroutineScope 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.RoomMember @@ -41,12 +41,10 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext @AssistedInject class ChangeRolesPresenter( @@ -54,6 +52,7 @@ class ChangeRolesPresenter( private val room: JoinedRoom, private val dispatchers: CoroutineDispatchers, private val analyticsService: AnalyticsService, + @RoomCoroutineScope private val roomCoroutineScope: CoroutineScope, ) : Presenter { @AssistedFactory fun interface Factory { @@ -62,7 +61,6 @@ class ChangeRolesPresenter( @Composable override fun present(): ChangeRolesState { - val coroutineScope = rememberCoroutineScope() val dataSource = remember { RoomMemberListDataSource(room, dispatchers) } var query by rememberSaveable { mutableStateOf(null) } var searchActive by rememberSaveable { mutableStateOf(false) } @@ -145,7 +143,7 @@ class ChangeRolesPresenter( saveState.value = AsyncAction.ConfirmingNoParams } !saveState.value.isLoading() -> { - coroutineScope.save(usersWithRole.value, selectedUsers, saveState) + roomCoroutineScope.save(usersWithRole.value, selectedUsers, saveState) } } } @@ -217,11 +215,7 @@ class ChangeRolesPresenter( } .onSuccess { // Asynchronously reload the room members - launch { - withContext(NonCancellable) { - room.updateMembers() - } - } + launch { room.updateMembers() } saveState.value = AsyncAction.Success(true) } } From c6df9df7188fbf7328d52976ab537c24f28d4249 Mon Sep 17 00:00:00 2001 From: Ben Banfield-Zanin Date: Thu, 6 Nov 2025 10:23:07 +0000 Subject: [PATCH 054/216] Use the dedicated (element.io !!) subdomain for the bug report URL by default. --- appconfig/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appconfig/build.gradle.kts b/appconfig/build.gradle.kts index 15b8bd51ba..166aafe2ff 100644 --- a/appconfig/build.gradle.kts +++ b/appconfig/build.gradle.kts @@ -32,7 +32,7 @@ android { value = if (isEnterpriseBuild) { BuildTimeConfig.BUG_REPORT_URL ?: "" } else { - "https://riot.im/bugreports/submit" + "https://rageshakes.element.io/api/submit" }, ) buildConfigFieldStr( From 0be6d130257c919b9c55d3655c2358a9b2ad628b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 6 Nov 2025 12:22:48 +0100 Subject: [PATCH 055/216] MatrixAuthenticationService: remove `fun getHomeserverDetails(): StateFlow`. The MatrixHomeServerDetails are now return by `setHomeserver` --- .../login/impl/changeserver/ChangeServerPresenter.kt | 8 +++----- .../android/features/login/impl/login/LoginHelper.kt | 3 +-- .../matrix/api/auth/MatrixAuthenticationService.kt | 9 ++++++--- .../matrix/impl/auth/RustMatrixAuthenticationService.kt | 7 ++----- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt index 4df9eb12d5..d3db7496c5 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt @@ -60,11 +60,9 @@ class ChangeServerPresenter( title = data.title, accountProviderUrl = data.url, ) - authenticationService.setHomeserver(data.url).map { - authenticationService.getHomeserverDetails().value!! - // Valid, remember user choice - accountProviderDataSource.userSelection(data) - }.getOrThrow() + authenticationService.setHomeserver(data.url).getOrThrow() + // Homeserver is valid, remember user choice + accountProviderDataSource.userSelection(data) }.runCatchingUpdatingState(changeServerAction, errorTransform = ChangeServerError::from) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginHelper.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginHelper.kt index 82ee87c372..8dbd2a4df3 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginHelper.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginHelper.kt @@ -65,8 +65,7 @@ class LoginHelper( loginHint: String?, ) = coroutineScope.launch { suspend { - authenticationService.setHomeserver(homeserverUrl).map { - val matrixHomeServerDetails = authenticationService.getHomeserverDetails().value!! + authenticationService.setHomeserver(homeserverUrl).map { matrixHomeServerDetails -> if (matrixHomeServerDetails.supportsOidcLogin) { // Retrieve the details right now val oidcPrompt = if (isAccountCreation) OidcPrompt.Create else OidcPrompt.Login diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt index 38777944ee..1255e40a14 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt @@ -13,7 +13,6 @@ import io.element.android.libraries.matrix.api.auth.external.ExternalSession import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep import io.element.android.libraries.matrix.api.core.SessionId -import kotlinx.coroutines.flow.StateFlow interface MatrixAuthenticationService { /** @@ -22,8 +21,12 @@ interface MatrixAuthenticationService { * Generally this method should not be used directly, prefer using [MatrixClientProvider.getOrRestore] instead. */ suspend fun restoreSession(sessionId: SessionId): Result - fun getHomeserverDetails(): StateFlow - suspend fun setHomeserver(homeserver: String): Result + + /** + * Set the homeserver to use for authentication, and return its details. + */ + suspend fun setHomeserver(homeserver: String): Result + suspend fun login(username: String, password: String): Result /** diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt index 9859358ae6..7ed523812f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt @@ -37,7 +37,6 @@ import io.element.android.libraries.sessionstorage.api.LoginType import io.element.android.libraries.sessionstorage.api.SessionStore import kotlinx.coroutines.CancellationException import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.ClientBuilder @@ -111,9 +110,7 @@ class RustMatrixAuthenticationService( return passphrase } - override fun getHomeserverDetails(): StateFlow = currentHomeserver - - override suspend fun setHomeserver(homeserver: String): Result = + override suspend fun setHomeserver(homeserver: String): Result = withContext(coroutineDispatchers.io) { val emptySessionPath = rotateSessionPath() runCatchingExceptions { @@ -123,7 +120,7 @@ class RustMatrixAuthenticationService( currentClient = client val homeServerDetails = client.homeserverLoginDetails().map() - currentHomeserver.value = homeServerDetails.copy(url = homeserver) + homeServerDetails.copy(url = homeserver) }.onFailure { clear() }.mapFailure { failure -> From cee6475eb859a5fe35771d8181345188b00ea498 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 6 Nov 2025 12:29:08 +0100 Subject: [PATCH 056/216] Do not override the value of url returned by the SDK --- .../matrix/impl/auth/RustMatrixAuthenticationService.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt index 7ed523812f..dc93b9aaf5 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt @@ -36,7 +36,6 @@ import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory import io.element.android.libraries.sessionstorage.api.LoginType import io.element.android.libraries.sessionstorage.api.SessionStore import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.ClientBuilder @@ -66,7 +65,6 @@ class RustMatrixAuthenticationService( // Ideally it would be possible to get the sessionPath from the Client to avoid doing this. private var sessionPaths: SessionPaths? = null private var currentClient: Client? = null - private var currentHomeserver = MutableStateFlow(null) private val newMatrixClientObservers = mutableListOf<(MatrixClient) -> Unit>() override fun listenToNewMatrixClients(lambda: (MatrixClient) -> Unit) { @@ -119,8 +117,7 @@ class RustMatrixAuthenticationService( } currentClient = client - val homeServerDetails = client.homeserverLoginDetails().map() - homeServerDetails.copy(url = homeserver) + client.homeserverLoginDetails().map() }.onFailure { clear() }.mapFailure { failure -> From 5fe2c86e1d5e8862169474d5f085b7bcc1066c0f Mon Sep 17 00:00:00 2001 From: Skye Elliot Date: Wed, 5 Nov 2025 17:04:19 +0000 Subject: [PATCH 057/216] feat: Convert `ComposerAlertMolecule` to use alert levels. --- .../features/messages/impl/MessagesView.kt | 1 - .../identity/IdentityChangeStateView.kt | 3 +- .../virtual/TimelineItemRoomBeginningView.kt | 1 - .../atomic/molecules/ComposerAlertMolecule.kt | 88 ++++++++++++++++--- 4 files changed, 78 insertions(+), 15 deletions(-) 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 0221bf2c94..23dca9467f 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 @@ -522,7 +522,6 @@ private fun SuccessorRoomBanner( content = stringResource(R.string.screen_room_timeline_tombstoned_room_message).toAnnotatedString(), onSubmitClick = { onRoomSuccessorClick(roomSuccessor.roomId) }, modifier = modifier, - isCritical = false, submitText = stringResource(R.string.screen_room_timeline_tombstoned_room_action) ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateView.kt index 0f55985740..f5f81a3b64 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateView.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.appconfig.LearnMoreConfig import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.designsystem.atomic.molecules.ComposerAlertLevel import io.element.android.libraries.designsystem.atomic.molecules.ComposerAlertMolecule import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -113,7 +114,7 @@ private fun ViolationAlert( }, submitText = stringResource(submitTextId), onSubmitClick = onSubmitClick, - isCritical = isCritical, + level = if (isCritical) ComposerAlertLevel.Critical else ComposerAlertLevel.Default, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt index a3cab8610f..65667041c3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt @@ -45,7 +45,6 @@ fun TimelineItemRoomBeginningView( avatar = null, content = stringResource(R.string.screen_room_timeline_upgraded_room_message).toAnnotatedString(), onSubmitClick = { onPredecessorRoomClick(predecessorRoom.roomId) }, - isCritical = false, submitText = stringResource(R.string.screen_room_timeline_upgraded_room_action) ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ComposerAlertMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ComposerAlertMolecule.kt index 07e2454dbc..b898871a6b 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ComposerAlertMolecule.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ComposerAlertMolecule.kt @@ -18,12 +18,15 @@ import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize @@ -34,8 +37,8 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.text.toAnnotatedString 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.Text -import io.element.android.libraries.designsystem.utils.BooleanProvider import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -44,20 +47,37 @@ fun ComposerAlertMolecule( content: AnnotatedString, onSubmitClick: () -> Unit, modifier: Modifier = Modifier, - isCritical: Boolean = false, + level: ComposerAlertLevel = ComposerAlertLevel.Default, + showIcon: Boolean = false, submitText: String = stringResource(CommonStrings.action_ok), ) { Column( modifier.fillMaxWidth() ) { - val lineColor = if (isCritical) ElementTheme.colors.borderCriticalSubtle else ElementTheme.colors.borderInfoSubtle + val lineColor = when (level) { + ComposerAlertLevel.Default -> ElementTheme.colors.borderInfoSubtle + ComposerAlertLevel.Info -> ElementTheme.colors.borderInfoSubtle + ComposerAlertLevel.Critical -> ElementTheme.colors.borderCriticalSubtle + } + + val startColor = when (level) { + ComposerAlertLevel.Default -> ElementTheme.colors.bgInfoSubtle + ComposerAlertLevel.Info -> ElementTheme.colors.bgInfoSubtle + ComposerAlertLevel.Critical -> ElementTheme.colors.bgCriticalSubtle + } + + val textColor = when (level) { + ComposerAlertLevel.Default -> ElementTheme.colors.textPrimary + ComposerAlertLevel.Info -> ElementTheme.colors.textInfoPrimary + ComposerAlertLevel.Critical -> ElementTheme.colors.textCriticalPrimary + } + Box( modifier = Modifier .fillMaxWidth() .height(1.dp) .background(lineColor) ) - val startColor = if (isCritical) ElementTheme.colors.bgCriticalSubtle else ElementTheme.colors.bgInfoSubtle val brush = Brush.verticalGradient( listOf(startColor, ElementTheme.colors.bgCanvasDefault), ) @@ -77,16 +97,28 @@ fun ComposerAlertMolecule( avatarData = avatar, avatarType = AvatarType.User, ) + } else if (showIcon) { + val icon = when (level) { + ComposerAlertLevel.Default -> CompoundIcons.Info() + ComposerAlertLevel.Info -> CompoundIcons.Info() + ComposerAlertLevel.Critical -> CompoundIcons.Error() + } + val iconTint = when (level) { + ComposerAlertLevel.Default -> ElementTheme.colors.iconPrimary + ComposerAlertLevel.Info -> ElementTheme.colors.iconInfoPrimary + ComposerAlertLevel.Critical -> ElementTheme.colors.iconCriticalPrimary + } + Icon( + painter = rememberVectorPainter(icon), + tint = iconTint, + contentDescription = null, + ) } Text( text = content, modifier = Modifier.weight(1f), style = ElementTheme.typography.fontBodyMdRegular, - color = if (isCritical) { - ElementTheme.colors.textCriticalPrimary - } else { - ElementTheme.colors.textPrimary - }, + color = textColor, textAlign = TextAlign.Start, ) } @@ -101,13 +133,45 @@ fun ComposerAlertMolecule( } } +enum class ComposerAlertLevel { + Default, + Info, + Critical +} + +internal data class ComposerAlertMoleculeParams( + val level: ComposerAlertLevel, + val avatar: AvatarData? = null, + val showIcon: Boolean = false, +) + +internal class ComposerAlertMoleculeParamsProvider : PreviewParameterProvider { + private val allLevels = sequenceOf( + ComposerAlertLevel.Default, + ComposerAlertLevel.Info, + ComposerAlertLevel.Critical + ) + + override val values: Sequence + get() = allLevels.flatMap { level -> + sequenceOf( + ComposerAlertMoleculeParams(level = level), + ComposerAlertMoleculeParams(level = level, avatar = anAvatarData(size = AvatarSize.ComposerAlert)), + ComposerAlertMoleculeParams(level = level, showIcon = true), + ) + } +} + @PreviewsDayNight @Composable -internal fun ComposerAlertMoleculePreview(@PreviewParameter(BooleanProvider::class) isCritical: Boolean) = ElementPreview { +internal fun ComposerAlertMoleculePreview( + @PreviewParameter(ComposerAlertMoleculeParamsProvider::class) params: ComposerAlertMoleculeParams, +) = ElementPreview { ComposerAlertMolecule( - avatar = anAvatarData(size = AvatarSize.ComposerAlert), + avatar = params.avatar, content = "Alice’s verified identity has changed. Learn more".toAnnotatedString(), - isCritical = isCritical, + level = params.level, + showIcon = params.showIcon, onSubmitClick = {}, ) } From 6db952c7df82b0383359b68b591a3a476e030d8a Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 6 Nov 2025 13:47:52 +0100 Subject: [PATCH 058/216] quality: fix more tests --- .../impl/roles/ChangeRolesPresenter.kt | 1 - .../impl/roles/ChangeRolesPresenterTest.kt | 28 +++++-------------- .../test/FakeRolesAndPermissionsEntryPoint.kt | 19 +++++++++++++ .../impl/DefaultRoomDetailsEntryPointTest.kt | 2 ++ 4 files changed, 28 insertions(+), 22 deletions(-) create mode 100644 features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeRolesAndPermissionsEntryPoint.kt diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt index 87a659f7ce..719fdee2c2 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt @@ -104,7 +104,6 @@ class ChangeRolesPresenter( val roomInfo by room.roomInfoFlow.collectAsState() fun canChangeMemberRole(userId: UserId): Boolean { - // This is used to group the val currentUserRole = roomInfo.roleOf(room.sessionId) val otherUserRole = roomInfo.roleOf(userId) return currentUserRole.powerLevel > otherUserRole.powerLevel diff --git a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenterTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenterTest.kt index 6bcad2c4cc..4b3ed237cd 100644 --- a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenterTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenterTest.kt @@ -351,21 +351,15 @@ class ChangeRolesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) + skipItems(2) val initialState = awaitItem() assertThat(initialState.selectedUsers).hasSize(1) - initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2))) awaitItem().eventSink(ChangeRolesEvent.Save) val confirmingState = awaitItem() assertThat(confirmingState.savingState).isEqualTo(AsyncAction.ConfirmingNoParams) - confirmingState.eventSink(ChangeRolesEvent.Save) - - val loadingState = awaitItem() - assertThat(loadingState.savingState).isInstanceOf(AsyncAction.Loading::class.java) - skipItems(1) - + assertThat(awaitItem().savingState).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().savingState).isEqualTo(AsyncAction.Success(true)) } } @@ -413,18 +407,12 @@ class ChangeRolesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) + skipItems(2) val initialState = awaitItem() assertThat(initialState.selectedUsers).hasSize(1) - initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2))) - awaitItem().eventSink(ChangeRolesEvent.Save) - - val loadingState = awaitItem() - assertThat(loadingState.savingState).isInstanceOf(AsyncAction.Loading::class.java) - skipItems(1) - + assertThat(awaitItem().savingState).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().savingState).isEqualTo(AsyncAction.Success(true)) assertThat(analyticsService.capturedEvents.last()).isEqualTo(RoomModeration(RoomModeration.Action.ChangeMemberRole, RoomModeration.Role.Moderator)) } @@ -491,7 +479,7 @@ class ChangeRolesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) + skipItems(2) val initialState = awaitItem() assertThat(initialState.selectedUsers).hasSize(1) @@ -501,8 +489,6 @@ class ChangeRolesPresenterTest { val loadingState = awaitItem() assertThat(loadingState.savingState).isInstanceOf(AsyncAction.Loading::class.java) - skipItems(1) - assertThat(awaitItem().savingState).isEqualTo(AsyncAction.Success(true)) assertThat(analyticsService.capturedEvents.last()).isEqualTo(RoomModeration(RoomModeration.Action.ChangeMemberRole, RoomModeration.Role.User)) } @@ -520,7 +506,7 @@ class ChangeRolesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) + skipItems(2) val initialState = awaitItem() assertThat(initialState.selectedUsers).hasSize(1) @@ -529,7 +515,6 @@ class ChangeRolesPresenterTest { awaitItem().eventSink(ChangeRolesEvent.Save) val loadingState = awaitItem() assertThat(loadingState.savingState).isInstanceOf(AsyncAction.Loading::class.java) - skipItems(1) val failedState = awaitItem() assertThat(failedState.savingState).isInstanceOf(AsyncAction.Failure::class.java) @@ -567,5 +552,6 @@ internal fun TestScope.createChangeRolesPresenter( room = room, dispatchers = dispatchers, analyticsService = analyticsService, + roomCoroutineScope = this, ) } diff --git a/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeRolesAndPermissionsEntryPoint.kt b/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeRolesAndPermissionsEntryPoint.kt new file mode 100644 index 0000000000..489f1ad3b0 --- /dev/null +++ b/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeRolesAndPermissionsEntryPoint.kt @@ -0,0 +1,19 @@ +/* + * 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. + */ + +package io.element.android.features.changeroommemberroles.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.rolesandpermissions.api.RolesAndPermissionsEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeRolesAndPermissionsEntryPoint : RolesAndPermissionsEntryPoint { + override fun createNode(parentNode: Node, buildContext: BuildContext): Node { + lambdaError() + } +} diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt index f9bc40b711..52de9aa8f1 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt @@ -13,6 +13,7 @@ import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat import io.element.android.features.call.test.FakeElementCallEntryPoint import io.element.android.features.changeroommemberroles.test.FakeChangeRoomMemberRolesEntryPoint +import io.element.android.features.changeroommemberroles.test.FakeRolesAndPermissionsEntryPoint import io.element.android.features.knockrequests.test.FakeKnockRequestsListEntryPoint import io.element.android.features.messages.test.FakeMessagesEntryPoint import io.element.android.features.poll.test.history.FakePollHistoryEntryPoint @@ -58,6 +59,7 @@ class DefaultRoomDetailsEntryPointTest { outgoingVerificationEntryPoint = FakeOutgoingVerificationEntryPoint(), reportRoomEntryPoint = FakeReportRoomEntryPoint(), changeRoomMemberRolesEntryPoint = FakeChangeRoomMemberRolesEntryPoint(), + rolesAndPermissionsEntryPoint = FakeRolesAndPermissionsEntryPoint(), ) } val callback = object : RoomDetailsEntryPoint.Callback { From 4cc5cf008407e4f36370f2a713632d63c8fcc906 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 6 Nov 2025 14:23:41 +0100 Subject: [PATCH 059/216] Fix test --- .../changeserver/ChangeServerPresenterTest.kt | 14 ++- .../ChooseAccountProviderPresenterTest.kt | 7 +- .../ConfirmAccountProviderPresenterTest.kt | 91 +++++++++++++------ .../LoginPasswordPresenterTest.kt | 28 ++++-- .../onboarding/OnBoardingPresenterTest.kt | 7 +- .../auth/FakeMatrixAuthenticationService.kt | 23 +---- 6 files changed, 111 insertions(+), 59 deletions(-) diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt index 3b88ab03c0..47f9fee728 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt @@ -18,6 +18,7 @@ import io.element.android.features.wellknown.test.FakeWellknownRetriever import io.element.android.features.wellknown.test.anElementWellKnown import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.uri.ensureProtocol +import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_HOMESERVER import io.element.android.libraries.matrix.test.A_HOMESERVER_URL import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService @@ -46,7 +47,11 @@ class ChangeServerPresenterTest { @Test fun `present - change server ok`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.success(A_HOMESERVER) + }, + ) createPresenter( authenticationService = authenticationService, enterpriseService = FakeEnterpriseService( @@ -55,7 +60,6 @@ class ChangeServerPresenterTest { ).test { val initialState = awaitItem() assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized) - authenticationService.givenHomeserver(A_HOMESERVER) initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) val loadingState = awaitItem() assertThat(loadingState.changeServerAction).isInstanceOf(AsyncData.Loading::class.java) @@ -66,10 +70,16 @@ class ChangeServerPresenterTest { @Test fun `present - change server error`() = runTest { + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.failure(AN_EXCEPTION) + }, + ) createPresenter( enterpriseService = FakeEnterpriseService( isAllowedToConnectToHomeserverResult = { true }, ), + authenticationService = authenticationService, ).test { val initialState = awaitItem() assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized) diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenterTest.kt index 2e13b0e555..96dc5b87ff 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenterTest.kt @@ -95,7 +95,11 @@ class ChooseAccountProviderPresenterTest { @Test fun `present - select account provider and continue - error then clear error`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.failure(AN_EXCEPTION) + }, + ) val presenter = createPresenter( enterpriseService = FakeEnterpriseService( defaultHomeserverListResult = { listOf(ACCOUNT_PROVIDER_FROM_CONFIG_1, ACCOUNT_PROVIDER_FROM_CONFIG_2) }, @@ -111,7 +115,6 @@ class ChooseAccountProviderPresenterTest { } awaitItem().also { assertThat(it.selectedAccountProvider).isEqualTo(accountProvider1) - authenticationService.givenChangeServerError(AN_EXCEPTION) it.eventSink(ChooseAccountProviderEvents.Continue) skipItems(1) // Loading diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt index 3978d3be6e..82c2caff61 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt @@ -53,11 +53,14 @@ class ConfirmAccountProviderPresenterTest { @Test fun `present - continue password login`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.success(A_HOMESERVER) + }, + ) val presenter = createConfirmAccountProviderPresenter( matrixAuthenticationService = authenticationService, ) - authenticationService.givenHomeserver(A_HOMESERVER) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -75,11 +78,14 @@ class ConfirmAccountProviderPresenterTest { @Test fun `present - continue oidc`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.success(A_HOMESERVER_OIDC) + }, + ) val presenter = createConfirmAccountProviderPresenter( matrixAuthenticationService = authenticationService, ) - authenticationService.givenHomeserver(A_HOMESERVER_OIDC) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -97,13 +103,16 @@ class ConfirmAccountProviderPresenterTest { @Test fun `present - oidc - cancel with failure`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.success(A_HOMESERVER_OIDC) + }, + ) val defaultOidcActionFlow = FakeOidcActionFlow() val presenter = createConfirmAccountProviderPresenter( matrixAuthenticationService = authenticationService, defaultOidcActionFlow = defaultOidcActionFlow, ) - authenticationService.givenHomeserver(A_HOMESERVER_OIDC) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -125,13 +134,16 @@ class ConfirmAccountProviderPresenterTest { @Test fun `present - oidc - cancel with success`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.success(A_HOMESERVER_OIDC) + }, + ) val defaultOidcActionFlow = FakeOidcActionFlow() val presenter = createConfirmAccountProviderPresenter( matrixAuthenticationService = authenticationService, defaultOidcActionFlow = defaultOidcActionFlow, ) - authenticationService.givenHomeserver(A_HOMESERVER_OIDC) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -152,13 +164,16 @@ class ConfirmAccountProviderPresenterTest { @Test fun `present - oidc - cancel to unblock`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.success(A_HOMESERVER_OIDC) + }, + ) val defaultOidcActionFlow = FakeOidcActionFlow() val presenter = createConfirmAccountProviderPresenter( matrixAuthenticationService = authenticationService, defaultOidcActionFlow = defaultOidcActionFlow, ) - authenticationService.givenHomeserver(A_HOMESERVER_OIDC) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -175,13 +190,16 @@ class ConfirmAccountProviderPresenterTest { @Test fun `present - oidc - success with failure`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.success(A_HOMESERVER_OIDC) + }, + ) val defaultOidcActionFlow = FakeOidcActionFlow() val presenter = createConfirmAccountProviderPresenter( matrixAuthenticationService = authenticationService, defaultOidcActionFlow = defaultOidcActionFlow, ) - authenticationService.givenHomeserver(A_HOMESERVER_OIDC) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -205,13 +223,16 @@ class ConfirmAccountProviderPresenterTest { @Test fun `present - oidc - success with success`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.success(A_HOMESERVER_OIDC) + }, + ) val defaultOidcActionFlow = FakeOidcActionFlow() val presenter = createConfirmAccountProviderPresenter( matrixAuthenticationService = authenticationService, defaultOidcActionFlow = defaultOidcActionFlow, ) - authenticationService.givenHomeserver(A_HOMESERVER_OIDC) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -232,7 +253,11 @@ class ConfirmAccountProviderPresenterTest { @Test fun `present - submit fails`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.failure(AN_EXCEPTION) + }, + ) val presenter = createConfirmAccountProviderPresenter( matrixAuthenticationService = authenticationService, ) @@ -240,7 +265,6 @@ class ConfirmAccountProviderPresenterTest { presenter.present() }.test { val initialState = awaitItem() - authenticationService.givenChangeServerError(RuntimeException()) initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) skipItems(1) // Loading val failureState = awaitItem() @@ -251,7 +275,11 @@ class ConfirmAccountProviderPresenterTest { @Test fun `present - clear error`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.failure(AN_EXCEPTION) + }, + ) val presenter = createConfirmAccountProviderPresenter( matrixAuthenticationService = authenticationService, ) @@ -261,7 +289,6 @@ class ConfirmAccountProviderPresenterTest { val initialState = awaitItem() // Submit will return an error - authenticationService.givenChangeServerError(AN_EXCEPTION) initialState.eventSink(ConfirmAccountProviderEvents.Continue) skipItems(1) // Loading @@ -279,8 +306,11 @@ class ConfirmAccountProviderPresenterTest { @Test fun `present - confirm account creation without oidc and without url generates an error`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() - authenticationService.givenHomeserver(A_HOMESERVER) + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.success(A_HOMESERVER) + }, + ) val presenter = createConfirmAccountProviderPresenter( params = ConfirmAccountProviderPresenter.Params(isAccountCreation = true), matrixAuthenticationService = authenticationService, @@ -306,8 +336,11 @@ class ConfirmAccountProviderPresenterTest { @Test fun `present - confirm account creation with oidc is successful`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() - authenticationService.givenHomeserver(A_HOMESERVER_OIDC) + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.success(A_HOMESERVER_OIDC) + }, + ) val presenter = createConfirmAccountProviderPresenter( params = ConfirmAccountProviderPresenter.Params(isAccountCreation = true), matrixAuthenticationService = authenticationService, @@ -327,8 +360,11 @@ class ConfirmAccountProviderPresenterTest { @Test fun `present - confirm account creation with oidc and url continues with oidc`() = runTest { val aUrl = "aUrl" - val authenticationService = FakeMatrixAuthenticationService() - authenticationService.givenHomeserver(A_HOMESERVER_OIDC) + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.success(A_HOMESERVER_OIDC) + }, + ) val presenter = createConfirmAccountProviderPresenter( params = ConfirmAccountProviderPresenter.Params(isAccountCreation = true), matrixAuthenticationService = authenticationService, @@ -349,8 +385,11 @@ class ConfirmAccountProviderPresenterTest { @Test fun `present - confirm account creation without oidc and with url continuing with url`() = runTest { val aUrl = "aUrl" - val authenticationService = FakeMatrixAuthenticationService() - authenticationService.givenHomeserver(A_HOMESERVER) + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.success(A_HOMESERVER) + }, + ) val presenter = createConfirmAccountProviderPresenter( params = ConfirmAccountProviderPresenter.Params(isAccountCreation = true), matrixAuthenticationService = authenticationService, diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt index af158ec0da..fd048d1db0 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt @@ -42,8 +42,11 @@ class LoginPasswordPresenterTest { @Test fun `present - enter login and password`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() - authenticationService.givenHomeserver(A_HOMESERVER) + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.success(A_HOMESERVER) + }, + ) createLoginPasswordPresenter( authenticationService = authenticationService, ).test { @@ -61,8 +64,11 @@ class LoginPasswordPresenterTest { @Test fun `present - submit`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() - authenticationService.givenHomeserver(A_HOMESERVER) + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.success(A_HOMESERVER) + }, + ) createLoginPasswordPresenter( authenticationService = authenticationService, ).test { @@ -81,8 +87,11 @@ class LoginPasswordPresenterTest { @Test fun `present - submit with error`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() - authenticationService.givenHomeserver(A_HOMESERVER) + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.success(A_HOMESERVER) + }, + ) createLoginPasswordPresenter( authenticationService = authenticationService, ).test { @@ -102,8 +111,11 @@ class LoginPasswordPresenterTest { @Test fun `present - clear error`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() - authenticationService.givenHomeserver(A_HOMESERVER) + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.success(A_HOMESERVER) + }, + ) createLoginPasswordPresenter( authenticationService = authenticationService, ).test { diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt index 16f6c649fa..1cbbda450b 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt @@ -214,7 +214,11 @@ class OnBoardingPresenterTest { @Test fun `present - default account provider - login and clear error`() = runTest { - val authenticationService = FakeMatrixAuthenticationService() + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.failure(AN_EXCEPTION) + }, + ) val presenter = createPresenter( params = OnBoardingNode.Params( accountProvider = A_HOMESERVER_URL, @@ -231,7 +235,6 @@ class OnBoardingPresenterTest { skipItems(3) awaitItem().also { assertThat(it.defaultAccountProvider).isEqualTo(A_HOMESERVER_URL) - authenticationService.givenChangeServerError(AN_EXCEPTION) it.eventSink(OnBoardingEvents.OnSignIn(A_HOMESERVER_URL)) skipItems(1) // Loading diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeMatrixAuthenticationService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeMatrixAuthenticationService.kt index f1554df1d0..7901eafeea 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeMatrixAuthenticationService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeMatrixAuthenticationService.kt @@ -22,8 +22,6 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.simulateLongTask -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow val A_OIDC_DATA = OidcDetails(url = "a-url") @@ -31,13 +29,12 @@ class FakeMatrixAuthenticationService( var matrixClientResult: ((SessionId) -> Result)? = null, var loginWithQrCodeResult: (qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit) -> Result = lambdaRecorder Unit, Result> { _, _ -> Result.success(A_SESSION_ID) }, - private val importCreatedSessionLambda: (ExternalSession) -> Result = { lambdaError() } + private val importCreatedSessionLambda: (ExternalSession) -> Result = { lambdaError() }, + private val setHomeserverResult: (String) -> Result = { lambdaError() }, ) : MatrixAuthenticationService { - private val homeserver = MutableStateFlow(null) private var oidcError: Throwable? = null private var oidcCancelError: Throwable? = null private var loginError: Throwable? = null - private var changeServerError: Throwable? = null private var matrixClient: MatrixClient? = null private var onAuthenticationListener: ((MatrixClient) -> Unit)? = null @@ -53,16 +50,8 @@ class FakeMatrixAuthenticationService( } } - override fun getHomeserverDetails(): StateFlow { - return homeserver - } - - fun givenHomeserver(homeserver: MatrixHomeServerDetails) { - this.homeserver.value = homeserver - } - - override suspend fun setHomeserver(homeserver: String): Result = simulateLongTask { - changeServerError?.let { Result.failure(it) } ?: Result.success(Unit) + override suspend fun setHomeserver(homeserver: String): Result = simulateLongTask { + setHomeserverResult(homeserver) } override suspend fun login(username: String, password: String): Result = simulateLongTask { @@ -115,10 +104,6 @@ class FakeMatrixAuthenticationService( loginError = throwable } - fun givenChangeServerError(throwable: Throwable?) { - changeServerError = throwable - } - fun givenMatrixClient(matrixClient: MatrixClient) { this.matrixClient = matrixClient } From 6617db0ce611d0eb44b020fd79eeb87407c7454e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 6 Nov 2025 14:32:00 +0100 Subject: [PATCH 060/216] MatrixHomeServerDetails does not need to be Parcelable --- .../libraries/matrix/api/auth/MatrixHomeServerDetails.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt index c2f0741571..ce3af70121 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt @@ -7,12 +7,8 @@ package io.element.android.libraries.matrix.api.auth -import android.os.Parcelable -import kotlinx.parcelize.Parcelize - -@Parcelize data class MatrixHomeServerDetails( val url: String, val supportsPasswordLogin: Boolean, val supportsOidcLogin: Boolean, -) : Parcelable +) From 8fa2c6c85fb79c7d331cfc11e90f4c4d7aeb0538 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 6 Nov 2025 14:39:59 +0100 Subject: [PATCH 061/216] Remove A_HOMESERVER and A_HOMESERVER_OIDC from TestData and replace by local `fun aMatrixHomeServerDetails()`. --- .../login/impl/MatrixHomeServerDetails.kt | 21 ++++++++++++++++ .../changeserver/ChangeServerPresenterTest.kt | 4 +-- .../ConfirmAccountProviderPresenterTest.kt | 25 +++++++++---------- .../LoginPasswordPresenterTest.kt | 10 ++++---- .../android/libraries/matrix/test/TestData.kt | 3 --- 5 files changed, 40 insertions(+), 23 deletions(-) create mode 100644 features/login/impl/src/test/kotlin/io/element/android/features/login/impl/MatrixHomeServerDetails.kt diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/MatrixHomeServerDetails.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/MatrixHomeServerDetails.kt new file mode 100644 index 0000000000..1bf32d20e2 --- /dev/null +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/MatrixHomeServerDetails.kt @@ -0,0 +1,21 @@ +/* + * 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. + */ + +package io.element.android.features.login.impl + +import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails +import io.element.android.libraries.matrix.test.A_HOMESERVER_URL + +fun aMatrixHomeServerDetails( + url: String = A_HOMESERVER_URL, + supportsPasswordLogin: Boolean = false, + supportsOidcLogin: Boolean = false, +) = MatrixHomeServerDetails( + url = url, + supportsPasswordLogin = supportsPasswordLogin, + supportsOidcLogin = supportsOidcLogin, +) diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt index 47f9fee728..d723796265 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt @@ -10,6 +10,7 @@ package io.element.android.features.login.impl.changeserver import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService +import io.element.android.features.login.impl.aMatrixHomeServerDetails import io.element.android.features.login.impl.accesscontrol.DefaultAccountProviderAccessControl import io.element.android.features.login.impl.accountprovider.AccountProvider import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource @@ -19,7 +20,6 @@ import io.element.android.features.wellknown.test.anElementWellKnown import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.uri.ensureProtocol import io.element.android.libraries.matrix.test.AN_EXCEPTION -import io.element.android.libraries.matrix.test.A_HOMESERVER import io.element.android.libraries.matrix.test.A_HOMESERVER_URL import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.wellknown.api.ElementWellKnown @@ -49,7 +49,7 @@ class ChangeServerPresenterTest { fun `present - change server ok`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(A_HOMESERVER) + Result.success(aMatrixHomeServerDetails()) }, ) createPresenter( diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt index 82c2caff61..792abdf1a4 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt @@ -13,6 +13,7 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.appconfig.AuthenticationConfig import io.element.android.features.enterprise.test.FakeEnterpriseService +import io.element.android.features.login.impl.aMatrixHomeServerDetails import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.features.login.impl.login.LoginMode import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported @@ -22,8 +23,6 @@ import io.element.android.features.login.impl.web.WebClientUrlForAuthenticationR import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.test.AN_EXCEPTION -import io.element.android.libraries.matrix.test.A_HOMESERVER -import io.element.android.libraries.matrix.test.A_HOMESERVER_OIDC import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.oidc.api.OidcAction import io.element.android.libraries.oidc.api.OidcActionFlow @@ -55,7 +54,7 @@ class ConfirmAccountProviderPresenterTest { fun `present - continue password login`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(A_HOMESERVER) + Result.success(aMatrixHomeServerDetails(supportsPasswordLogin = true)) }, ) val presenter = createConfirmAccountProviderPresenter( @@ -80,7 +79,7 @@ class ConfirmAccountProviderPresenterTest { fun `present - continue oidc`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(A_HOMESERVER_OIDC) + Result.success(aMatrixHomeServerDetails(supportsOidcLogin = true)) }, ) val presenter = createConfirmAccountProviderPresenter( @@ -105,7 +104,7 @@ class ConfirmAccountProviderPresenterTest { fun `present - oidc - cancel with failure`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(A_HOMESERVER_OIDC) + Result.success(aMatrixHomeServerDetails(supportsOidcLogin = true)) }, ) val defaultOidcActionFlow = FakeOidcActionFlow() @@ -136,7 +135,7 @@ class ConfirmAccountProviderPresenterTest { fun `present - oidc - cancel with success`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(A_HOMESERVER_OIDC) + Result.success(aMatrixHomeServerDetails(supportsOidcLogin = true)) }, ) val defaultOidcActionFlow = FakeOidcActionFlow() @@ -166,7 +165,7 @@ class ConfirmAccountProviderPresenterTest { fun `present - oidc - cancel to unblock`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(A_HOMESERVER_OIDC) + Result.success(aMatrixHomeServerDetails(supportsOidcLogin = true)) }, ) val defaultOidcActionFlow = FakeOidcActionFlow() @@ -192,7 +191,7 @@ class ConfirmAccountProviderPresenterTest { fun `present - oidc - success with failure`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(A_HOMESERVER_OIDC) + Result.success(aMatrixHomeServerDetails(supportsOidcLogin = true)) }, ) val defaultOidcActionFlow = FakeOidcActionFlow() @@ -225,7 +224,7 @@ class ConfirmAccountProviderPresenterTest { fun `present - oidc - success with success`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(A_HOMESERVER_OIDC) + Result.success(aMatrixHomeServerDetails(supportsOidcLogin = true)) }, ) val defaultOidcActionFlow = FakeOidcActionFlow() @@ -308,7 +307,7 @@ class ConfirmAccountProviderPresenterTest { fun `present - confirm account creation without oidc and without url generates an error`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(A_HOMESERVER) + Result.success(aMatrixHomeServerDetails()) }, ) val presenter = createConfirmAccountProviderPresenter( @@ -338,7 +337,7 @@ class ConfirmAccountProviderPresenterTest { fun `present - confirm account creation with oidc is successful`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(A_HOMESERVER_OIDC) + Result.success(aMatrixHomeServerDetails(supportsOidcLogin = true)) }, ) val presenter = createConfirmAccountProviderPresenter( @@ -362,7 +361,7 @@ class ConfirmAccountProviderPresenterTest { val aUrl = "aUrl" val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(A_HOMESERVER_OIDC) + Result.success(aMatrixHomeServerDetails(supportsOidcLogin = true)) }, ) val presenter = createConfirmAccountProviderPresenter( @@ -387,7 +386,7 @@ class ConfirmAccountProviderPresenterTest { val aUrl = "aUrl" val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(A_HOMESERVER) + Result.success(aMatrixHomeServerDetails()) }, ) val presenter = createConfirmAccountProviderPresenter( diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt index fd048d1db0..bfdc0a6785 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt @@ -10,11 +10,11 @@ package io.element.android.features.login.impl.screens.loginpassword import com.google.common.truth.Truth.assertThat import io.element.android.appconfig.AuthenticationConfig import io.element.android.features.enterprise.test.FakeEnterpriseService +import io.element.android.features.login.impl.aMatrixHomeServerDetails import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.test.AN_EXCEPTION -import io.element.android.libraries.matrix.test.A_HOMESERVER import io.element.android.libraries.matrix.test.A_PASSWORD import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_USER_NAME @@ -44,7 +44,7 @@ class LoginPasswordPresenterTest { fun `present - enter login and password`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(A_HOMESERVER) + Result.success(aMatrixHomeServerDetails()) }, ) createLoginPasswordPresenter( @@ -66,7 +66,7 @@ class LoginPasswordPresenterTest { fun `present - submit`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(A_HOMESERVER) + Result.success(aMatrixHomeServerDetails()) }, ) createLoginPasswordPresenter( @@ -89,7 +89,7 @@ class LoginPasswordPresenterTest { fun `present - submit with error`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(A_HOMESERVER) + Result.success(aMatrixHomeServerDetails()) }, ) createLoginPasswordPresenter( @@ -113,7 +113,7 @@ class LoginPasswordPresenterTest { fun `present - clear error`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(A_HOMESERVER) + Result.success(aMatrixHomeServerDetails()) }, ) createLoginPasswordPresenter( diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt index 8c22c90cd7..dc1817cf12 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt @@ -8,7 +8,6 @@ package io.element.android.libraries.matrix.test import androidx.annotation.ColorInt -import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomAlias @@ -79,8 +78,6 @@ const val AN_ACCOUNT_PROVIDER = "matrix.org" const val AN_ACCOUNT_PROVIDER_2 = "element.io" const val AN_ACCOUNT_PROVIDER_3 = "other.io" -val A_HOMESERVER = MatrixHomeServerDetails(A_HOMESERVER_URL, supportsPasswordLogin = true, supportsOidcLogin = false) -val A_HOMESERVER_OIDC = MatrixHomeServerDetails(A_HOMESERVER_URL, supportsPasswordLogin = false, supportsOidcLogin = true) val A_ROOM_NOTIFICATION_MODE = RoomNotificationMode.MUTE const val AN_AVATAR_URL = "mxc://data" From e3ed75d19ed036e0cb18e1edd4259610b731ed4d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 6 Nov 2025 15:07:44 +0100 Subject: [PATCH 062/216] Ensure user cannot select unsupported homeserver. In this case show the appropriate error (parity with iOS) --- .../changeserver/ChangeServerPresenter.kt | 9 +++++- .../changeserver/ChangeServerStateProvider.kt | 8 +++++ .../login/impl/error/ChangeServerError.kt | 1 + .../changeserver/ChangeServerPresenterTest.kt | 29 ++++++++++++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt index d3db7496c5..71d4e97c3b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt @@ -13,6 +13,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import dev.zacsweers.metro.Inject +import io.element.android.features.login.impl.R import io.element.android.features.login.impl.accesscontrol.DefaultAccountProviderAccessControl import io.element.android.features.login.impl.accountprovider.AccountProvider import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource @@ -60,7 +61,13 @@ class ChangeServerPresenter( title = data.title, accountProviderUrl = data.url, ) - authenticationService.setHomeserver(data.url).getOrThrow() + val details = authenticationService.setHomeserver(data.url).getOrThrow() + if (details.supportsOidcLogin.not() && details.supportsPasswordLogin.not()) { + // Unsupported homeserver + throw ChangeServerError.Error( + messageId = R.string.screen_login_error_unsupported_authentication, + ) + } // Homeserver is valid, remember user choice accountProviderDataSource.userSelection(data) }.runCatchingUpdatingState(changeServerAction, errorTransform = ChangeServerError::from) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStateProvider.kt index a97ff2dda1..8a7d015750 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStateProvider.kt @@ -8,6 +8,7 @@ package io.element.android.features.login.impl.changeserver import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.login.impl.R import io.element.android.features.login.impl.error.ChangeServerError import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.ui.strings.CommonStrings @@ -34,6 +35,13 @@ open class ChangeServerStateProvider : PreviewParameterProvider error is AuthenticationException.SlidingSyncVersion -> SlidingSyncAlert is AuthenticationException.Oidc -> Error(messageStr = error.message) is AccountProviderAccessException.NeedElementProException -> NeedElementPro( diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt index d723796265..ce026b447c 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt @@ -10,6 +10,7 @@ package io.element.android.features.login.impl.changeserver import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService +import io.element.android.features.login.impl.R import io.element.android.features.login.impl.aMatrixHomeServerDetails import io.element.android.features.login.impl.accesscontrol.DefaultAccountProviderAccessControl import io.element.android.features.login.impl.accountprovider.AccountProvider @@ -49,7 +50,7 @@ class ChangeServerPresenterTest { fun `present - change server ok`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(aMatrixHomeServerDetails()) + Result.success(aMatrixHomeServerDetails(supportsOidcLogin = true)) }, ) createPresenter( @@ -95,6 +96,32 @@ class ChangeServerPresenterTest { } } + @Test + fun `present - change server unsupported server`() = runTest { + val authenticationService = FakeMatrixAuthenticationService( + setHomeserverResult = { + Result.success(aMatrixHomeServerDetails()) + }, + ) + createPresenter( + enterpriseService = FakeEnterpriseService( + isAllowedToConnectToHomeserverResult = { true }, + ), + authenticationService = authenticationService, + ).test { + val initialState = awaitItem() + assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized) + initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(AccountProvider(url = A_HOMESERVER_URL))) + val loadingState = awaitItem() + assertThat(loadingState.changeServerAction).isInstanceOf(AsyncData.Loading::class.java) + val failureState = awaitItem() + assertThat(failureState.changeServerAction).isInstanceOf(AsyncData.Failure::class.java) + assertThat(failureState.changeServerAction.errorOrNull()).isEqualTo( + ChangeServerError.Error(R.string.screen_login_error_unsupported_authentication) + ) + } + } + @Test fun `present - change server not allowed error`() = runTest { val isAllowedToConnectToHomeserverResult = lambdaRecorder { false } From a9958505d375d8b6d7e20a5f4b40b72bbe280d44 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 6 Nov 2025 15:21:50 +0100 Subject: [PATCH 063/216] Always let the user try what they have entered, to get an explicit error if they continue --- .../features/login/impl/resolver/HomeserverResolver.kt | 7 ++++--- .../SearchAccountProviderPresenterTest.kt | 8 +++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverResolver.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverResolver.kt index c43839517c..4cf1d416e8 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverResolver.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverResolver.kt @@ -52,9 +52,10 @@ class HomeserverResolver( } } } - // If list is empty, and the user has entered an URL, do not block the user. - if (currentList.isEmpty() && trimmedUserInput.isValidUrl()) { - emit(listOf(HomeserverData(homeserverUrl = trimmedUserInput))) + // If list is empty, and candidateBase is a valid an URL, do not block the user. + // A unsupported homeserver / homeserver not found error will be displayed if the user continues + if (currentList.isEmpty() && candidateBase.isValidUrl()) { + emit(listOf(HomeserverData(homeserverUrl = candidateBase))) } } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt index 67453119c7..f4c193ced2 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt @@ -85,7 +85,13 @@ class SearchAccountProviderPresenterTest { assertThat(withInputState.userInput).isEqualTo("test") assertThat(initialState.userInputResult).isEqualTo(AsyncData.Uninitialized) assertThat(awaitItem().userInputResult).isInstanceOf(AsyncData.Loading::class.java) - assertThat(awaitItem().userInputResult).isEqualTo(AsyncData.Uninitialized) + assertThat(awaitItem().userInputResult).isEqualTo( + AsyncData.Success( + listOf( + aHomeserverData(homeserverUrl = "https://test"), + ) + ) + ) } } From fd0ef1ae7a12fd81c2feac61daf85cfc6f23eae1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 6 Nov 2025 15:22:19 +0100 Subject: [PATCH 064/216] Small cleanup --- .../searchaccountprovider/SearchAccountProviderStateProvider.kt | 2 +- .../notifications/DefaultActiveNotificationsProviderTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt index 3dd7a3d8c5..97cc861911 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt @@ -43,5 +43,5 @@ fun aHomeserverDataList(): List { fun aHomeserverData( homeserverUrl: String = AuthenticationConfig.MATRIX_ORG_URL, ): HomeserverData { - return HomeserverData(homeserverUrl = homeserverUrl,) + return HomeserverData(homeserverUrl = homeserverUrl) } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultActiveNotificationsProviderTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultActiveNotificationsProviderTest.kt index a0ce8b2edb..b06842ccdb 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultActiveNotificationsProviderTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultActiveNotificationsProviderTest.kt @@ -44,7 +44,7 @@ class DefaultActiveNotificationsProviderTest { @Test fun `getMembershipNotificationsForSession returns only membership notifications for that session id`() { val activeNotifications = listOf( - aStatusBarNotification(id = notificationIdProvider.getRoomMessagesNotificationId(A_SESSION_ID), groupId = A_SESSION_ID.value,), + aStatusBarNotification(id = notificationIdProvider.getRoomMessagesNotificationId(A_SESSION_ID), groupId = A_SESSION_ID.value), aStatusBarNotification(id = notificationIdProvider.getSummaryNotificationId(A_SESSION_ID_2), groupId = A_SESSION_ID_2.value), aStatusBarNotification( id = notificationIdProvider.getRoomInvitationNotificationId(A_SESSION_ID_2), From 5b472fdc313ee05df8d06b32d03d5756efee16a8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 6 Nov 2025 15:25:39 +0100 Subject: [PATCH 065/216] Use presenter test extension --- .../ChangeAccountProviderPresenterTest.kt | 16 ++--- .../ConfirmAccountProviderPresenterTest.kt | 60 +++++-------------- .../CreateAccountPresenterTest.kt | 28 +++------ .../qrcode/intro/QrCodeIntroPresenterTest.kt | 16 ++--- .../qrcode/scan/QrCodeScanPresenterTest.kt | 15 +---- .../SearchAccountProviderPresenterTest.kt | 24 ++------ 6 files changed, 39 insertions(+), 120 deletions(-) diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt index f2e933390b..99fa65876d 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt @@ -7,9 +7,6 @@ package io.element.android.features.login.impl.screens.changeaccountprovider -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow -import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService @@ -18,6 +15,7 @@ import io.element.android.features.login.impl.changeserver.aChangeServerState import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_2 import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.test import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -34,9 +32,7 @@ class ChangeAccountProviderPresenterTest { defaultHomeserverListResult = { emptyList() } ), ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() assertThat(initialState.accountProviders).isEqualTo( listOf( @@ -63,9 +59,7 @@ class ChangeAccountProviderPresenterTest { } ), ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() assertThat(initialState.accountProviders).isEqualTo( listOf( @@ -99,9 +93,7 @@ class ChangeAccountProviderPresenterTest { } ), ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() assertThat(initialState.accountProviders).isEqualTo( listOf( diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt index 792abdf1a4..182b7d6e27 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt @@ -7,9 +7,6 @@ package io.element.android.features.login.impl.screens.confirmaccountprovider -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow -import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.appconfig.AuthenticationConfig import io.element.android.features.enterprise.test.FakeEnterpriseService @@ -28,6 +25,7 @@ import io.element.android.libraries.oidc.api.OidcAction import io.element.android.libraries.oidc.api.OidcActionFlow import io.element.android.libraries.oidc.test.customtab.FakeOidcActionFlow import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.test import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -39,9 +37,7 @@ class ConfirmAccountProviderPresenterTest { @Test fun `present - initial test`() = runTest { val presenter = createConfirmAccountProviderPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() assertThat(initialState.isAccountCreation).isFalse() assertThat(initialState.submitEnabled).isTrue() @@ -60,9 +56,7 @@ class ConfirmAccountProviderPresenterTest { val presenter = createConfirmAccountProviderPresenter( matrixAuthenticationService = authenticationService, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) val loadingState = awaitItem() @@ -85,9 +79,7 @@ class ConfirmAccountProviderPresenterTest { val presenter = createConfirmAccountProviderPresenter( matrixAuthenticationService = authenticationService, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) val loadingState = awaitItem() @@ -112,9 +104,7 @@ class ConfirmAccountProviderPresenterTest { matrixAuthenticationService = authenticationService, defaultOidcActionFlow = defaultOidcActionFlow, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) val loadingState = awaitItem() @@ -143,9 +133,7 @@ class ConfirmAccountProviderPresenterTest { matrixAuthenticationService = authenticationService, defaultOidcActionFlow = defaultOidcActionFlow, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) val loadingState = awaitItem() @@ -173,9 +161,7 @@ class ConfirmAccountProviderPresenterTest { matrixAuthenticationService = authenticationService, defaultOidcActionFlow = defaultOidcActionFlow, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) val loadingState = awaitItem() @@ -199,9 +185,7 @@ class ConfirmAccountProviderPresenterTest { matrixAuthenticationService = authenticationService, defaultOidcActionFlow = defaultOidcActionFlow, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) val loadingState = awaitItem() @@ -232,9 +216,7 @@ class ConfirmAccountProviderPresenterTest { matrixAuthenticationService = authenticationService, defaultOidcActionFlow = defaultOidcActionFlow, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) val loadingState = awaitItem() @@ -260,9 +242,7 @@ class ConfirmAccountProviderPresenterTest { val presenter = createConfirmAccountProviderPresenter( matrixAuthenticationService = authenticationService, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(ConfirmAccountProviderEvents.Continue) skipItems(1) // Loading @@ -282,9 +262,7 @@ class ConfirmAccountProviderPresenterTest { val presenter = createConfirmAccountProviderPresenter( matrixAuthenticationService = authenticationService, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() // Submit will return an error @@ -317,9 +295,7 @@ class ConfirmAccountProviderPresenterTest { throw AccountCreationNotSupported() }, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink(ConfirmAccountProviderEvents.Continue) skipItems(1) // Loading @@ -344,9 +320,7 @@ class ConfirmAccountProviderPresenterTest { params = ConfirmAccountProviderPresenter.Params(isAccountCreation = true), matrixAuthenticationService = authenticationService, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink(ConfirmAccountProviderEvents.Continue) skipItems(1) // Loading @@ -369,9 +343,7 @@ class ConfirmAccountProviderPresenterTest { matrixAuthenticationService = authenticationService, webClientUrlForAuthenticationRetriever = FakeWebClientUrlForAuthenticationRetriever { aUrl }, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink(ConfirmAccountProviderEvents.Continue) skipItems(1) // Loading @@ -394,9 +366,7 @@ class ConfirmAccountProviderPresenterTest { matrixAuthenticationService = authenticationService, webClientUrlForAuthenticationRetriever = FakeWebClientUrlForAuthenticationRetriever { aUrl }, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink(ConfirmAccountProviderEvents.Continue) skipItems(1) // Loading diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenterTest.kt index 8d70173d11..63f68b2221 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenterTest.kt @@ -7,9 +7,6 @@ package io.element.android.features.login.impl.screens.createaccount -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow -import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.core.meta.BuildMeta @@ -26,6 +23,7 @@ import io.element.android.libraries.matrix.test.verification.FakeSessionVerifica import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value +import io.element.android.tests.testutils.test import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -37,9 +35,7 @@ class CreateAccountPresenterTest { @Test fun `present - initial state`() = runTest { val presenter = createPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() assertThat(initialState.url).isEqualTo("aUrl") assertThat(initialState.pageProgress).isEqualTo(0) @@ -51,9 +47,7 @@ class CreateAccountPresenterTest { @Test fun `present - set up progress update the state`() = runTest { val presenter = createPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink(CreateAccountEvents.SetPageProgress(33)) assertThat(awaitItem().pageProgress).isEqualTo(33) @@ -65,9 +59,7 @@ class CreateAccountPresenterTest { val presenter = createPresenter( messageParser = FakeMessageParser { error("An error") } ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink(CreateAccountEvents.OnMessageReceived("")) assertThat(awaitItem().createAction).isInstanceOf(AsyncAction.Failure::class.java) @@ -77,9 +69,7 @@ class CreateAccountPresenterTest { @Test fun `present - receiving a message containing isTrusted is ignored`() = runTest { val presenter = createPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink(CreateAccountEvents.OnMessageReceived("isTrusted")) } @@ -98,9 +88,7 @@ class CreateAccountPresenterTest { messageParser = FakeMessageParser(lambda), clientProvider = clientProvider, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink(CreateAccountEvents.OnMessageReceived("aMessage")) assertThat(awaitItem().createAction.isLoading()).isTrue() @@ -118,9 +106,7 @@ class CreateAccountPresenterTest { ), messageParser = FakeMessageParser { anExternalSession() } ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink(CreateAccountEvents.OnMessageReceived("")) assertThat(awaitItem().createAction.isLoading()).isTrue() diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroPresenterTest.kt index 83686b56c6..6cc34fd32f 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroPresenterTest.kt @@ -7,14 +7,12 @@ package io.element.android.features.login.impl.screens.qrcode.intro -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow -import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.permissions.test.FakePermissionsPresenter import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory +import io.element.android.tests.testutils.test import kotlinx.coroutines.test.runTest import org.junit.Test @@ -22,9 +20,7 @@ class QrCodeIntroPresenterTest { @Test fun `present - initial state`() = runTest { val presenter = createQrCodeIntroPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { awaitItem().run { assertThat(appName).isEqualTo("AppName") assertThat(desktopAppName).isEqualTo("DesktopAppName") @@ -39,9 +35,7 @@ class QrCodeIntroPresenterTest { val permissionsPresenter = FakePermissionsPresenter().apply { setPermissionGranted() } val permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionsPresenter) val presenter = createQrCodeIntroPresenter(permissionsPresenterFactory = permissionsPresenterFactory) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { awaitItem().eventSink(QrCodeIntroEvents.Continue) assertThat(awaitItem().canContinue).isTrue() } @@ -52,9 +46,7 @@ class QrCodeIntroPresenterTest { val permissionsPresenter = FakePermissionsPresenter() val permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionsPresenter) val presenter = createQrCodeIntroPresenter(permissionsPresenterFactory = permissionsPresenterFactory) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { awaitItem().eventSink(QrCodeIntroEvents.Continue) assertThat(awaitItem().cameraPermissionState.showDialog).isTrue() } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanPresenterTest.kt index a4d594399f..058f417fd1 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanPresenterTest.kt @@ -7,9 +7,6 @@ package io.element.android.features.login.impl.screens.qrcode.scan -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow -import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService @@ -34,9 +31,7 @@ class QrCodeScanPresenterTest { @Test fun `present - initial state`() = runTest { val presenter = createQrCodeScanPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { awaitItem().run { assertThat(isScanning).isTrue() assertThat(authenticationAction.isUninitialized()).isTrue() @@ -114,9 +109,7 @@ class QrCodeScanPresenterTest { parseQrCodeLoginDataResult = { Result.failure(Exception("Failed to parse QR code")) } ) val presenter = createQrCodeScanPresenter(qrCodeLoginDataFactory = qrCodeLoginDataFactory) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink(QrCodeScanEvents.QrCodeScanned(byteArrayOf())) assertThat(awaitItem().isScanning).isFalse() @@ -140,9 +133,7 @@ class QrCodeScanPresenterTest { } qrCodeLoginManager.resetAction = resetAction val presenter = createQrCodeScanPresenter(qrCodeLoginDataFactory = qrCodeLoginDataFactory, qrCodeLoginManager = qrCodeLoginManager) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { // Skip initial item skipItems(1) diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt index f4c193ced2..f19743bac0 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt @@ -7,9 +7,6 @@ package io.element.android.features.login.impl.screens.searchaccountprovider -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow -import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.login.impl.changeserver.aChangeServerState import io.element.android.features.login.impl.resolver.HomeserverResolver @@ -18,6 +15,7 @@ import io.element.android.libraries.matrix.test.auth.FakeHomeServerLoginCompatib import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value +import io.element.android.tests.testutils.test import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -34,9 +32,7 @@ class SearchAccountProviderPresenterTest { homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeLoginCompatibilityChecker), changeServerPresenter = { aChangeServerState() } ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() assertThat(initialState.userInput).isEmpty() assertThat(initialState.userInputResult).isEqualTo(AsyncData.Uninitialized) @@ -50,9 +46,7 @@ class SearchAccountProviderPresenterTest { homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeLoginCompatibilityChecker), changeServerPresenter = { aChangeServerState() } ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("https://test.org")) val withInputState = awaitItem() @@ -76,9 +70,7 @@ class SearchAccountProviderPresenterTest { homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRetriever), changeServerPresenter = { aChangeServerState() } ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("test")) val withInputState = awaitItem() @@ -111,9 +103,7 @@ class SearchAccountProviderPresenterTest { homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeLoginCompatibilityChecker), changeServerPresenter = { aChangeServerState() } ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("test")) val withInputState = awaitItem() @@ -153,9 +143,7 @@ class SearchAccountProviderPresenterTest { homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeLoginCompatibilityChecker), changeServerPresenter = { aChangeServerState() } ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("test")) val withInputState = awaitItem() From 0657a201ead6eddf9ebe8a560c584764602b9016 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 6 Nov 2025 14:58:50 +0000 Subject: [PATCH 066/216] Update screenshots --- ...tures.login.impl.changeserver_ChangeServerView_Day_5_en.png | 3 +++ ...res.login.impl.changeserver_ChangeServerView_Night_5_en.png | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/features.login.impl.changeserver_ChangeServerView_Day_5_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.login.impl.changeserver_ChangeServerView_Night_5_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.changeserver_ChangeServerView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.changeserver_ChangeServerView_Day_5_en.png new file mode 100644 index 0000000000..1125024a5a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.changeserver_ChangeServerView_Day_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ae6d230961018f1128ea655529a4287dbe3bbbc2762079d0c4f335313d2f2ab0 +size 25166 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.changeserver_ChangeServerView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.changeserver_ChangeServerView_Night_5_en.png new file mode 100644 index 0000000000..777f0bb627 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.changeserver_ChangeServerView_Night_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1502711e6305f9adf4719b28c1fe3755089ae6c42c6c66e8e5bf73ab2bbb65c1 +size 23874 From f7cca0fc0c713beea3a6b83a23d80e56c012e85c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 6 Nov 2025 18:16:39 +0100 Subject: [PATCH 067/216] Ensure the form data are not lost when opening the log viewer. Closes #5579 --- .../features/rageshake/impl/bugreport/BugReportPresenter.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt index bfcfcf424e..659c397c96 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt @@ -32,7 +32,7 @@ class BugReportPresenter( private val bugReporter: BugReporter, private val crashDataStore: CrashDataStore, private val screenshotHolder: ScreenshotHolder, - @AppCoroutineScope + @param:AppCoroutineScope private val appCoroutineScope: CoroutineScope, ) : Presenter { private class BugReporterUploadListener( @@ -77,7 +77,7 @@ class BugReportPresenter( val sendingAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } - val formState: MutableState = remember { + val formState: MutableState = rememberSaveable { mutableStateOf(BugReportFormState.Default) } val uploadListener = BugReporterUploadListener(sendingProgress, sendingAction) From d296d7d0b4680ffef8fffed894574a5eebd71336 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 6 Nov 2025 17:40:02 +0000 Subject: [PATCH 068/216] Update screenshots --- ...references.impl.advanced_AdvancedSettingsViewDark_0_en.png | 4 ++-- ...references.impl.advanced_AdvancedSettingsViewDark_1_en.png | 4 ++-- ...references.impl.advanced_AdvancedSettingsViewDark_2_en.png | 4 ++-- ...references.impl.advanced_AdvancedSettingsViewDark_3_en.png | 4 ++-- ...references.impl.advanced_AdvancedSettingsViewDark_4_en.png | 4 ++-- ...references.impl.advanced_AdvancedSettingsViewDark_5_en.png | 4 ++-- ...references.impl.advanced_AdvancedSettingsViewDark_6_en.png | 4 ++-- ...references.impl.advanced_AdvancedSettingsViewDark_7_en.png | 4 ++-- ...references.impl.advanced_AdvancedSettingsViewDark_8_en.png | 4 ++-- ...eferences.impl.advanced_AdvancedSettingsViewLight_0_en.png | 4 ++-- ...eferences.impl.advanced_AdvancedSettingsViewLight_1_en.png | 4 ++-- ...eferences.impl.advanced_AdvancedSettingsViewLight_2_en.png | 4 ++-- ...eferences.impl.advanced_AdvancedSettingsViewLight_3_en.png | 4 ++-- ...eferences.impl.advanced_AdvancedSettingsViewLight_4_en.png | 4 ++-- ...eferences.impl.advanced_AdvancedSettingsViewLight_5_en.png | 4 ++-- ...eferences.impl.advanced_AdvancedSettingsViewLight_6_en.png | 4 ++-- ...eferences.impl.advanced_AdvancedSettingsViewLight_7_en.png | 4 ++-- ...eferences.impl.advanced_AdvancedSettingsViewLight_8_en.png | 4 ++-- ...ns.impl.permissions_ChangeRoomPermissionsView_Day_0_en.png | 4 ++-- ...ns.impl.permissions_ChangeRoomPermissionsView_Day_1_en.png | 4 ++-- ...ns.impl.permissions_ChangeRoomPermissionsView_Day_2_en.png | 4 ++-- ...ns.impl.permissions_ChangeRoomPermissionsView_Day_3_en.png | 4 ++-- ...ns.impl.permissions_ChangeRoomPermissionsView_Day_4_en.png | 4 ++-- ....impl.permissions_ChangeRoomPermissionsView_Night_0_en.png | 4 ++-- ....impl.permissions_ChangeRoomPermissionsView_Night_1_en.png | 4 ++-- ....impl.permissions_ChangeRoomPermissionsView_Night_2_en.png | 4 ++-- ....impl.permissions_ChangeRoomPermissionsView_Night_3_en.png | 4 ++-- ....impl.permissions_ChangeRoomPermissionsView_Night_4_en.png | 4 ++-- ...mponents.preferences_PreferenceDropdown_Preferences_en.png | 4 ++-- 29 files changed, 58 insertions(+), 58 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en.png index b4caf353fa..ae30993ae5 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5116af838229fd1d17eb6d63ffdde82e90b75ff01000f8bf6f83edb982555443 -size 47358 +oid sha256:d986fd93989b178dc08957270a75fee126c9910a45d067ca7c3353d188b69850 +size 47377 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en.png index d14d977afe..e1c896297c 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d2f0f54ecb417dcc7ebb2e125b61e9e01f682dd7fea0eabb729c243387b6004 -size 47247 +oid sha256:cb5c468b4f8235595bcfb3355fa29cfd43567d51e27785357bdecb13ab1dadd9 +size 47268 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en.png index 7791c3dada..dc78febfda 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ef498d98d519dfe496622ec3ec7289ef5e636bb4c8ce4512cde1997fba3247d -size 47237 +oid sha256:e16ace640aa43a2d6b923a46fec94ca41e1b9ce1632cbd0319c646c08dc1b167 +size 47257 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en.png index d0b1a44c01..bc69773fda 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:665aa1c40622f38b0abe12d763c260a07011f7c32e47442d44477d2fead32e6c -size 47224 +oid sha256:f90e8efb79b3525fafdee503a0aa5280e1d5bf0275ea3be0e79ee323c7b03751 +size 47248 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en.png index 3abb98e008..3a3942319f 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6df36eb76909634d7ef59d9fa44edc1aa1f87412b8524ec2a9f3c26554e89065 -size 47069 +oid sha256:8afb2771627534be13f709b1e125d9d4ae26d4ca1a7db102b31ef14620bb45a9 +size 47089 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en.png index a45ca7d865..4d87b9f694 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64e6997a02c70f17866e90f19cea2878844738748ce9ad60f85fbf04a3f35d8a -size 47360 +oid sha256:4e7b71b415175d4cae28404ca4aa361929df63fb670b9b5c86c327dd6df73ddf +size 47379 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en.png index dee1f01854..6ae9086f2a 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eae7e14242722e0b65e6bfe02f0d8c54e4581113c19888acfdbfd84b260ba53a -size 47013 +oid sha256:693180156b1bad6c7e0f1ef698ec8b6787912c2ca2f7d94326e2cda043ba6819 +size 47034 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en.png index 6e7bf498c1..fc1664cc49 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56d937d008aa6fea8b4dadc12eb313f217b15b2d7458d5d3b4b0af77918988fc -size 46648 +oid sha256:ba1df0d1d16e51d28ecbfcc34be2d7ffb6722efd8bc6089b4befb2d79f82ce5f +size 46668 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en.png index a293d0038e..76ed86b02b 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b1c44eb7d614c5db5c48a75cf483ee9364aa2113dffd79faf116835a21519b79 -size 53087 +oid sha256:3268b8e9c04a5a966f7d5b991299e783d6efe04cb05800ef45b2fd226b1d10bf +size 53105 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en.png index 2de7d822fa..b080444cda 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd31762cef1c2d84318d3fcb63f08c3b544368c59643d6ea54174e9ca6e7191b -size 48985 +oid sha256:e44bb25d897d6f4de241267c56c6e3bed1d68d3d21522c4d0200a82041ddc083 +size 48999 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en.png index 167e63caf4..4879d67434 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7789e460d69fa17b7a412a456930f2383c0ebb84a7a603c7443f0e7b66f2be0e -size 48866 +oid sha256:1cb9fe598d7bb977561b403e43acf2dc2ab943f17cd82aa57981c2adafdc07e7 +size 48879 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en.png index 96a27a4fc8..f46bf72380 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2fddfcdd50bf43eec8b0c5739067609aede15b6541de75282274803464d5ffc4 -size 48885 +oid sha256:981005ce4565843a041995c6c69cc9503a983de5df82433019fa33dc5ce3f597 +size 48896 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en.png index 056e1a8e21..0645a6d0b9 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:85c09edfa3db27e686dceb7aa21e427a25cdc4860669cb716a820b8ebf8a255e -size 48868 +oid sha256:8f80185f3563d3e74abd3a1cccc79fbdfe47e4cf0e3bea98130698e74d09eab4 +size 48878 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en.png index fdfaa42df9..a0d683dc0c 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d694ba80e6faeb81a195064e59d3febe4630df718124e7ee842ba1aad2634117 -size 48789 +oid sha256:1cc613718c250b031c8bf551996bb528956894fea2e2e98619b5f45361d6947d +size 48804 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en.png index 2e3b8e8deb..c611d7e93a 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4b556d0d99e4e93a736e25f79df51bd02f465192496463d0480081105de1077c -size 48987 +oid sha256:badb0c35e0c1f6b3121a2469862b5559e061023513771a65ebdeb94ec7b98078 +size 49001 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en.png index 5c4df596af..74bc5f88d8 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e285f7f4435053db05f95dee11bf3c46f4649c1f6a2f6e18ad91e652f0725ac -size 48809 +oid sha256:aa36e842c20867e60424763ec0e2d9ed7f01030cf0525df3b0965273ad3e7e06 +size 48823 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en.png index 1bddc35de9..03e8601f97 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:607061d3f293b5d575b236cd24f8d13b3d2db6dbd76e21710b698ccdee38541b -size 48492 +oid sha256:6f9a0affa7a07696ce1a0be56e1264b8b7213dd1fa5627b6af3307ba9ebaca36 +size 48508 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en.png index 859e412c51..8f10026eed 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:adfc7ec5289fbd2bcdc3b6be31b1644165f24bd422957bb038b381b57d309e98 -size 55242 +oid sha256:d0241bcb4fec4fe87d0a161960726bc9a33987b78c4748a6670771ca3e9b7584 +size 55254 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en.png index abe319f714..2021890e00 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9043007191252fac22ab58a635e0c59d96edcba741fccf9bb9812889eb15ca24 -size 60259 +oid sha256:7c180424588c0856aee7ba2de7d93845c96201ecbdc4f23a00b4d671a903a980 +size 60193 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en.png index 282b3fc89e..dc8055d41c 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cadaea0794b81e28b03c84b4331d2dd11df6cff63344e1e2561de9e3a1450c23 -size 60178 +oid sha256:ab650b1fa9b1e7b464fb8ac9006d8d7705d9d4409093797efac24b9f4ad90afd +size 60109 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en.png index 5823a4adec..6c2cf322be 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f55bfa9823ab3480ed846e137ed7e1fa1f3a0011ef8034458ac2baa4261cd7e8 -size 52084 +oid sha256:d692109b09fbdd5f0c147ebbb7f352d62491f785a5266308d3a60447f91a6e00 +size 52103 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en.png index ca5dd4e9c0..296a9fc1fe 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b5ce4f5ac003767c68a89465e1eb396f7d5fb67f2157a4e00a56591820666644 -size 49522 +oid sha256:4c6726e8db1fc6999fb43cac1614ad8179adc1191b4c1e62b8e66fc5fd70bf76 +size 49542 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en.png index a14ec8aad7..7acc0ebd0f 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8c5692d76f33a622452727fc4b1d1438cb608038f1cb0b6ba86a7b8e98f143db -size 56335 +oid sha256:e8519f86746bb89c6236332f83fbfc00fad488593f487f3d4148285abd5a3cf7 +size 56356 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en.png index c5b0cafa86..180d3ca9c7 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:644b0d4d7c9ac619fa277086071a35d08f24408816a73b5293acde325d06373b -size 58568 +oid sha256:27d7653e6052117a360dfe0e03e16875958e9f19bd56355881e9399fedcabf6c +size 58578 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en.png index 70aa7e518e..f0157f1ced 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a62487bdcd023634fbb7a81642821dbca4b813798aae1dfaa4d5b852d7e03fca -size 58470 +oid sha256:2fad72bab0a58f75ca68aa67442dcae44946e0c2b156139e03536dda051f4115 +size 58471 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en.png index 8f56a177e4..f3f664d4f6 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08d28320336e02d0db2d4eb5b20793879d54e52f3dbb287f6399efcad5dbc47e -size 50565 +oid sha256:fd4f60534b4df7f3631d79b39e7aeb22609d967f7edef0ee5dee4ea05f1aa183 +size 50635 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en.png index a0a2e1a701..cb69a420a5 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64bc6edeffdcec9381c9049383d72ed4f5fe785de3f0b24d9f7a32466a26f24b -size 47347 +oid sha256:d860c6be97aa2929fb37e25f312f14ee17312b2e201268e232fc6a5dd2621ce2 +size 47394 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en.png index b4dd57cfa6..ade6d6b994 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:45da36f66abead151d7634c2c554862db03948eede77adf1f397fead1429e67e -size 54151 +oid sha256:4c4e198a9cb65a66ea0854d9fcde915ea4a70826aef3ccbb4a8e7f2d38f285fd +size 54200 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.preferences_PreferenceDropdown_Preferences_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.preferences_PreferenceDropdown_Preferences_en.png index 9e7bb7a63e..99527960b1 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.preferences_PreferenceDropdown_Preferences_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.preferences_PreferenceDropdown_Preferences_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9af7f91767070914af81742b8f9d9a85c68e5aa818d19b7981527eb7e4360ec4 -size 31366 +oid sha256:73749a1655e26374776fa7e8aa83582b969cd40259a5ff580710a2b19acdddb6 +size 32072 From 1d720573919ebcb2f29cf47830e604f2a5c5148b Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 6 Nov 2025 20:07:01 +0100 Subject: [PATCH 069/216] change(room members): show ModerationView for banned members too --- .../roomdetails/impl/members/RoomMemberListPresenter.kt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt index 90619d2e2d..a0eca8039a 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt @@ -17,7 +17,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dev.zacsweers.metro.Inject -import io.element.android.features.roommembermoderation.api.ModerationAction import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents import io.element.android.features.roommembermoderation.api.RoomMemberModerationState import io.element.android.libraries.architecture.AsyncData @@ -164,11 +163,7 @@ class RoomMemberListPresenter( is RoomMemberListEvents.OnSearchActiveChanged -> isSearchActive = event.active is RoomMemberListEvents.UpdateSearchQuery -> searchQuery = event.query is RoomMemberListEvents.RoomMemberSelected -> - if (event.roomMember.membership == RoomMembershipState.BAN) { - roomModerationState.eventSink(RoomMemberModerationEvents.ProcessAction(ModerationAction.UnbanUser, event.roomMember.toMatrixUser())) - } else { - roomModerationState.eventSink(RoomMemberModerationEvents.ShowActionsForUser(event.roomMember.toMatrixUser())) - } + roomModerationState.eventSink(RoomMemberModerationEvents.ShowActionsForUser(event.roomMember.toMatrixUser())) } } From 8d76dd916126abe29add8afe50af926a0edfd58c Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 6 Nov 2025 20:10:07 +0100 Subject: [PATCH 070/216] change(room members): add reason to unban --- .../impl/InternalRoomMemberModerationEvents.kt | 2 +- .../impl/RoomMemberModerationPresenter.kt | 8 ++++++-- .../impl/RoomMemberModerationView.kt | 13 ++++++++----- .../impl/RoomMemberModerationPresenterTest.kt | 2 +- .../impl/RoomMemberModerationViewTest.kt | 2 +- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationEvents.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationEvents.kt index 902c2bd21f..1bdcfb88f9 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationEvents.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationEvents.kt @@ -12,6 +12,6 @@ import io.element.android.features.roommembermoderation.api.RoomMemberModeration sealed interface InternalRoomMemberModerationEvents : RoomMemberModerationEvents { data class DoKickUser(val reason: String) : InternalRoomMemberModerationEvents data class DoBanUser(val reason: String) : InternalRoomMemberModerationEvents - data object DoUnbanUser : InternalRoomMemberModerationEvents + data class DoUnbanUser(val reason: String) : InternalRoomMemberModerationEvents data object Reset : InternalRoomMemberModerationEvents } diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt index 995baa069b..9ff6b0b0de 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt @@ -118,7 +118,7 @@ class RoomMemberModerationPresenter( } is InternalRoomMemberModerationEvents.DoUnbanUser -> { selectedUser?.let { - coroutineScope.unbanUser(it.userId, unbanUserAsyncAction) + coroutineScope.unbanUser(it.userId, event.reason, unbanUserAsyncAction) } selectedUser = null } @@ -197,10 +197,14 @@ class RoomMemberModerationPresenter( private fun CoroutineScope.unbanUser( userId: UserId, + reason: String, unbanUserAction: MutableState>, ) = runActionAndWaitForMembershipChange(unbanUserAction) { analyticsService.capture(RoomModeration(RoomModeration.Action.UnbanMember)) - room.unbanUser(userId = userId) + room.unbanUser( + userId = userId, + reason = reason.takeIf { it.isNotBlank() }, + ) } private fun CoroutineScope.runActionAndWaitForMembershipChange( diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt index 248b6cd02b..b7d3dfee95 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt @@ -166,18 +166,21 @@ private fun RoomMemberAsyncActions( } when (val action = state.unbanUserAsyncAction) { is AsyncAction.Confirming -> { - ConfirmationDialog( + TextFieldDialog( title = stringResource(R.string.screen_bottom_sheet_manage_room_member_unban_member_confirmation_title), - content = stringResource(R.string.screen_bottom_sheet_manage_room_member_unban_member_confirmation_description), submitText = stringResource(R.string.screen_bottom_sheet_manage_room_member_unban_member_confirmation_action), - onSubmitClick = { + onSubmit = { reason -> val userDisplayName = selectedUser?.getBestName().orEmpty() asyncIndicatorState.enqueue { AsyncIndicator.Loading(text = stringResource(R.string.screen_bottom_sheet_manage_room_member_unbanning_user, userDisplayName)) } - state.eventSink(InternalRoomMemberModerationEvents.DoUnbanUser) + state.eventSink(InternalRoomMemberModerationEvents.DoUnbanUser(reason = reason)) }, - onDismiss = { state.eventSink(InternalRoomMemberModerationEvents.Reset) }, + onDismissRequest = { state.eventSink(InternalRoomMemberModerationEvents.Reset) }, + placeholder = stringResource(id = CommonStrings.common_reason), + label = stringResource(id = CommonStrings.common_reason), + content = stringResource(R.string.screen_bottom_sheet_manage_room_member_unban_member_confirmation_description), + value = "", ) } is AsyncAction.Failure -> { diff --git a/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenterTest.kt b/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenterTest.kt index 40e09b3620..423e9f6621 100644 --- a/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenterTest.kt +++ b/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenterTest.kt @@ -290,7 +290,7 @@ class RoomMemberModerationPresenterTest { ) ) skipItems(2) - initialState.eventSink(InternalRoomMemberModerationEvents.DoUnbanUser) + initialState.eventSink(InternalRoomMemberModerationEvents.DoUnbanUser("Reason")) skipItems(1) val loadingState = awaitState() assertThat(loadingState.unbanUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java) diff --git a/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationViewTest.kt b/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationViewTest.kt index fc1b815562..2ac8a025bb 100644 --- a/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationViewTest.kt +++ b/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationViewTest.kt @@ -181,7 +181,7 @@ class RoomMemberModerationViewTest { ), ) rule.pressTag(TestTags.dialogPositive.value) - eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.DoUnbanUser) + eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.DoUnbanUser("")) } @Test From b61ce1b19cf46a5a196accb4be7c5f111af0eeba Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Nov 2025 09:41:57 +0100 Subject: [PATCH 071/216] Create specific errors for Invalid or Unsupporte homeserver. --- .../impl/changeserver/ChangeServerPresenter.kt | 6 +----- .../changeserver/ChangeServerStateProvider.kt | 5 +---- .../login/impl/changeserver/ChangeServerView.kt | 16 ++++++++++++++++ .../login/impl/error/ChangeServerError.kt | 5 +++-- .../impl/error/ChangeServerErrorProvider.kt | 6 ++---- .../features/login/impl/login/LoginModeView.kt | 11 +++++++++++ .../changeserver/ChangeServerPresenterTest.kt | 3 +-- 7 files changed, 35 insertions(+), 17 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt index 71d4e97c3b..fc114aaa6b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt @@ -13,7 +13,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import dev.zacsweers.metro.Inject -import io.element.android.features.login.impl.R import io.element.android.features.login.impl.accesscontrol.DefaultAccountProviderAccessControl import io.element.android.features.login.impl.accountprovider.AccountProvider import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource @@ -63,10 +62,7 @@ class ChangeServerPresenter( ) val details = authenticationService.setHomeserver(data.url).getOrThrow() if (details.supportsOidcLogin.not() && details.supportsPasswordLogin.not()) { - // Unsupported homeserver - throw ChangeServerError.Error( - messageId = R.string.screen_login_error_unsupported_authentication, - ) + throw ChangeServerError.UnsupportedServer } // Homeserver is valid, remember user choice accountProviderDataSource.userSelection(data) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStateProvider.kt index 8a7d015750..554c9b545e 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStateProvider.kt @@ -8,7 +8,6 @@ package io.element.android.features.login.impl.changeserver import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.features.login.impl.R import io.element.android.features.login.impl.error.ChangeServerError import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.ui.strings.CommonStrings @@ -37,9 +36,7 @@ open class ChangeServerStateProvider : PreviewParameterProvider { when (val error = state.changeServerAction.error as? ChangeServerError) { + ChangeServerError.InvalidServer -> + ErrorDialog( + modifier = modifier, + content = stringResource(R.string.screen_change_server_error_invalid_homeserver), + onSubmit = { + eventSink.invoke(ChangeServerEvents.ClearError) + } + ) + ChangeServerError.UnsupportedServer -> + ErrorDialog( + modifier = modifier, + content = stringResource(R.string.screen_login_error_unsupported_authentication), + onSubmit = { + eventSink.invoke(ChangeServerEvents.ClearError) + } + ) is ChangeServerError.Error -> { ErrorDialog( modifier = modifier, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerError.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerError.kt index a814e61aaa..2c4550953e 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerError.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerError.kt @@ -11,7 +11,6 @@ import androidx.annotation.StringRes import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.res.stringResource -import io.element.android.features.login.impl.R import io.element.android.features.login.impl.changeserver.AccountProviderAccessException import io.element.android.libraries.matrix.api.auth.AuthenticationException import io.element.android.libraries.ui.strings.CommonStrings @@ -37,6 +36,8 @@ sealed class ChangeServerError : Exception() { ) : ChangeServerError() data object SlidingSyncAlert : ChangeServerError() + data object InvalidServer : ChangeServerError() + data object UnsupportedServer : ChangeServerError() companion object { fun from(error: Throwable): ChangeServerError = when (error) { @@ -51,7 +52,7 @@ sealed class ChangeServerError : Exception() { unauthorisedAccountProviderTitle = error.unauthorisedAccountProviderTitle, authorisedAccountProviderTitles = error.authorisedAccountProviderTitles, ) - else -> Error(messageId = R.string.screen_change_server_error_invalid_homeserver) + else -> InvalidServer } } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerErrorProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerErrorProvider.kt index 333347851a..4a9a7e934b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerErrorProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerErrorProvider.kt @@ -8,14 +8,11 @@ package io.element.android.features.login.impl.error import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.features.login.impl.R class ChangeServerErrorProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( - ChangeServerError.Error( - messageId = R.string.screen_change_server_error_invalid_homeserver, - ), + ChangeServerError.InvalidServer, ChangeServerError.Error( messageStr = "An error description", ), @@ -28,5 +25,6 @@ class ChangeServerErrorProvider : PreviewParameterProvider { authorisedAccountProviderTitles = listOf("provider.org", "provider.io"), ), ChangeServerError.SlidingSyncAlert, + ChangeServerError.UnsupportedServer, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt index c3fe5eac47..4c97897cce 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt @@ -41,6 +41,17 @@ fun LoginModeView( when (val error = loginMode.error) { is ChangeServerError -> { when (error) { + ChangeServerError.InvalidServer -> + ErrorDialog( + content = stringResource(R.string.screen_change_server_error_invalid_homeserver), + onSubmit = onClearError, + ) + is ChangeServerError.UnsupportedServer -> { + ErrorDialog( + content = stringResource(R.string.screen_login_error_unsupported_authentication), + onSubmit = onClearError, + ) + } is ChangeServerError.Error -> { ErrorDialog( content = error.message(), diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt index ce026b447c..d1090cb237 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt @@ -10,7 +10,6 @@ package io.element.android.features.login.impl.changeserver import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService -import io.element.android.features.login.impl.R import io.element.android.features.login.impl.aMatrixHomeServerDetails import io.element.android.features.login.impl.accesscontrol.DefaultAccountProviderAccessControl import io.element.android.features.login.impl.accountprovider.AccountProvider @@ -117,7 +116,7 @@ class ChangeServerPresenterTest { val failureState = awaitItem() assertThat(failureState.changeServerAction).isInstanceOf(AsyncData.Failure::class.java) assertThat(failureState.changeServerAction.errorOrNull()).isEqualTo( - ChangeServerError.Error(R.string.screen_login_error_unsupported_authentication) + ChangeServerError.UnsupportedServer ) } } From e12726f40526f30edd97bf1295881fcf7ff13fd9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Nov 2025 09:52:04 +0100 Subject: [PATCH 072/216] Improve error mapping --- .../changeserver/ChangeServerStateProvider.kt | 3 +-- .../login/impl/changeserver/ChangeServerView.kt | 3 ++- .../login/impl/error/ChangeServerError.kt | 14 ++------------ .../features/login/impl/login/LoginModeView.kt | 4 ++-- .../matrix/api/auth/AuthenticationException.kt | 15 +++++++++------ .../matrix/impl/auth/AuthenticationException.kt | 1 - 6 files changed, 16 insertions(+), 24 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStateProvider.kt index 554c9b545e..020d5a0851 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStateProvider.kt @@ -10,13 +10,12 @@ package io.element.android.features.login.impl.changeserver import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.login.impl.error.ChangeServerError import io.element.android.libraries.architecture.AsyncData -import io.element.android.libraries.ui.strings.CommonStrings open class ChangeServerStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aChangeServerState(), - aChangeServerState(changeServerAction = AsyncData.Failure(ChangeServerError.Error(CommonStrings.error_unknown))), + aChangeServerState(changeServerAction = AsyncData.Failure(ChangeServerError.Error(null))), aChangeServerState(changeServerAction = AsyncData.Failure(ChangeServerError.SlidingSyncAlert)), aChangeServerState( changeServerAction = AsyncData.Failure( diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt index a9784580d1..bf13250f7f 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt @@ -26,6 +26,7 @@ import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.LocalBuildMeta +import io.element.android.libraries.ui.strings.CommonStrings @Composable fun ChangeServerView( @@ -58,7 +59,7 @@ fun ChangeServerView( is ChangeServerError.Error -> { ErrorDialog( modifier = modifier, - content = error.message(), + content = error.messageStr ?: stringResource(CommonStrings.error_unknown), onSubmit = { eventSink.invoke(ChangeServerEvents.ClearError) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerError.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerError.kt index 2c4550953e..cd5dcf95b3 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerError.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerError.kt @@ -7,23 +7,13 @@ package io.element.android.features.login.impl.error -import androidx.annotation.StringRes -import androidx.compose.runtime.Composable -import androidx.compose.runtime.ReadOnlyComposable -import androidx.compose.ui.res.stringResource import io.element.android.features.login.impl.changeserver.AccountProviderAccessException import io.element.android.libraries.matrix.api.auth.AuthenticationException -import io.element.android.libraries.ui.strings.CommonStrings sealed class ChangeServerError : Exception() { data class Error( - @StringRes val messageId: Int? = null, val messageStr: String? = null, - ) : ChangeServerError() { - @Composable - @ReadOnlyComposable - fun message(): String = messageStr ?: stringResource(messageId ?: CommonStrings.error_unknown) - } + ) : ChangeServerError() data class NeedElementPro( val unauthorisedAccountProviderTitle: String, @@ -52,7 +42,7 @@ sealed class ChangeServerError : Exception() { unauthorisedAccountProviderTitle = error.unauthorisedAccountProviderTitle, authorisedAccountProviderTitles = error.authorisedAccountProviderTitles, ) - else -> InvalidServer + else -> Error(messageStr = error.message) } } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt index 4c97897cce..1c5d1f929a 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt @@ -54,7 +54,7 @@ fun LoginModeView( } is ChangeServerError.Error -> { ErrorDialog( - content = error.message(), + content = error.messageStr ?: stringResource(CommonStrings.error_unknown), onSubmit = onClearError, ) } @@ -102,7 +102,7 @@ fun LoginModeView( } is AuthenticationException.AccountAlreadyLoggedIn -> { ErrorDialog( - content = stringResource(CommonStrings.error_account_already_logged_in, error.message.orEmpty()), + content = stringResource(CommonStrings.error_account_already_logged_in, error.userId), onSubmit = onClearError, ) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt index 03e8d57150..7370135729 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt @@ -7,10 +7,13 @@ package io.element.android.libraries.matrix.api.auth -sealed class AuthenticationException(message: String) : Exception(message) { - class AccountAlreadyLoggedIn(userId: String) : AuthenticationException(userId) - class InvalidServerName(message: String) : AuthenticationException(message) - class SlidingSyncVersion(message: String) : AuthenticationException(message) - class Oidc(message: String) : AuthenticationException(message) - class Generic(message: String) : AuthenticationException(message) +sealed class AuthenticationException(message: String?) : Exception(message) { + data class AccountAlreadyLoggedIn( + val userId: String, + ) : AuthenticationException(null) + + class InvalidServerName(message: String?) : AuthenticationException(message) + class SlidingSyncVersion(message: String?) : AuthenticationException(message) + class Oidc(message: String?) : AuthenticationException(message) + class Generic(message: String?) : AuthenticationException(message) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt index 05eb4c4d5f..1960fe7c60 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt @@ -12,7 +12,6 @@ import org.matrix.rustcomponents.sdk.ClientBuildException import org.matrix.rustcomponents.sdk.OidcException fun Throwable.mapAuthenticationException(): AuthenticationException { - val message = this.message ?: "Unknown error" return when (this) { is AuthenticationException -> this is ClientBuildException -> when (this) { From 98792c9562b6c349a93d11220ba7a6f993f8a8ba Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Nov 2025 10:04:48 +0100 Subject: [PATCH 073/216] Improve error mapping --- .../features/login/impl/error/ChangeServerError.kt | 13 +++++++++++-- .../matrix/api/auth/AuthenticationException.kt | 1 + .../matrix/impl/auth/AuthenticationException.kt | 2 +- .../impl/auth/AuthenticationExceptionMappingTest.kt | 6 +++--- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerError.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerError.kt index cd5dcf95b3..02b71b7291 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerError.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerError.kt @@ -32,8 +32,17 @@ sealed class ChangeServerError : Exception() { companion object { fun from(error: Throwable): ChangeServerError = when (error) { is ChangeServerError -> error - is AuthenticationException.SlidingSyncVersion -> SlidingSyncAlert - is AuthenticationException.Oidc -> Error(messageStr = error.message) + is AuthenticationException -> { + when (error) { + is AuthenticationException.SlidingSyncVersion -> SlidingSyncAlert + is AuthenticationException.InvalidServerName, + is AuthenticationException.ServerUnreachable -> InvalidServer + // AccountAlreadyLoggedIn error should not happen at this point + is AuthenticationException.AccountAlreadyLoggedIn -> Error(messageStr = error.message) + is AuthenticationException.Generic -> Error(messageStr = error.message) + is AuthenticationException.Oidc -> Error(messageStr = error.message) + } + } is AccountProviderAccessException.NeedElementProException -> NeedElementPro( unauthorisedAccountProviderTitle = error.unauthorisedAccountProviderTitle, applicationId = error.applicationId, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt index 7370135729..954a8e9de2 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt @@ -14,6 +14,7 @@ sealed class AuthenticationException(message: String?) : Exception(message) { class InvalidServerName(message: String?) : AuthenticationException(message) class SlidingSyncVersion(message: String?) : AuthenticationException(message) + class ServerUnreachable(message: String?) : AuthenticationException(message) class Oidc(message: String?) : AuthenticationException(message) class Generic(message: String?) : AuthenticationException(message) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt index 1960fe7c60..ba43065fad 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt @@ -19,7 +19,7 @@ fun Throwable.mapAuthenticationException(): AuthenticationException { is ClientBuildException.InvalidServerName -> AuthenticationException.InvalidServerName(message) is ClientBuildException.SlidingSyncVersion -> AuthenticationException.SlidingSyncVersion(message) is ClientBuildException.Sdk -> AuthenticationException.Generic(message) - is ClientBuildException.ServerUnreachable -> AuthenticationException.Generic(message) + is ClientBuildException.ServerUnreachable -> AuthenticationException.ServerUnreachable(message) is ClientBuildException.SlidingSync -> AuthenticationException.Generic(message) is ClientBuildException.WellKnownDeserializationException -> AuthenticationException.Generic(message) is ClientBuildException.WellKnownLookupFailed -> AuthenticationException.Generic(message) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt index c73ba1424b..db37fc40b5 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt @@ -16,10 +16,10 @@ import org.matrix.rustcomponents.sdk.OidcException class AuthenticationExceptionMappingTest { @Test - fun `mapping an exception with no message returns 'Unknown error' message`() { + fun `mapping an exception with no message returns null message`() { val exception = Exception() val mappedException = exception.mapAuthenticationException() - assertThat(mappedException.message).isEqualTo("Unknown error") + assertThat(mappedException.message).isEqualTo(null) } @Test @@ -46,7 +46,7 @@ class AuthenticationExceptionMappingTest { assertThat(ClientBuildException.Sdk("SDK issue").mapAuthenticationException()) .isException("SDK issue") assertThat(ClientBuildException.ServerUnreachable("Server unreachable").mapAuthenticationException()) - .isException("Server unreachable") + .isException("Server unreachable") assertThat(ClientBuildException.SlidingSync("Sliding Sync").mapAuthenticationException()) .isException("Sliding Sync") assertThat(ClientBuildException.WellKnownDeserializationException("WellKnown Deserialization").mapAuthenticationException()) From 70e3e768aa8c0d80d8c479cc1ec2741cfaeb11cc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Nov 2025 10:18:45 +0100 Subject: [PATCH 074/216] Avoid usage of `not()` and add unit tests. --- .../changeserver/ChangeServerPresenter.kt | 2 +- .../changeserver/ChangeServerPresenterTest.kt | 2 +- .../ConfirmAccountProviderPresenterTest.kt | 2 +- .../LoginPasswordPresenterTest.kt | 2 +- .../api/auth/MatrixHomeServerDetails.kt | 4 +- .../api/auth/MatrixHomeServerDetailsTest.kt | 50 +++++++++++++++++++ .../test/auth}/MatrixHomeServerDetails.kt | 2 +- 7 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetailsTest.kt rename {features/login/impl/src/test/kotlin/io/element/android/features/login/impl => libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth}/MatrixHomeServerDetails.kt (92%) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt index fc114aaa6b..463662b6f1 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt @@ -61,7 +61,7 @@ class ChangeServerPresenter( accountProviderUrl = data.url, ) val details = authenticationService.setHomeserver(data.url).getOrThrow() - if (details.supportsOidcLogin.not() && details.supportsPasswordLogin.not()) { + if (details.isSupported) { throw ChangeServerError.UnsupportedServer } // Homeserver is valid, remember user choice diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt index d1090cb237..8fd12fb589 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt @@ -10,7 +10,6 @@ package io.element.android.features.login.impl.changeserver import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService -import io.element.android.features.login.impl.aMatrixHomeServerDetails import io.element.android.features.login.impl.accesscontrol.DefaultAccountProviderAccessControl import io.element.android.features.login.impl.accountprovider.AccountProvider import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource @@ -22,6 +21,7 @@ import io.element.android.libraries.core.uri.ensureProtocol import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_HOMESERVER_URL import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService +import io.element.android.libraries.matrix.test.auth.aMatrixHomeServerDetails import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.libraries.wellknown.api.WellknownRetriever import io.element.android.libraries.wellknown.api.WellknownRetrieverResult diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt index 182b7d6e27..cd845eb401 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt @@ -10,7 +10,6 @@ package io.element.android.features.login.impl.screens.confirmaccountprovider import com.google.common.truth.Truth.assertThat import io.element.android.appconfig.AuthenticationConfig import io.element.android.features.enterprise.test.FakeEnterpriseService -import io.element.android.features.login.impl.aMatrixHomeServerDetails import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.features.login.impl.login.LoginMode import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported @@ -21,6 +20,7 @@ import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService +import io.element.android.libraries.matrix.test.auth.aMatrixHomeServerDetails import io.element.android.libraries.oidc.api.OidcAction import io.element.android.libraries.oidc.api.OidcActionFlow import io.element.android.libraries.oidc.test.customtab.FakeOidcActionFlow diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt index bfdc0a6785..3a7efd3bb2 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt @@ -10,7 +10,6 @@ package io.element.android.features.login.impl.screens.loginpassword import com.google.common.truth.Truth.assertThat import io.element.android.appconfig.AuthenticationConfig import io.element.android.features.enterprise.test.FakeEnterpriseService -import io.element.android.features.login.impl.aMatrixHomeServerDetails import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.SessionId @@ -19,6 +18,7 @@ import io.element.android.libraries.matrix.test.A_PASSWORD import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService +import io.element.android.libraries.matrix.test.auth.aMatrixHomeServerDetails import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.test import kotlinx.coroutines.test.runTest diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt index ce3af70121..2244258eb8 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt @@ -11,4 +11,6 @@ data class MatrixHomeServerDetails( val url: String, val supportsPasswordLogin: Boolean, val supportsOidcLogin: Boolean, -) +) { + val isSupported = supportsPasswordLogin || supportsOidcLogin +} diff --git a/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetailsTest.kt b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetailsTest.kt new file mode 100644 index 0000000000..39f8759ca3 --- /dev/null +++ b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetailsTest.kt @@ -0,0 +1,50 @@ +/* + * 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. + */ + +package io.element.android.libraries.matrix.api.auth + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.test.auth.aMatrixHomeServerDetails +import org.junit.Test + +class MatrixHomeServerDetailsTest { + @Test + fun `if homeserver supports oidc, then it is supported`() { + val sut = aMatrixHomeServerDetails( + supportsOidcLogin = true, + supportsPasswordLogin = false, + ) + assertThat(sut.isSupported).isTrue() + } + + @Test + fun `if homeserver supports password, then it is supported`() { + val sut = aMatrixHomeServerDetails( + supportsOidcLogin = false, + supportsPasswordLogin = true, + ) + assertThat(sut.isSupported).isTrue() + } + + @Test + fun `if homeserver supports both, then it is supported`() { + val sut = aMatrixHomeServerDetails( + supportsOidcLogin = true, + supportsPasswordLogin = true, + ) + assertThat(sut.isSupported).isTrue() + } + + @Test + fun `if homeserver supports none, then it is not supported`() { + val sut = aMatrixHomeServerDetails( + supportsOidcLogin = false, + supportsPasswordLogin = false, + ) + assertThat(sut.isSupported).isFalse() + } +} diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/MatrixHomeServerDetails.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/MatrixHomeServerDetails.kt similarity index 92% rename from features/login/impl/src/test/kotlin/io/element/android/features/login/impl/MatrixHomeServerDetails.kt rename to libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/MatrixHomeServerDetails.kt index 1bf32d20e2..379034a002 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/MatrixHomeServerDetails.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/MatrixHomeServerDetails.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.login.impl +package io.element.android.libraries.matrix.test.auth import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import io.element.android.libraries.matrix.test.A_HOMESERVER_URL From 8f63916a8ec974c9f2afa5dc5546a7d1c9eeec72 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Nov 2025 10:21:38 +0100 Subject: [PATCH 075/216] Remove `param` --- .../features/rageshake/impl/bugreport/BugReportPresenter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt index 659c397c96..790ecdd092 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt @@ -32,7 +32,7 @@ class BugReportPresenter( private val bugReporter: BugReporter, private val crashDataStore: CrashDataStore, private val screenshotHolder: ScreenshotHolder, - @param:AppCoroutineScope + @AppCoroutineScope private val appCoroutineScope: CoroutineScope, ) : Presenter { private class BugReporterUploadListener( From 1e39b45236778c6e9174518ab63b00a57c8e4126 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Nov 2025 10:22:08 +0100 Subject: [PATCH 076/216] fix(deps): update dependency com.posthog:posthog-android to v3.26.0 (#5696) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a3f6eed13c..ebda86b90d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -216,7 +216,7 @@ haze_materials = { module = "dev.chrisbanes.haze:haze-materials", version.ref = color_picker = "io.mhssn:colorpicker:1.0.0" # Analytics -posthog = "com.posthog:posthog-android:3.25.0" +posthog = "com.posthog:posthog-android:3.26.0" sentry = "io.sentry:sentry-android:8.25.0" # main branch can be tested replacing the version with main-SNAPSHOT matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.29.2" From 596b773535fcfc91cf9d4f28c39341010a5d13f4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Nov 2025 10:59:22 +0100 Subject: [PATCH 077/216] fix(deps): update metro to v0.7.5 (#5697) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ebda86b90d..63101fe24c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -52,7 +52,7 @@ haze = "1.6.10" dependencyAnalysis = "3.4.1" # DI -metro = "0.7.4" +metro = "0.7.5" # Auto service autoservice = "1.1.1" From cc01e19bf7a25c28f9f46c25f075c1d315434415 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Nov 2025 11:05:13 +0100 Subject: [PATCH 078/216] Use isNull() --- .../matrix/impl/auth/AuthenticationExceptionMappingTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt index db37fc40b5..e84b42ea2f 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt @@ -19,7 +19,7 @@ class AuthenticationExceptionMappingTest { fun `mapping an exception with no message returns null message`() { val exception = Exception() val mappedException = exception.mapAuthenticationException() - assertThat(mappedException.message).isEqualTo(null) + assertThat(mappedException.message).isNull() } @Test From 3fd961c3292475fcde812f4e3142567a6d919e87 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 7 Nov 2025 11:04:44 +0000 Subject: [PATCH 079/216] Update screenshots --- .../features.login.impl.login_LoginModeView_Day_5_en.png | 4 ++-- .../features.login.impl.login_LoginModeView_Day_6_en.png | 3 +++ .../features.login.impl.login_LoginModeView_Night_5_en.png | 4 ++-- .../features.login.impl.login_LoginModeView_Night_6_en.png | 3 +++ 4 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Day_6_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Night_6_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Day_5_en.png index 2e7635cfdb..1125024a5a 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4eaf3d650155e9a779cf796cef116eaba0f1d6722b229332b224475393a88178 -size 16828 +oid sha256:ae6d230961018f1128ea655529a4287dbe3bbbc2762079d0c4f335313d2f2ab0 +size 25166 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Day_6_en.png new file mode 100644 index 0000000000..2e7635cfdb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Day_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4eaf3d650155e9a779cf796cef116eaba0f1d6722b229332b224475393a88178 +size 16828 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Night_5_en.png index 10ca16827e..777f0bb627 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:59f062f54833df71be9d7c4e785bb01013a10642e0d863bf7ef2abd8862b93c8 -size 15476 +oid sha256:1502711e6305f9adf4719b28c1fe3755089ae6c42c6c66e8e5bf73ab2bbb65c1 +size 23874 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Night_6_en.png new file mode 100644 index 0000000000..10ca16827e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Night_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59f062f54833df71be9d7c4e785bb01013a10642e0d863bf7ef2abd8862b93c8 +size 15476 From 6acd0ed687afe18537fd14baae8993dbd5d69d5a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Nov 2025 12:18:47 +0100 Subject: [PATCH 080/216] Update how eventSink is used in DefaultVoiceMessageComposerPresenter --- .../DefaultVoiceMessageComposerPresenter.kt | 55 ++++++++----------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenter.kt index 7c5eeb4969..68a95438db 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenter.kt @@ -68,7 +68,6 @@ class DefaultVoiceMessageComposerPresenter( } private val permissionsPresenter = permissionsPresenterFactory.create(Manifest.permission.RECORD_AUDIO) - private val mediaSender = mediaSenderFactory.create(timelineMode) @Composable @@ -88,7 +87,7 @@ class DefaultVoiceMessageComposerPresenter( player.setMedia(recording.file.path) } - val onLifecycleEvent = { event: Lifecycle.Event -> + fun handleLifecycleEvent(event: Lifecycle.Event) { when (event) { Lifecycle.Event.ON_PAUSE -> { sessionCoroutineScope.finishRecording() @@ -101,13 +100,12 @@ class DefaultVoiceMessageComposerPresenter( } } - val onVoiceMessageRecorderEvent = { event: VoiceMessageComposerEvents.RecorderEvent -> - val permissionGranted = permissionState.permissionGranted - when (event.recorderEvent) { + fun handleVoiceMessageRecorderEvent(event: VoiceMessageRecorderEvent) { + when (event) { VoiceMessageRecorderEvent.Start -> { Timber.v("Voice message record button pressed") when { - permissionGranted -> { + permissionState.permissionGranted -> { localCoroutineScope.startRecording() } else -> { @@ -126,7 +124,8 @@ class DefaultVoiceMessageComposerPresenter( } } } - val onPlayerEvent = { event: VoiceMessagePlayerEvent -> + + fun handleVoiceMessagePlayerEvent(event: VoiceMessagePlayerEvent) { localCoroutineScope.launch { when (event) { VoiceMessagePlayerEvent.Play -> player.play() @@ -136,28 +135,16 @@ class DefaultVoiceMessageComposerPresenter( } } - val onAcceptPermissionsRationale = { - permissionState.eventSink(PermissionsEvents.OpenSystemSettingAndCloseDialog) - } - - val onDismissPermissionsRationale = { - permissionState.eventSink(PermissionsEvents.CloseDialog) - } - - val onDismissSendFailureDialog = { - showSendFailureDialog = false - } - - val onSendButtonPress = lambda@{ + fun sendVoiceMessage() { val finishedState = recorderState as? VoiceRecorderState.Finished if (finishedState == null) { val exception = VoiceMessageException.FileException("No file to send") analyticsService.trackError(exception) Timber.e(exception) - return@lambda + return } if (isSending) { - return@lambda + return } isSending = true player.pause() @@ -176,21 +163,27 @@ class DefaultVoiceMessageComposerPresenter( } } - val handleEvents: (VoiceMessageComposerEvents) -> Unit = { event -> + fun handleEvent(event: VoiceMessageComposerEvents) { when (event) { - is VoiceMessageComposerEvents.RecorderEvent -> onVoiceMessageRecorderEvent(event) - is VoiceMessageComposerEvents.PlayerEvent -> onPlayerEvent(event.playerEvent) + is VoiceMessageComposerEvents.RecorderEvent -> handleVoiceMessageRecorderEvent(event.recorderEvent) + is VoiceMessageComposerEvents.PlayerEvent -> handleVoiceMessagePlayerEvent(event.playerEvent) is VoiceMessageComposerEvents.SendVoiceMessage -> localCoroutineScope.launch { - onSendButtonPress() + sendVoiceMessage() } VoiceMessageComposerEvents.DeleteVoiceMessage -> { player.pause() localCoroutineScope.deleteRecording() } - VoiceMessageComposerEvents.DismissPermissionsRationale -> onDismissPermissionsRationale() - VoiceMessageComposerEvents.AcceptPermissionRationale -> onAcceptPermissionsRationale() - is VoiceMessageComposerEvents.LifecycleEvent -> onLifecycleEvent(event.event) - VoiceMessageComposerEvents.DismissSendFailureDialog -> onDismissSendFailureDialog() + VoiceMessageComposerEvents.DismissPermissionsRationale -> { + permissionState.eventSink(PermissionsEvents.CloseDialog) + } + VoiceMessageComposerEvents.AcceptPermissionRationale -> { + permissionState.eventSink(PermissionsEvents.OpenSystemSettingAndCloseDialog) + } + is VoiceMessageComposerEvents.LifecycleEvent -> handleLifecycleEvent(event.event) + VoiceMessageComposerEvents.DismissSendFailureDialog -> { + showSendFailureDialog = false + } } } @@ -211,7 +204,7 @@ class DefaultVoiceMessageComposerPresenter( showPermissionRationaleDialog = permissionState.showDialog, showSendFailureDialog = showSendFailureDialog, keepScreenOn = keepScreenOn, - eventSink = handleEvents, + eventSink = ::handleEvent, ) } From 7ad9c8f6877ed3e02cf1e3fca045ed3f7dc34c25 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Nov 2025 12:20:33 +0100 Subject: [PATCH 081/216] Rename handleEvents to handleEvent --- .../io/element/android/appnav/loggedin/LoggedInPresenter.kt | 2 +- .../features/analytics/impl/AnalyticsOptInPresenter.kt | 4 ++-- .../impl/preferences/AnalyticsPreferencesPresenter.kt | 4 ++-- .../announcement/impl/spaces/SpaceAnnouncementPresenter.kt | 4 ++-- .../android/features/call/impl/ui/CallScreenPresenter.kt | 4 ++-- .../createroom/impl/configureroom/ConfigureRoomPresenter.kt | 4 ++-- .../features/logout/impl/AccountDeactivationPresenter.kt | 4 ++-- .../android/features/forward/impl/ForwardMessagesPresenter.kt | 4 ++-- .../ftue/impl/notifications/NotificationsOptInPresenter.kt | 4 ++-- .../io/element/android/features/home/impl/HomePresenter.kt | 4 ++-- .../features/home/impl/filters/RoomListFiltersPresenter.kt | 4 ++-- .../android/features/home/impl/roomlist/RoomListPresenter.kt | 4 ++-- .../features/home/impl/search/RoomListSearchPresenter.kt | 4 ++-- .../android/features/home/impl/spaces/HomeSpacesPresenter.kt | 4 ++-- .../invite/impl/acceptdecline/AcceptDeclineInvitePresenter.kt | 4 ++-- .../invite/impl/declineandblock/DeclineAndBlockPresenter.kt | 4 ++-- .../invitepeople/impl/DefaultInvitePeoplePresenter.kt | 4 ++-- .../android/features/joinroom/impl/JoinRoomPresenter.kt | 4 ++-- .../knockrequests/impl/banner/KnockRequestsBannerPresenter.kt | 4 ++-- .../knockrequests/impl/list/KnockRequestsListPresenter.kt | 4 ++-- .../impl/common/permissions/DefaultPermissionsPresenter.kt | 4 ++-- .../features/location/impl/send/SendLocationPresenter.kt | 4 ++-- .../features/location/impl/show/ShowLocationPresenter.kt | 4 ++-- .../impl/common/permissions/FakePermissionsPresenter.kt | 2 +- .../lockscreen/impl/settings/LockScreenSettingsPresenter.kt | 4 ++-- .../impl/setup/biometric/SetupBiometricPresenter.kt | 4 ++-- .../features/lockscreen/impl/setup/pin/SetupPinPresenter.kt | 4 ++-- .../features/lockscreen/impl/unlock/PinUnlockPresenter.kt | 4 ++-- .../features/login/impl/changeserver/ChangeServerPresenter.kt | 4 ++-- .../confirmaccountprovider/ConfirmAccountProviderPresenter.kt | 4 ++-- .../impl/screens/createaccount/CreateAccountPresenter.kt | 4 ++-- .../impl/screens/loginpassword/LoginPasswordPresenter.kt | 4 ++-- .../login/impl/screens/qrcode/intro/QrCodeIntroPresenter.kt | 4 ++-- .../login/impl/screens/qrcode/scan/QrCodeScanPresenter.kt | 4 ++-- .../searchaccountprovider/SearchAccountProviderPresenter.kt | 4 ++-- .../element/android/features/logout/impl/LogoutPresenter.kt | 4 ++-- .../features/logout/impl/direct/DirectLogoutPresenter.kt | 4 ++-- .../android/features/messages/impl/MessagesPresenter.kt | 2 +- .../features/messages/impl/actionlist/ActionListPresenter.kt | 4 ++-- .../impl/attachments/preview/AttachmentsPreviewPresenter.kt | 4 ++-- .../resolve/ResolveVerifiedUserSendFailurePresenter.kt | 4 ++-- .../android/features/messages/impl/link/LinkPresenter.kt | 4 ++-- .../messages/impl/messagecomposer/MessageComposerPresenter.kt | 4 ++-- .../impl/pinned/banner/PinnedMessagesBannerPresenter.kt | 2 +- .../messages/impl/pinned/list/PinnedMessagesListPresenter.kt | 4 ++-- .../features/messages/impl/report/ReportMessagePresenter.kt | 4 ++-- .../features/messages/impl/timeline/TimelinePresenter.kt | 4 ++-- .../components/customreaction/CustomReactionPresenter.kt | 4 ++-- .../components/customreaction/picker/EmojiPickerPresenter.kt | 4 ++-- .../components/reactionsummary/ReactionSummaryPresenter.kt | 4 ++-- .../android/features/poll/impl/create/CreatePollPresenter.kt | 4 ++-- .../features/poll/impl/history/PollHistoryPresenter.kt | 4 ++-- .../preferences/impl/advanced/AdvancedSettingsPresenter.kt | 4 ++-- .../preferences/impl/blockedusers/BlockedUsersPresenter.kt | 4 ++-- .../preferences/impl/developer/DeveloperSettingsPresenter.kt | 4 ++-- .../impl/notifications/NotificationSettingsPresenter.kt | 4 ++-- .../edit/EditDefaultNotificationSettingPresenter.kt | 4 ++-- .../impl/user/editprofile/EditUserProfilePresenter.kt | 4 ++-- .../features/rageshake/impl/bugreport/BugReportPresenter.kt | 4 ++-- .../rageshake/impl/crash/DefaultCrashDetectionPresenter.kt | 4 ++-- .../impl/detection/DefaultRageshakeDetectionPresenter.kt | 4 ++-- .../impl/preferences/DefaultRageshakePreferencesPresenter.kt | 4 ++-- .../android/features/reportroom/impl/ReportRoomPresenter.kt | 4 ++-- .../roomaliasresolver/impl/RoomAliasResolverPresenter.kt | 4 ++-- .../android/features/roomdetails/impl/RoomDetailsPresenter.kt | 4 ++-- .../roomdetails/impl/edit/RoomDetailsEditPresenter.kt | 4 ++-- .../roomdetails/impl/members/RoomMemberListPresenter.kt | 4 ++-- .../notificationsettings/RoomNotificationSettingsPresenter.kt | 4 ++-- .../impl/securityandprivacy/SecurityAndPrivacyPresenter.kt | 4 ++-- .../editroomaddress/EditRoomAddressPresenter.kt | 4 ++-- .../roomdirectory/impl/root/RoomDirectoryPresenter.kt | 4 ++-- .../securebackup/impl/disable/SecureBackupDisablePresenter.kt | 4 ++-- .../impl/enter/SecureBackupEnterRecoveryKeyPresenter.kt | 4 ++-- .../impl/reset/password/ResetIdentityPasswordPresenter.kt | 2 +- .../impl/reset/root/ResetIdentityRootPresenter.kt | 2 +- .../securebackup/impl/root/SecureBackupRootPresenter.kt | 4 ++-- .../securebackup/impl/setup/SecureBackupSetupPresenter.kt | 4 ++-- .../io/element/android/features/share/impl/SharePresenter.kt | 4 ++-- .../android/features/signedout/impl/SignedOutPresenter.kt | 4 ++-- .../android/features/space/impl/leave/LeaveSpacePresenter.kt | 4 ++-- .../android/features/space/impl/root/SpacePresenter.kt | 4 ++-- .../impl/joinbyaddress/JoinRoomByAddressPresenter.kt | 4 ++-- .../features/startchat/impl/root/StartChatPresenter.kt | 4 ++-- .../features/userprofile/impl/root/UserProfilePresenter.kt | 4 ++-- .../impl/incoming/IncomingVerificationPresenter.kt | 4 ++-- .../impl/outgoing/OutgoingVerificationPresenter.kt | 4 ++-- .../impl/FullScreenIntentPermissionsPresenter.kt | 4 ++-- .../mediaviewer/impl/gallery/MediaGalleryPresenter.kt | 4 ++-- .../libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt | 4 ++-- .../libraries/permissions/impl/DefaultPermissionsPresenter.kt | 4 ++-- .../push/impl/battery/BatteryOptimizationPresenter.kt | 4 ++-- .../android/libraries/roomselect/impl/RoomSelectPresenter.kt | 4 ++-- .../troubleshoot/impl/TroubleshootNotificationsPresenter.kt | 4 ++-- .../troubleshoot/impl/history/PushHistoryPresenter.kt | 4 ++-- 94 files changed, 182 insertions(+), 182 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt index 934803fe26..61f1ce715c 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt @@ -122,7 +122,7 @@ class LoggedInPresenter( ignoreRegistrationError = ignoreRegistrationError, forceNativeSlidingSyncMigration = forceNativeSlidingSyncMigration, appName = buildMeta.applicationName, - eventSink = ::handleEvent + eventSink = ::handleEvent, ) } diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenter.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenter.kt index fd590955c8..718d90cfd8 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenter.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenter.kt @@ -27,7 +27,7 @@ class AnalyticsOptInPresenter( override fun present(): AnalyticsOptInState { val localCoroutineScope = rememberCoroutineScope() - fun handleEvents(event: AnalyticsOptInEvents) { + fun handleEvent(event: AnalyticsOptInEvents) { when (event) { is AnalyticsOptInEvents.EnableAnalytics -> localCoroutineScope.setIsEnabled(event.isEnabled) } @@ -39,7 +39,7 @@ class AnalyticsOptInPresenter( return AnalyticsOptInState( applicationName = buildMeta.applicationName, hasPolicyLink = AnalyticsConfig.POLICY_LINK.isNotEmpty(), - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenter.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenter.kt index fee6188d23..3e7d5aa305 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenter.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenter.kt @@ -30,7 +30,7 @@ class AnalyticsPreferencesPresenter( val localCoroutineScope = rememberCoroutineScope() val isEnabled = analyticsService.userConsentFlow.collectAsState(initial = false) - fun handleEvents(event: AnalyticsOptInEvents) { + fun handleEvent(event: AnalyticsOptInEvents) { when (event) { is AnalyticsOptInEvents.EnableAnalytics -> localCoroutineScope.setIsEnabled(event.isEnabled) } @@ -40,7 +40,7 @@ class AnalyticsPreferencesPresenter( applicationName = buildMeta.applicationName, isEnabled = isEnabled.value, policyUrl = AnalyticsConfig.POLICY_LINK, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementPresenter.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementPresenter.kt index 4d1b2f3e4b..428421ab3e 100644 --- a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementPresenter.kt +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/spaces/SpaceAnnouncementPresenter.kt @@ -24,7 +24,7 @@ class SpaceAnnouncementPresenter( override fun present(): SpaceAnnouncementState { val localCoroutineScope = rememberCoroutineScope() - fun handleEvents(event: SpaceAnnouncementEvents) { + fun handleEvent(event: SpaceAnnouncementEvents) { when (event) { SpaceAnnouncementEvents.Continue -> localCoroutineScope.launch { announcementStore.setAnnouncementStatus(Announcement.Space, AnnouncementStatus.Shown) @@ -33,7 +33,7 @@ class SpaceAnnouncementPresenter( } return SpaceAnnouncementState( - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } } diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt index a7302c9730..9a3637c997 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt @@ -161,7 +161,7 @@ class CallScreenPresenter( } } - fun handleEvents(event: CallScreenEvents) { + fun handleEvent(event: CallScreenEvents) { when (event) { is CallScreenEvents.Hangup -> { val widgetId = callWidgetDriver.value?.id @@ -201,7 +201,7 @@ class CallScreenPresenter( userAgent = userAgent, isCallActive = isWidgetLoaded, isInWidgetMode = isInWidgetMode, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index ae28b95bba..9784a8c700 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -116,7 +116,7 @@ class ConfigureRoomPresenter( localCoroutineScope.createRoom(config, createRoomAction) } - fun handleEvents(event: ConfigureRoomEvents) { + fun handleEvent(event: ConfigureRoomEvents) { when (event) { is ConfigureRoomEvents.RoomNameChanged -> dataStore.setRoomName(event.name) is ConfigureRoomEvents.TopicChanged -> dataStore.setTopic(event.topic) @@ -149,7 +149,7 @@ class ConfigureRoomPresenter( cameraPermissionState = cameraPermissionState, homeserverName = homeserverName, roomAddressValidity = roomAddressValidity.value, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenter.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenter.kt index eb751366a8..4fd1320842 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenter.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenter.kt @@ -33,7 +33,7 @@ class AccountDeactivationPresenter( val formState = remember { mutableStateOf(DeactivateFormState.Default) } - fun handleEvents(event: AccountDeactivationEvents) { + fun handleEvent(event: AccountDeactivationEvents) { when (event) { is AccountDeactivationEvents.SetEraseData -> { updateFormState(formState) { @@ -63,7 +63,7 @@ class AccountDeactivationPresenter( return AccountDeactivationState( deactivateFormState = formState.value, accountDeactivationAction = action.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenter.kt b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenter.kt index b1e9534533..002fbd8ebd 100644 --- a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenter.kt +++ b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenter.kt @@ -47,7 +47,7 @@ class ForwardMessagesPresenter( @Composable override fun present(): ForwardMessagesState { - fun handleEvents(event: ForwardMessagesEvents) { + fun handleEvent(event: ForwardMessagesEvents) { when (event) { ForwardMessagesEvents.ClearError -> forwardingActionState.value = AsyncAction.Uninitialized } @@ -55,7 +55,7 @@ class ForwardMessagesPresenter( return ForwardMessagesState( forwardAction = forwardingActionState.value, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenter.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenter.kt index b6f5d76351..fc8a392a9b 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenter.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenter.kt @@ -51,7 +51,7 @@ class NotificationsOptInPresenter( override fun present(): NotificationsOptInState { val notificationsPermissionsState = postNotificationPermissionsPresenter.present() - fun handleEvents(event: NotificationsOptInEvents) { + fun handleEvent(event: NotificationsOptInEvents) { when (event) { NotificationsOptInEvents.ContinueClicked -> { if (notificationsPermissionsState.permissionGranted) { @@ -78,7 +78,7 @@ class NotificationsOptInPresenter( return NotificationsOptInState( notificationsPermissionState = notificationsPermissionsState, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt index e3ca9612d1..6e98297a9a 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt @@ -85,7 +85,7 @@ class HomePresenter( val showAvatarIndicator by indicatorService.showRoomListTopBarIndicator() val directLogoutState = logoutPresenter.present() - fun handleEvents(event: HomeEvents) { + fun handleEvent(event: HomeEvents) { when (event) { is HomeEvents.SelectHomeNavigationBarItem -> coroutineState.launch { if (event.item == HomeNavigationBarItem.Spaces) { @@ -117,7 +117,7 @@ class HomePresenter( canReportBug = canReportBug, directLogoutState = directLogoutState, isSpaceFeatureEnabled = isSpaceFeatureEnabled, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenter.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenter.kt index 08c34d60ff..03b07306bc 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenter.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenter.kt @@ -27,7 +27,7 @@ class RoomListFiltersPresenter( @Composable override fun present(): RoomListFiltersState { - fun handleEvents(event: RoomListFiltersEvents) { + fun handleEvent(event: RoomListFiltersEvents) { when (event) { RoomListFiltersEvents.ClearSelectedFilters -> { filterSelectionStrategy.clear() @@ -63,7 +63,7 @@ class RoomListFiltersPresenter( return RoomListFiltersState( filterSelectionStates = filters, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenter.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenter.kt index 65d4eb297d..0d6d5eb366 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenter.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenter.kt @@ -113,7 +113,7 @@ class RoomListPresenter( val contextMenu = remember { mutableStateOf(RoomListState.ContextMenu.Hidden) } val declineInviteMenu = remember { mutableStateOf(RoomListState.DeclineInviteMenu.Hidden) } - fun handleEvents(event: RoomListEvents) { + fun handleEvent(event: RoomListEvents) { when (event) { is RoomListEvents.UpdateVisibleRange -> coroutineScope.launch { updateVisibleRange(event.range) @@ -169,7 +169,7 @@ class RoomListPresenter( acceptDeclineInviteState = acceptDeclineInviteState, hideInvitesAvatars = hideInvitesAvatar, canReportRoom = canReportRoom, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchPresenter.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchPresenter.kt index ba77b0cdce..a62a987a22 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchPresenter.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchPresenter.kt @@ -40,7 +40,7 @@ class RoomListSearchPresenter( dataSource.setSearchQuery(searchQuery) } - fun handleEvents(event: RoomListSearchEvents) { + fun handleEvent(event: RoomListSearchEvents) { when (event) { RoomListSearchEvents.ClearQuery -> { searchQuery = "" @@ -61,7 +61,7 @@ class RoomListSearchPresenter( isSearchActive = isSearchActive, query = searchQuery, results = searchResults, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesPresenter.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesPresenter.kt index b4a2fc9339..274ee3da44 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesPresenter.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesPresenter.kt @@ -38,7 +38,7 @@ class HomeSpacesPresenter( seenInvitesStore.seenRoomIds().map { it.toImmutableSet() } }.collectAsState(persistentSetOf()) - fun handleEvents(event: HomeSpacesEvents) { + fun handleEvent(event: HomeSpacesEvents) { // when (event) { } } @@ -47,7 +47,7 @@ class HomeSpacesPresenter( spaceRooms = spaceRooms, seenSpaceInvites = seenSpaceInvites, hideInvitesAvatar = hideInvitesAvatar, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } } diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenter.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenter.kt index 7d15971fe4..6fbe32d2ec 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenter.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/acceptdecline/AcceptDeclineInvitePresenter.kt @@ -39,7 +39,7 @@ class AcceptDeclineInvitePresenter( val declinedAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } - fun handleEvents(event: AcceptDeclineInviteEvents) { + fun handleEvent(event: AcceptDeclineInviteEvents) { when (event) { is AcceptDeclineInviteEvents.AcceptInvite -> { localCoroutineScope.acceptInvite(event.invite.roomId, acceptedAction) @@ -70,7 +70,7 @@ class AcceptDeclineInvitePresenter( return AcceptDeclineInviteState( acceptAction = acceptedAction.value, declineAction = declinedAction.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockPresenter.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockPresenter.kt index 59812a5e04..6cc035f791 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockPresenter.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockPresenter.kt @@ -48,7 +48,7 @@ class DeclineAndBlockPresenter( val coroutineScope = rememberCoroutineScope() - fun handleEvents(event: DeclineAndBlockEvents) { + fun handleEvent(event: DeclineAndBlockEvents) { when (event) { DeclineAndBlockEvents.ClearDeclineAction -> declineAction.value = AsyncAction.Uninitialized DeclineAndBlockEvents.Decline -> coroutineScope.decline(reportReason, blockUser, reportRoom, declineAction) @@ -63,7 +63,7 @@ class DeclineAndBlockPresenter( reportReason = reportReason, blockUser = blockUser, declineAction = declineAction.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenter.kt b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenter.kt index 5e2b00a3f1..a77d8e54d4 100644 --- a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenter.kt +++ b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenter.kt @@ -103,7 +103,7 @@ class DefaultInvitePeoplePresenter( ) } - fun handleEvents(event: InvitePeopleEvents) { + fun handleEvent(event: InvitePeopleEvents) { when (event) { is DefaultInvitePeopleEvents.OnSearchActiveChanged -> { searchActive = event.active @@ -139,7 +139,7 @@ class DefaultInvitePeoplePresenter( searchResults = searchResults.value, showSearchLoader = showSearchLoader.value, sendInvitesAction = sendInvitesAction.value, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt index 2d737a17d4..e851d731e3 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt @@ -151,7 +151,7 @@ class JoinRoomPresenter( contentState.markRoomInviteAsSeen() } - fun handleEvents(event: JoinRoomEvents) { + fun handleEvent(event: JoinRoomEvents) { when (event) { JoinRoomEvents.JoinRoom -> coroutineScope.joinRoom(joinAction) is JoinRoomEvents.KnockRoom -> coroutineScope.knockRoom(knockAction, knockMessage) @@ -197,7 +197,7 @@ class JoinRoomPresenter( knockMessage = knockMessage, hideInviteAvatars = hideInviteAvatars, canReportRoom = canReportRoom, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } 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 index a1db930500..7a81e98d55 100644 --- 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 @@ -52,7 +52,7 @@ class KnockRequestsBannerPresenter( } } - fun handleEvents(event: KnockRequestsBannerEvents) { + fun handleEvent(event: KnockRequestsBannerEvents) { when (event) { is KnockRequestsBannerEvents.AcceptSingleRequest -> { sessionCoroutineScope.acceptSingleKnockRequest( @@ -73,7 +73,7 @@ class KnockRequestsBannerPresenter( displayAcceptError = showAcceptError.value, canAccept = permissions.canAccept, isVisible = shouldShowBanner, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListPresenter.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListPresenter.kt index ff943fcf6b..54d0a490c8 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListPresenter.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListPresenter.kt @@ -38,7 +38,7 @@ class KnockRequestsListPresenter( val coroutineScope = rememberCoroutineScope() - fun handleEvents(event: KnockRequestsListEvents) { + fun handleEvent(event: KnockRequestsListEvents) { when (event) { KnockRequestsListEvents.AcceptAll -> { currentAction = KnockRequestsAction.AcceptAll @@ -73,7 +73,7 @@ class KnockRequestsListPresenter( currentAction = currentAction, permissions = permissions, asyncAction = asyncAction.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/DefaultPermissionsPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/DefaultPermissionsPresenter.kt index 0ef520aadb..ebb5a6211c 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/DefaultPermissionsPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/DefaultPermissionsPresenter.kt @@ -33,7 +33,7 @@ class DefaultPermissionsPresenter( override fun present(): PermissionsState { val multiplePermissionsState = rememberMultiplePermissionsState(permissions = permissions) - fun handleEvents(event: PermissionsEvents) { + fun handleEvent(event: PermissionsEvents) { when (event) { PermissionsEvents.RequestPermissions -> multiplePermissionsState.launchMultiplePermissionRequest() } @@ -46,7 +46,7 @@ class DefaultPermissionsPresenter( else -> PermissionsState.Permissions.NoneGranted }, shouldShowRationale = multiplePermissionsState.shouldShowRationale, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt index 9914920ac3..96fdc6aea5 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt @@ -78,7 +78,7 @@ class SendLocationPresenter( } } - fun handleEvents(event: SendLocationEvents) { + fun handleEvent(event: SendLocationEvents) { when (event) { is SendLocationEvents.SendLocation -> scope.launch { sendLocation(event, mode) @@ -103,7 +103,7 @@ class SendLocationPresenter( mode = mode, hasLocationPermission = permissionsState.isAnyGranted, appName = appName, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt index 7929843571..c7c28472df 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt @@ -56,7 +56,7 @@ class ShowLocationPresenter( } } - fun handleEvents(event: ShowLocationEvents) { + fun handleEvent(event: ShowLocationEvents) { when (event) { ShowLocationEvents.Share -> locationActions.share(location, description) is ShowLocationEvents.TrackMyLocation -> { @@ -86,7 +86,7 @@ class ShowLocationPresenter( hasLocationPermission = permissionsState.isAnyGranted, isTrackMyLocation = isTrackMyLocation, appName = appName, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } } diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/FakePermissionsPresenter.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/FakePermissionsPresenter.kt index 7351dd71f4..7443655a95 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/FakePermissionsPresenter.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/FakePermissionsPresenter.kt @@ -19,7 +19,7 @@ class FakePermissionsPresenter : PermissionsPresenter { private var state = PermissionsState( permissions = PermissionsState.Permissions.NoneGranted, shouldShowRationale = false, - eventSink = ::handleEvent + eventSink = ::handleEvent, ) set(value) { field = value.copy(eventSink = ::handleEvent) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenter.kt index 6f238c964b..58f190d103 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenter.kt @@ -50,7 +50,7 @@ class LockScreenSettingsPresenter( val biometricUnlock = biometricAuthenticatorManager.rememberConfirmBiometricAuthenticator() - fun handleEvents(event: LockScreenSettingsEvents) { + fun handleEvent(event: LockScreenSettingsEvents) { when (event) { LockScreenSettingsEvents.CancelRemovePin -> showRemovePinConfirmation = false LockScreenSettingsEvents.ConfirmRemovePin -> { @@ -82,7 +82,7 @@ class LockScreenSettingsPresenter( isBiometricEnabled = isBiometricEnabled, showRemovePinConfirmation = showRemovePinConfirmation, showToggleBiometric = biometricAuthenticatorManager.isDeviceSecured, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricPresenter.kt index f1183d8541..19b1bb2956 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricPresenter.kt @@ -34,7 +34,7 @@ class SetupBiometricPresenter( val coroutineScope = rememberCoroutineScope() val biometricUnlock = biometricAuthenticatorManager.rememberConfirmBiometricAuthenticator() - fun handleEvents(event: SetupBiometricEvents) { + fun handleEvent(event: SetupBiometricEvents) { when (event) { SetupBiometricEvents.AllowBiometric -> coroutineScope.launch { biometricUnlock.setup() @@ -52,7 +52,7 @@ class SetupBiometricPresenter( return SetupBiometricState( isBiometricSetupDone = isBiometricSetupDone, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinPresenter.kt index 36065484c7..24cd2b59a0 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinPresenter.kt @@ -73,7 +73,7 @@ class SetupPinPresenter( } } - fun handleEvents(event: SetupPinEvents) { + fun handleEvent(event: SetupPinEvents) { when (event) { is SetupPinEvents.OnPinEntryChanged -> { // Use the fromConfirmationStep flag from ui to avoid race condition. @@ -106,7 +106,7 @@ class SetupPinPresenter( isConfirmationStep = isConfirmationStep, setupPinFailure = setupPinFailure, appName = buildMeta.applicationName, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt index fc2e61d404..352f3f8055 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt @@ -94,7 +94,7 @@ class PinUnlockPresenter( isUnlocked.value = true } - fun handleEvents(event: PinUnlockEvents) { + fun handleEvent(event: PinUnlockEvents) { when (event) { is PinUnlockEvents.OnPinKeypadPressed -> { pinEntryState.value = pinEntry.process(event.pinKeypadModel) @@ -129,7 +129,7 @@ class PinUnlockPresenter( showBiometricUnlock = biometricUnlock.isActive, biometricUnlockResult = biometricUnlockResult, isUnlocked = isUnlocked.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt index 4df9eb12d5..178dff1062 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt @@ -38,7 +38,7 @@ class ChangeServerPresenter( mutableStateOf(AsyncData.Uninitialized) } - fun handleEvents(event: ChangeServerEvents) { + fun handleEvent(event: ChangeServerEvents) { when (event) { is ChangeServerEvents.ChangeServer -> localCoroutineScope.changeServer(event.accountProvider, changeServerAction) ChangeServerEvents.ClearError -> changeServerAction.value = AsyncData.Uninitialized @@ -47,7 +47,7 @@ class ChangeServerPresenter( return ChangeServerState( changeServerAction = changeServerAction.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt index d485755afb..8451703ea0 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt @@ -40,7 +40,7 @@ class ConfirmAccountProviderPresenter( val loginMode by loginHelper.collectLoginMode() - fun handleEvents(event: ConfirmAccountProviderEvents) { + fun handleEvent(event: ConfirmAccountProviderEvents) { when (event) { ConfirmAccountProviderEvents.Continue -> { loginHelper.submit( @@ -58,7 +58,7 @@ class ConfirmAccountProviderPresenter( accountProvider = accountProvider, isAccountCreation = params.isAccountCreation, loginMode = loginMode, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt index a3aec0eb81..5a0a5cff64 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/CreateAccountPresenter.kt @@ -50,7 +50,7 @@ class CreateAccountPresenter( val pageProgress: MutableState = remember { mutableIntStateOf(0) } val createAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } - fun handleEvents(event: CreateAccountEvents) { + fun handleEvent(event: CreateAccountEvents) { when (event) { is CreateAccountEvents.SetPageProgress -> { pageProgress.value = event.progress @@ -68,7 +68,7 @@ class CreateAccountPresenter( pageProgress = pageProgress.value, isDebugBuild = buildMeta.isDebuggable, createAction = createAction.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenter.kt index 80a5711d52..7ea743ed61 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenter.kt @@ -41,7 +41,7 @@ class LoginPasswordPresenter( } val accountProvider by accountProviderDataSource.flow.collectAsState() - fun handleEvents(event: LoginPasswordEvents) { + fun handleEvent(event: LoginPasswordEvents) { when (event) { is LoginPasswordEvents.SetLogin -> updateFormState(formState) { copy(login = event.login) @@ -60,7 +60,7 @@ class LoginPasswordPresenter( accountProvider = accountProvider, formState = formState.value, loginAction = loginAction.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroPresenter.kt index b90e2a6aeb..6c8af76bfb 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroPresenter.kt @@ -39,7 +39,7 @@ class QrCodeIntroPresenter( } } - fun handleEvents(event: QrCodeIntroEvents) { + fun handleEvent(event: QrCodeIntroEvents) { when (event) { QrCodeIntroEvents.Continue -> if (cameraPermissionState.permissionGranted) { canContinue = true @@ -55,7 +55,7 @@ class QrCodeIntroPresenter( desktopAppName = buildMeta.desktopApplicationName, cameraPermissionState = cameraPermissionState, canContinue = canContinue, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanPresenter.kt index ed612f2ac3..df206fe7ce 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanPresenter.kt @@ -53,7 +53,7 @@ class QrCodeScanPresenter( authenticationAction.value = AsyncAction.Failure(it) } - fun handleEvents(event: QrCodeScanEvents) { + fun handleEvent(event: QrCodeScanEvents) { when (event) { QrCodeScanEvents.TryAgain -> { isScanning = true @@ -69,7 +69,7 @@ class QrCodeScanPresenter( return QrCodeScanState( isScanning = isScanning, authenticationAction = authenticationAction.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenter.kt index 657a21111c..6869aef2a1 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenter.kt @@ -45,7 +45,7 @@ class SearchAccountProviderPresenter( onUserInput(userInput, data) } - fun handleEvents(event: SearchAccountProviderEvents) { + fun handleEvent(event: SearchAccountProviderEvents) { when (event) { is SearchAccountProviderEvents.UserInput -> { userInput = event.input @@ -57,7 +57,7 @@ class SearchAccountProviderPresenter( userInput = userInput, userInputResult = data.value, changeServerState = changeServerState, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt index 9f3b379372..1c7f7b5879 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt @@ -73,7 +73,7 @@ class LogoutPresenter( } } - fun handleEvents(event: LogoutEvents) { + fun handleEvent(event: LogoutEvents) { when (event) { is LogoutEvents.Logout -> { if (logoutAction.value.isConfirming() || event.ignoreSdkError) { @@ -96,7 +96,7 @@ class LogoutPresenter( backupUploadState = backupUploadState, waitingForALongTime = waitingForALongTime, logoutAction = logoutAction.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenter.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenter.kt index 1e74aa2bc7..bb4c9802bf 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenter.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DirectLogoutPresenter.kt @@ -47,7 +47,7 @@ class DirectLogoutPresenter( val isLastDevice by encryptionService.isLastDevice.collectAsState() - fun handleEvents(event: DirectLogoutEvents) { + fun handleEvent(event: DirectLogoutEvents) { when (event) { is DirectLogoutEvents.Logout -> { if (logoutAction.value.isConfirming() || event.ignoreSdkError) { @@ -66,7 +66,7 @@ class DirectLogoutPresenter( canDoDirectSignOut = !isLastDevice && !backupUploadState.isBackingUp(), logoutAction = logoutAction.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index acab975a45..39f9a07faa 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -221,7 +221,7 @@ class MessagesPresenter( onPauseOrDispose {} } - fun handleEvents(event: MessagesEvents) { + fun handleEvent(event: MessagesEvents) { when (event) { is MessagesEvents.HandleAction -> { localCoroutineScope.handleTimelineAction( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt index 370b3853ea..f53d8afed4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt @@ -106,7 +106,7 @@ class DefaultActionListPresenter( val isThreadsEnabled = featureFlagService.isFeatureEnabledFlow(FeatureFlags.Threads).collectAsState(false) - fun handleEvents(event: ActionListEvents) { + fun handleEvent(event: ActionListEvents) { when (event) { ActionListEvents.Clear -> target.value = ActionListState.Target.None is ActionListEvents.ComputeForMessage -> localCoroutineScope.computeForMessage( @@ -122,7 +122,7 @@ class DefaultActionListPresenter( return ActionListState( target = target.value, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt index 34a4f12fe5..a3662c9e12 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt @@ -141,7 +141,7 @@ class AttachmentsPreviewPresenter( } } - fun handleEvents(attachmentsPreviewEvents: AttachmentsPreviewEvents) { + fun handleEvent(attachmentsPreviewEvents: AttachmentsPreviewEvents) { when (attachmentsPreviewEvents) { is AttachmentsPreviewEvents.SendAttachment -> { ongoingSendAttachmentJob.value = coroutineScope.launch { @@ -230,7 +230,7 @@ class AttachmentsPreviewPresenter( textEditorState = textEditorState, mediaOptimizationSelectorState = mediaOptimizationSelectorState, displayFileTooLargeError = displayFileTooLargeError, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailurePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailurePresenter.kt index 54aa26e6ab..f0f8f965cd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailurePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailurePresenter.kt @@ -47,7 +47,7 @@ class ResolveVerifiedUserSendFailurePresenter( } val coroutineScope = rememberCoroutineScope() - fun handleEvents(event: ResolveVerifiedUserSendFailureEvents) { + fun handleEvent(event: ResolveVerifiedUserSendFailureEvents) { when (event) { is ResolveVerifiedUserSendFailureEvents.ComputeForMessage -> { val sendState = event.messageEvent.localSendState as? LocalEventSendState.Failed.VerifiedUser @@ -92,7 +92,7 @@ class ResolveVerifiedUserSendFailurePresenter( verifiedUserSendFailure = verifiedUserSendFailure, resolveAction = resolveAction.value, retryAction = retryAction.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/LinkPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/LinkPresenter.kt index 9c4694bafa..a0aa62da56 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/LinkPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/LinkPresenter.kt @@ -24,7 +24,7 @@ class LinkPresenter( override fun present(): LinkState { val linkClick: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } - fun handleEvents(linkEvents: LinkEvents) { + fun handleEvent(linkEvents: LinkEvents) { when (linkEvents) { is LinkEvents.OnLinkClick -> { linkClick.value = AsyncAction.Loading @@ -48,7 +48,7 @@ class LinkPresenter( } return LinkState( linkClick = linkClick.value, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index f7dd029826..9ab40349cf 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -227,7 +227,7 @@ class MessageComposerPresenter( } } - fun handleEvents(event: MessageComposerEvents) { + fun handleEvent(event: MessageComposerEvents) { when (event) { MessageComposerEvents.ToggleFullScreenState -> isFullScreen.value = !isFullScreen.value MessageComposerEvents.CloseSpecialMode -> { @@ -382,7 +382,7 @@ class MessageComposerPresenter( suggestions = suggestions.toImmutableList(), resolveMentionDisplay = resolveMentionDisplay, resolveAtRoomMentionDisplay = resolveAtRoomMentionDisplay, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenter.kt index c0f2cccb6f..193fa10539 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenter.kt @@ -70,7 +70,7 @@ class PinnedMessagesBannerPresenter( expectedPinnedMessagesCount = expectedPinnedMessagesCount, pinnedItems = pinnedItems.value, currentPinnedMessageIndex = currentPinnedMessageIndex, - eventSink = ::handleEvent + eventSink = ::handleEvent, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt index 764286ce2e..2c9fb05ef7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt @@ -130,7 +130,7 @@ class PinnedMessagesListPresenter( } ) - fun handleEvents(event: PinnedMessagesListEvents) { + fun handleEvent(event: PinnedMessagesListEvents) { when (event) { is PinnedMessagesListEvents.HandleAction -> sessionCoroutineScope.handleTimelineAction(event.action, event.event) } @@ -143,7 +143,7 @@ class PinnedMessagesListPresenter( displayThreadSummaries = displayThreadSummaries, userEventPermissions = userEventPermissions, timelineItems = pinnedMessageItems, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessagePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessagePresenter.kt index 90b6585718..efdb4f3895 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessagePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessagePresenter.kt @@ -53,7 +53,7 @@ class ReportMessagePresenter( var blockUser by rememberSaveable { mutableStateOf(false) } var result: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } - fun handleEvents(event: ReportMessageEvents) { + fun handleEvent(event: ReportMessageEvents) { when (event) { is ReportMessageEvents.UpdateReason -> reason = event.reason ReportMessageEvents.ToggleBlockUser -> blockUser = !blockUser @@ -66,7 +66,7 @@ class ReportMessagePresenter( reason = reason, blockUser = blockUser, result = result.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index 6f9dab56c8..7607050580 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -140,7 +140,7 @@ class TimelinePresenter( value = featureFlagService.isFeatureEnabled(FeatureFlags.Threads) } - fun handleEvents(event: TimelineEvents) { + fun handleEvent(event: TimelineEvents) { when (event) { is TimelineEvents.LoadMore -> { if (event.direction == Timeline.PaginationDirection.FORWARDS && timelineMode is Timeline.Mode.Thread) { @@ -289,7 +289,7 @@ class TimelinePresenter( messageShield = messageShield.value, resolveVerifiedUserSendFailureState = resolveVerifiedUserSendFailureState, displayThreadSummaries = displayThreadSummaries, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt index bbddbb1066..a3502c8af0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt @@ -53,7 +53,7 @@ class CustomReactionPresenter( target.value = CustomReactionState.Target.None } - fun handleEvents(event: CustomReactionEvents) { + fun handleEvent(event: CustomReactionEvents) { when (event) { is CustomReactionEvents.ShowCustomReactionSheet -> handleShowCustomReactionSheet(event.event) is CustomReactionEvents.DismissCustomReactionSheet -> handleDismissCustomReactionSheet() @@ -71,7 +71,7 @@ class CustomReactionPresenter( target = target.value, selectedEmoji = selectedEmoji, recentEmojis = recentEmojis, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerPresenter.kt index ce9600b1f7..41a1d9f73c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/picker/EmojiPickerPresenter.kt @@ -90,7 +90,7 @@ class EmojiPickerPresenter( } val isInPreview = LocalInspectionMode.current - fun handleEvents(event: EmojiPickerEvents) { + fun handleEvent(event: EmojiPickerEvents) { when (event) { // For some reason, in preview mode the SearchBar emits this event with an `isActive = true` value automatically is EmojiPickerEvents.ToggleSearchActive -> if (!isInPreview) { @@ -106,7 +106,7 @@ class EmojiPickerPresenter( searchQuery = searchQuery, isSearchActive = isSearchActive, searchResults = emojiResults, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt index c3fb43d374..dd6ca16a08 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt @@ -36,7 +36,7 @@ class ReactionSummaryPresenter( } val targetWithAvatars = populateSenderAvatars(members = membersState.roomMembers().orEmpty().toImmutableList(), summary = target.value) - fun handleEvents(event: ReactionSummaryEvents) { + fun handleEvent(event: ReactionSummaryEvents) { when (event) { is ReactionSummaryEvents.ShowReactionSummary -> target.value = ReactionSummaryState.Summary( reactions = event.reactions.toImmutableList(), @@ -48,7 +48,7 @@ class ReactionSummaryPresenter( } return ReactionSummaryState( target = targetWithAvatars.value, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenter.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenter.kt index 5136571acd..6c8c04eb6a 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenter.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenter.kt @@ -96,7 +96,7 @@ class CreatePollPresenter( val scope = rememberCoroutineScope() - fun handleEvents(event: CreatePollEvents) { + fun handleEvent(event: CreatePollEvents) { when (event) { is CreatePollEvents.Save -> scope.launch { if (canSave) { @@ -183,7 +183,7 @@ class CreatePollPresenter( pollKind = poll.pollKind, showBackConfirmation = showBackConfirmation, showDeleteConfirmation = showDeleteConfirmation, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryPresenter.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryPresenter.kt index 15bc803f51..920b53eb41 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryPresenter.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryPresenter.kt @@ -62,7 +62,7 @@ class PollHistoryPresenter( } } val coroutineScope = rememberCoroutineScope() - fun handleEvents(event: PollHistoryEvents) { + fun handleEvent(event: PollHistoryEvents) { when (event) { is PollHistoryEvents.LoadMore -> { coroutineScope.loadMore(timeline) @@ -88,7 +88,7 @@ class PollHistoryPresenter( hasMoreToLoad = paginationState.hasMoreToLoad, pollHistoryItems = pollHistoryItems, activeFilter = activeFilter, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt index 6378b3038d..f7aa29cd70 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt @@ -82,7 +82,7 @@ class AdvancedSettingsPresenter( }.collect() } - fun handleEvents(event: AdvancedSettingsEvents) { + fun handleEvent(event: AdvancedSettingsEvents) { when (event) { is AdvancedSettingsEvents.SetDeveloperModeEnabled -> sessionCoroutineScope.launch { appPreferencesStore.setDeveloperModeEnabled(event.enabled) @@ -117,7 +117,7 @@ class AdvancedSettingsPresenter( mediaOptimizationState = mediaOptimizationState, theme = themeOption, mediaPreviewConfigState = mediaPreviewConfigState, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt index 5fbbe3e797..fb2c56f3fe 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt @@ -64,7 +64,7 @@ class BlockedUsersPresenter( } } - fun handleEvents(event: BlockedUsersEvents) { + fun handleEvent(event: BlockedUsersEvents) { when (event) { is BlockedUsersEvents.Unblock -> { pendingUserToUnblock = event.userId @@ -85,7 +85,7 @@ class BlockedUsersPresenter( return BlockedUsersState( blockedUsers = ignoredMatrixUser.toImmutableList(), unblockUserAction = unblockUserAction.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt index 1e68652e5c..618e63ccae 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt @@ -112,7 +112,7 @@ class DeveloperSettingsPresenter( computeCacheSize(cacheSize) } - fun handleEvents(event: DeveloperSettingsEvents) { + fun handleEvent(event: DeveloperSettingsEvents) { when (event) { is DeveloperSettingsEvents.UpdateEnabledFeature -> coroutineScope.updateEnabledFeature( enabledFeatures = enabledFeatures, @@ -161,7 +161,7 @@ class DeveloperSettingsPresenter( tracingLogPacks = tracingLogPacks, isEnterpriseBuild = enterpriseService.isEnterpriseBuild, showColorPicker = showColorPicker, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt index 00006d1086..e5d02611a5 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt @@ -129,7 +129,7 @@ class NotificationSettingsPresenter( ) } - fun handleEvents(event: NotificationSettingsEvents) { + fun handleEvent(event: NotificationSettingsEvents) { when (event) { is NotificationSettingsEvents.SetAtRoomNotificationsEnabled -> { localCoroutineScope.setAtRoomNotificationsEnabled(event.enabled, changeNotificationSettingAction) @@ -167,7 +167,7 @@ class NotificationSettingsPresenter( availablePushDistributors = availableDistributors, showChangePushProviderDialog = showChangePushProviderDialog, fullScreenIntentPermissionsState = key(refreshFullScreenIntentSettings) { fullScreenIntentPermissionsPresenter.present() }, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt index 25b062827f..c246d0c12c 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt @@ -70,7 +70,7 @@ class EditDefaultNotificationSettingPresenter( displayMentionsOnlyDisclaimer = !notificationSettingsService.canHomeServerPushEncryptedEventsToDevice().getOrDefault(true) } - fun handleEvents(event: EditDefaultNotificationSettingStateEvents) { + fun handleEvent(event: EditDefaultNotificationSettingStateEvents) { when (event) { is EditDefaultNotificationSettingStateEvents.SetNotificationMode -> { localCoroutineScope.setDefaultNotificationMode(event.mode, changeNotificationSettingAction) @@ -85,7 +85,7 @@ class EditDefaultNotificationSettingPresenter( roomsWithUserDefinedMode = roomsWithUserDefinedMode.value.toImmutableList(), changeNotificationSettingAction = changeNotificationSettingAction.value, displayMentionsOnlyDisclaimer = displayMentionsOnlyDisclaimer, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt index 3aa9c61555..e72c0a60e3 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt @@ -100,7 +100,7 @@ class EditUserProfilePresenter( val saveAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } val localCoroutineScope = rememberCoroutineScope() - fun handleEvents(event: EditUserProfileEvents) { + fun handleEvent(event: EditUserProfileEvents) { when (event) { is EditUserProfileEvents.Save -> localCoroutineScope.saveChanges( name = userDisplayName, @@ -143,7 +143,7 @@ class EditUserProfilePresenter( saveButtonEnabled = canSave && saveAction.value !is AsyncAction.Loading, saveAction = saveAction.value, cameraPermissionState = cameraPermissionState, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt index 790ecdd092..1a1a4649b2 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt @@ -82,7 +82,7 @@ class BugReportPresenter( } val uploadListener = BugReporterUploadListener(sendingProgress, sendingAction) - fun handleEvents(event: BugReportEvents) { + fun handleEvent(event: BugReportEvents) { when (event) { BugReportEvents.SendBugReport -> { if (formState.value.description.length < 10) { @@ -121,7 +121,7 @@ class BugReportPresenter( sending = sendingAction.value, formState = formState.value, screenshotUri = screenshotUri.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/DefaultCrashDetectionPresenter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/DefaultCrashDetectionPresenter.kt index 09080e5029..aca6d26297 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/DefaultCrashDetectionPresenter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/DefaultCrashDetectionPresenter.kt @@ -47,7 +47,7 @@ class DefaultCrashDetectionPresenter( } }.collectAsState(false) - fun handleEvents(event: CrashDetectionEvents) { + fun handleEvent(event: CrashDetectionEvents) { when (event) { CrashDetectionEvents.ResetAllCrashData -> localCoroutineScope.resetAll() CrashDetectionEvents.ResetAppHasCrashed -> localCoroutineScope.resetAppHasCrashed() @@ -57,7 +57,7 @@ class DefaultCrashDetectionPresenter( return CrashDetectionState( appName = buildMeta.applicationName, crashDetected = crashDetected, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/detection/DefaultRageshakeDetectionPresenter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/detection/DefaultRageshakeDetectionPresenter.kt index c6b02d2514..3ca14f102d 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/detection/DefaultRageshakeDetectionPresenter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/detection/DefaultRageshakeDetectionPresenter.kt @@ -48,7 +48,7 @@ class DefaultRageshakeDetectionPresenter( mutableStateOf(false) } - fun handleEvents(event: RageshakeDetectionEvents) { + fun handleEvent(event: RageshakeDetectionEvents) { when (event) { RageshakeDetectionEvents.Disable -> { preferencesState.eventSink(RageshakePreferencesEvents.SetIsEnabled(false)) @@ -67,7 +67,7 @@ class DefaultRageshakeDetectionPresenter( takeScreenshot = takeScreenshot.value, showDialog = showDialog.value, preferenceState = preferencesState, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/preferences/DefaultRageshakePreferencesPresenter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/preferences/DefaultRageshakePreferencesPresenter.kt index 00ca57ea0d..1c34e9400a 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/preferences/DefaultRageshakePreferencesPresenter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/preferences/DefaultRageshakePreferencesPresenter.kt @@ -47,7 +47,7 @@ class DefaultRageshakePreferencesPresenter( rageshakeDataStore.sensitivity() }.collectAsState(initial = 0f) - fun handleEvents(event: RageshakePreferencesEvents) { + fun handleEvent(event: RageshakePreferencesEvents) { when (event) { is RageshakePreferencesEvents.SetIsEnabled -> localCoroutineScope.setIsEnabled(event.isEnabled) is RageshakePreferencesEvents.SetSensitivity -> localCoroutineScope.setSensitivity(event.sensitivity) @@ -59,7 +59,7 @@ class DefaultRageshakePreferencesPresenter( isEnabled = isEnabled, isSupported = isSupported.value, sensitivity = sensitivity, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomPresenter.kt b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomPresenter.kt index 42ab1cf08a..8109aa0d8b 100644 --- a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomPresenter.kt +++ b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/ReportRoomPresenter.kt @@ -43,7 +43,7 @@ class ReportRoomPresenter( val coroutineScope = rememberCoroutineScope() - fun handleEvents(event: ReportRoomEvents) { + fun handleEvent(event: ReportRoomEvents) { when (event) { ReportRoomEvents.Report -> coroutineScope.reportRoom(reason, leaveRoom, reportAction) ReportRoomEvents.ToggleLeaveRoom -> { @@ -61,7 +61,7 @@ class ReportRoomPresenter( reason = reason, leaveRoom = leaveRoom, reportAction = reportAction.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt index c8a8d18fbc..8714bf9891 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt @@ -44,7 +44,7 @@ class RoomAliasResolverPresenter( resolveAlias(resolveState) } - fun handleEvents(event: RoomAliasResolverEvents) { + fun handleEvent(event: RoomAliasResolverEvents) { when (event) { RoomAliasResolverEvents.Retry -> coroutineScope.resolveAlias(resolveState) RoomAliasResolverEvents.DismissError -> resolveState.value = AsyncData.Uninitialized @@ -54,7 +54,7 @@ class RoomAliasResolverPresenter( return RoomAliasResolverState( roomAlias = roomAlias, resolveState = resolveState.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt index ccb4c48e7e..4c0467cee1 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt @@ -136,7 +136,7 @@ class RoomDetailsPresenter( val snackbarDispatcher = LocalSnackbarDispatcher.current val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState() - fun handleEvents(event: RoomDetailsEvent) { + fun handleEvent(event: RoomDetailsEvent) { when (event) { is RoomDetailsEvent.LeaveRoom -> { leaveRoomState.eventSink(LeaveRoomEvent.LeaveRoom(room.roomId, needsConfirmation = event.needsConfirmation)) @@ -204,7 +204,7 @@ class RoomDetailsPresenter( canReportRoom = canReportRoom, isTombstoned = roomInfo.successorRoom != null, showDebugInfo = isDeveloperModeEnabled, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt index cc207d8b24..fc9fa32be5 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt @@ -140,7 +140,7 @@ class RoomDetailsEditPresenter( val saveAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } val localCoroutineScope = rememberCoroutineScope() - fun handleEvents(event: RoomDetailsEditEvents) { + fun handleEvent(event: RoomDetailsEditEvents) { when (event) { is RoomDetailsEditEvents.Save -> localCoroutineScope.saveChanges( currentNameTrimmed = roomRawNameTrimmed, @@ -191,7 +191,7 @@ class RoomDetailsEditPresenter( saveButtonEnabled = saveButtonEnabled, saveAction = saveAction.value, cameraPermissionState = cameraPermissionState, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt index 90619d2e2d..b1cefd2537 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt @@ -159,7 +159,7 @@ class RoomMemberListPresenter( } } - fun handleEvents(event: RoomMemberListEvents) { + fun handleEvent(event: RoomMemberListEvents) { when (event) { is RoomMemberListEvents.OnSearchActiveChanged -> isSearchActive = event.active is RoomMemberListEvents.UpdateSearchQuery -> searchQuery = event.query @@ -179,7 +179,7 @@ class RoomMemberListPresenter( isSearchActive = isSearchActive, canInvite = canInvite, moderationState = roomModerationState, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt index 7bfdf9949c..4e5b57e7e5 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt @@ -99,7 +99,7 @@ class RoomNotificationSettingsPresenter( !notificationSettingsService.canHomeServerPushEncryptedEventsToDevice().getOrDefault(true) } - fun handleEvents(event: RoomNotificationSettingsEvents) { + fun handleEvent(event: RoomNotificationSettingsEvents) { when (event) { is RoomNotificationSettingsEvents.ChangeRoomNotificationMode -> { localCoroutineScope.setRoomNotificationMode(event.mode, pendingRoomNotificationMode, pendingSetDefault, setNotificationSettingAction) @@ -135,7 +135,7 @@ class RoomNotificationSettingsPresenter( setNotificationSettingAction = setNotificationSettingAction.value, restoreDefaultAction = restoreDefaultAction.value, displayMentionsOnlyDisclaimer = shouldDisplayMentionsOnlyDisclaimer, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyPresenter.kt index 4ff45bb588..fb552077fe 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyPresenter.kt @@ -107,7 +107,7 @@ class SecurityAndPrivacyPresenter( var showEnableEncryptionConfirmation by remember(savedSettings.isEncrypted) { mutableStateOf(false) } val permissions by room.securityAndPrivacyPermissionsAsState(syncUpdateFlow.value) - fun handleEvents(event: SecurityAndPrivacyEvents) { + fun handleEvent(event: SecurityAndPrivacyEvents) { when (event) { SecurityAndPrivacyEvents.Save -> { coroutineScope.save( @@ -158,7 +158,7 @@ class SecurityAndPrivacyPresenter( isKnockEnabled = isKnockEnabled, saveAction = saveAction.value, permissions = permissions, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) // If the history visibility is not available for the current access, use the fallback. diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/editroomaddress/EditRoomAddressPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/editroomaddress/EditRoomAddressPresenter.kt index 95aee73c13..badb40e331 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/editroomaddress/EditRoomAddressPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/editroomaddress/EditRoomAddressPresenter.kt @@ -62,7 +62,7 @@ class EditRoomAddressPresenter( ) } - fun handleEvents(event: EditRoomAddressEvents) { + fun handleEvent(event: EditRoomAddressEvents) { when (event) { EditRoomAddressEvents.Save -> coroutineScope.save( saveAction = saveAction, @@ -92,7 +92,7 @@ class EditRoomAddressPresenter( roomAddressValidity = roomAddressValidity.value, roomAddress = newRoomAddress, saveAction = saveAction.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt index 4696dd4263..77f7203368 100644 --- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt +++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt @@ -62,7 +62,7 @@ class RoomDirectoryPresenter( loadingMore = false } } - fun handleEvents(event: RoomDirectoryEvents) { + fun handleEvent(event: RoomDirectoryEvents) { when (event) { RoomDirectoryEvents.LoadMore -> { loadingMore = true @@ -77,7 +77,7 @@ class RoomDirectoryPresenter( query = searchQuery.orEmpty(), roomDescriptions = listState.items, displayLoadMoreIndicator = listState.hasMoreToLoad, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenter.kt index aa1de8fde9..54ea35e7f0 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisablePresenter.kt @@ -36,7 +36,7 @@ class SecureBackupDisablePresenter( Timber.tag(loggerTagDisable.value).d("backupState: $backupState") val disableAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } val coroutineScope = rememberCoroutineScope() - fun handleEvents(event: SecureBackupDisableEvents) { + fun handleEvent(event: SecureBackupDisableEvents) { when (event) { is SecureBackupDisableEvents.DisableBackup -> coroutineScope.disableBackup(disableAction) SecureBackupDisableEvents.DismissDialogs -> { @@ -49,7 +49,7 @@ class SecureBackupDisablePresenter( backupState = backupState, disableAction = disableAction.value, appName = buildMeta.applicationName, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenter.kt index b56a4542b0..0bf73a3322 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenter.kt @@ -44,7 +44,7 @@ class SecureBackupEnterRecoveryKeyPresenter( mutableStateOf(AsyncAction.Uninitialized) } - fun handleEvents(event: SecureBackupEnterRecoveryKeyEvents) { + fun handleEvent(event: SecureBackupEnterRecoveryKeyEvents) { when (event) { SecureBackupEnterRecoveryKeyEvents.ClearDialog -> { submitAction.value = AsyncAction.Uninitialized @@ -78,7 +78,7 @@ class SecureBackupEnterRecoveryKeyPresenter( ), isSubmitEnabled = recoveryKey.isNotEmpty() && submitAction.value.isUninitialized(), submitAction = submitAction.value, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordPresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordPresenter.kt index 26a6ae1fac..648a2cc7f9 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordPresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordPresenter.kt @@ -39,7 +39,7 @@ class ResetIdentityPasswordPresenter( return ResetIdentityPasswordState( resetAction = resetAction.value, - eventSink = ::handleEvent + eventSink = ::handleEvent, ) } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootPresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootPresenter.kt index 909cdc1379..6b357cec8d 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootPresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootPresenter.kt @@ -28,7 +28,7 @@ class ResetIdentityRootPresenter : Presenter { return ResetIdentityRootState( displayConfirmationDialog = displayConfirmDialog, - eventSink = ::handleEvent + eventSink = ::handleEvent, ) } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt index 67ec1c9960..7ecd056e4f 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt @@ -58,7 +58,7 @@ class SecureBackupRootPresenter( } } - fun handleEvents(event: SecureBackupRootEvents) { + fun handleEvent(event: SecureBackupRootEvents) { when (event) { SecureBackupRootEvents.RetryKeyBackupState -> localCoroutineScope.getKeyBackupStatus(doesBackupExistOnServerAction) SecureBackupRootEvents.EnableKeyStorage -> localCoroutineScope.enableBackup(enableAction) @@ -78,7 +78,7 @@ class SecureBackupRootPresenter( appName = buildMeta.applicationName, displayKeyStorageDisabledError = displayKeyStorageDisabledError, snackbarMessage = snackbarMessage, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt index 58a6c4b43c..a9b60c23a2 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt @@ -52,7 +52,7 @@ class SecureBackupSetupPresenter( } var showSaveConfirmationDialog by remember { mutableStateOf(false) } - fun handleEvents(event: SecureBackupSetupEvents) { + fun handleEvent(event: SecureBackupSetupEvents) { when (event) { SecureBackupSetupEvents.CreateRecoveryKey -> { coroutineScope.createOrChangeRecoveryKey(stateAndDispatch) @@ -81,7 +81,7 @@ class SecureBackupSetupPresenter( recoveryKeyViewState = recoveryKeyViewState, setupState = setupState, showSaveConfirmationDialog = showSaveConfirmationDialog, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/SharePresenter.kt b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/SharePresenter.kt index f5ce67498e..6d7011c4a7 100644 --- a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/SharePresenter.kt +++ b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/SharePresenter.kt @@ -55,7 +55,7 @@ class SharePresenter( @Composable override fun present(): ShareState { - fun handleEvents(event: ShareEvents) { + fun handleEvent(event: ShareEvents) { when (event) { ShareEvents.ClearError -> shareActionState.value = AsyncAction.Uninitialized } @@ -63,7 +63,7 @@ class SharePresenter( return ShareState( shareAction = shareActionState.value, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutPresenter.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutPresenter.kt index 7c9d1e6d83..ee7ad1831e 100644 --- a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutPresenter.kt +++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutPresenter.kt @@ -42,7 +42,7 @@ class SignedOutPresenter( }.collectAsState(initial = null) val coroutineScope = rememberCoroutineScope() - fun handleEvents(event: SignedOutEvents) { + fun handleEvent(event: SignedOutEvents) { when (event) { SignedOutEvents.SignInAgain -> coroutineScope.launch { sessionStore.removeSession(sessionId.value) @@ -53,7 +53,7 @@ class SignedOutPresenter( return SignedOutState( appName = buildMeta.applicationName, signedOutSession = signedOutSession, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenter.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenter.kt index 19e593bf7c..1433e17e92 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenter.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenter.kt @@ -97,7 +97,7 @@ class LeaveSpacePresenter( } } - fun handleEvents(event: LeaveSpaceEvents) { + fun handleEvent(event: LeaveSpaceEvents) { when (event) { LeaveSpaceEvents.Retry -> { leaveSpaceRooms = AsyncData.Loading() @@ -134,7 +134,7 @@ class LeaveSpacePresenter( isLastAdmin = leaveSpaceRooms.dataOrNull()?.current?.isLastAdmin == true, selectableSpaceRooms = selectableSpaceRooms, leaveSpaceAction = leaveSpaceAction.value, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt index 58f5c8f5fb..a8b8e3a080 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt @@ -93,7 +93,7 @@ class SpacePresenter( val acceptDeclineInviteState = acceptDeclineInvitePresenter.present() - fun handleEvents(event: SpaceEvents) { + fun handleEvent(event: SpaceEvents) { when (event) { SpaceEvents.LoadMore -> localCoroutineScope.paginate() is SpaceEvents.Join -> { @@ -128,7 +128,7 @@ class SpacePresenter( joinActions = joinActions.toImmutableMap(), acceptDeclineInviteState = acceptDeclineInviteState, topicViewerState = topicViewerState, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressPresenter.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressPresenter.kt index 350a59de73..5acb0629f7 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressPresenter.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressPresenter.kt @@ -48,7 +48,7 @@ class JoinRoomByAddressPresenter( var internalAddressState by remember { mutableStateOf(RoomAddressState.Unknown) } var validateAddress: Boolean by remember { mutableStateOf(false) } - fun handleEvents(event: JoinRoomByAddressEvents) { + fun handleEvent(event: JoinRoomByAddressEvents) { when (event) { JoinRoomByAddressEvents.Continue -> { when (val currentState = internalAddressState) { @@ -88,7 +88,7 @@ class JoinRoomByAddressPresenter( return JoinRoomByAddressState( address = address, addressState = addressState, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenter.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenter.kt index 10a9745f32..c683125caf 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenter.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenter.kt @@ -57,7 +57,7 @@ class StartChatPresenter( featureFlagService.isFeatureEnabledFlow(FeatureFlags.RoomDirectorySearch) }.collectAsState(initial = false) - fun handleEvents(event: StartChatEvents) { + fun handleEvent(event: StartChatEvents) { when (event) { is StartChatEvents.StartDM -> localCoroutineScope.launch { startDMAction.execute( @@ -75,7 +75,7 @@ class StartChatPresenter( userListState = userListState, startDmAction = startDmActionState.value, isRoomDirectorySearchEnabled = isRoomDirectorySearchEnabled, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } } diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt index 3f226d0213..9efff65d9d 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt @@ -99,7 +99,7 @@ class UserProfilePresenter( } val userProfile by produceState(null) { value = client.getProfile(userId).getOrNull() } - fun handleEvents(event: UserProfileEvents) { + fun handleEvent(event: UserProfileEvents) { when (event) { is UserProfileEvents.BlockUser -> { if (event.needsConfirmation) { @@ -151,7 +151,7 @@ class UserProfilePresenter( dmRoomId = dmRoomId, canCall = canCall, snackbarMessage = null, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationPresenter.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationPresenter.kt index 176176a895..38918314f6 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationPresenter.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationPresenter.kt @@ -107,7 +107,7 @@ class IncomingVerificationPresenter( } } - fun handleEvents(event: IncomingVerificationViewEvents) { + fun handleEvent(event: IncomingVerificationViewEvents) { Timber.d("Verification user action: ${event::class.simpleName}") when (event) { IncomingVerificationViewEvents.StartVerification -> @@ -141,7 +141,7 @@ class IncomingVerificationPresenter( return IncomingVerificationState( step = step, request = verificationRequest, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenter.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenter.kt index c985c15e36..9b3906606b 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenter.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenter.kt @@ -92,7 +92,7 @@ class OutgoingVerificationPresenter( observeVerificationService() } - fun handleEvents(event: OutgoingVerificationViewEvents) { + fun handleEvent(event: OutgoingVerificationViewEvents) { Timber.d("Verification user action: ${event::class.simpleName}") when (event) { // Just relay the event to the state machine @@ -109,7 +109,7 @@ class OutgoingVerificationPresenter( return OutgoingVerificationState( step = step, request = verificationRequest, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/libraries/fullscreenintent/impl/src/main/kotlin/io/element/android/libraries/fullscreenintent/impl/FullScreenIntentPermissionsPresenter.kt b/libraries/fullscreenintent/impl/src/main/kotlin/io/element/android/libraries/fullscreenintent/impl/FullScreenIntentPermissionsPresenter.kt index b05aad303c..be483b0683 100644 --- a/libraries/fullscreenintent/impl/src/main/kotlin/io/element/android/libraries/fullscreenintent/impl/FullScreenIntentPermissionsPresenter.kt +++ b/libraries/fullscreenintent/impl/src/main/kotlin/io/element/android/libraries/fullscreenintent/impl/FullScreenIntentPermissionsPresenter.kt @@ -63,7 +63,7 @@ class FullScreenIntentPermissionsPresenter( val isGranted = notificationManagerCompat.canUseFullScreenIntent() val isBannerDismissed by isFullScreenIntentBannerDismissed.collectAsState(initial = true) - fun handleEvents(event: FullScreenIntentPermissionsEvents) { + fun handleEvent(event: FullScreenIntentPermissionsEvents) { when (event) { FullScreenIntentPermissionsEvents.Dismiss -> coroutineScope.launch { dismissFullScreenIntentBanner() @@ -75,7 +75,7 @@ class FullScreenIntentPermissionsPresenter( return FullScreenIntentPermissionsState( permissionGranted = isGranted, shouldDisplayBanner = !isBannerDismissed && !isGranted, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt index 3f8a31cc26..955acfe7ec 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt @@ -82,7 +82,7 @@ class MediaGalleryPresenter( val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState() localMediaActions.Configure() - fun handleEvents(event: MediaGalleryEvents) { + fun handleEvent(event: MediaGalleryEvents) { when (event) { is MediaGalleryEvents.ChangeMode -> { mode = event.mode @@ -150,7 +150,7 @@ class MediaGalleryPresenter( groupedMediaItems = groupedMediaItems, mediaBottomSheetState = mediaBottomSheetState, snackbarMessage = snackbarMessage, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt index a04a1a370a..ea560a0b6a 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt @@ -90,7 +90,7 @@ class MediaViewerPresenter( } localMediaActions.Configure() - fun handleEvents(event: MediaViewerEvents) { + fun handleEvent(event: MediaViewerEvents) { when (event) { is MediaViewerEvents.LoadMedia -> { coroutineScope.downloadMedia(data = event.data) @@ -163,7 +163,7 @@ class MediaViewerPresenter( snackbarMessage = snackbarMessage, canShowInfo = inputs.canShowInfo, mediaBottomSheetState = mediaBottomSheetState, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } diff --git a/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt b/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt index c6bfbd73ed..e157aa56f5 100644 --- a/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt +++ b/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt @@ -99,7 +99,7 @@ class DefaultPermissionsPresenter( val showDialog = rememberSaveable { mutableStateOf(false) } - fun handleEvents(event: PermissionsEvents) { + fun handleEvent(event: PermissionsEvents) { when (event) { PermissionsEvents.CloseDialog -> { showDialog.value = false @@ -125,7 +125,7 @@ class DefaultPermissionsPresenter( showDialog = showDialog.value, permissionAlreadyAsked = isAlreadyAsked, permissionAlreadyDenied = isAlreadyDenied, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/battery/BatteryOptimizationPresenter.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/battery/BatteryOptimizationPresenter.kt index 3c876ece1d..6bff2092e0 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/battery/BatteryOptimizationPresenter.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/battery/BatteryOptimizationPresenter.kt @@ -47,7 +47,7 @@ class BatteryOptimizationPresenter( onPauseOrDispose {} } - fun handleEvents(event: BatteryOptimizationEvents) { + fun handleEvent(event: BatteryOptimizationEvents) { when (event) { BatteryOptimizationEvents.Dismiss -> coroutineScope.launch { mutableBatteryOptimizationStore.onOptimizationBannerDismissed() @@ -66,7 +66,7 @@ class BatteryOptimizationPresenter( return BatteryOptimizationState( shouldDisplayBanner = localShouldDisplayBanner && storeShouldDisplayBanner && !isSystemIgnoringBatteryOptimizations, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } } diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt index 841b955018..b6546784ea 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt @@ -63,7 +63,7 @@ class RoomSelectPresenter( } } - fun handleEvents(event: RoomSelectEvents) { + fun handleEvent(event: RoomSelectEvents) { when (event) { is RoomSelectEvents.SetSelectedRoom -> { selectedRooms = persistentListOf(event.room) @@ -87,7 +87,7 @@ class RoomSelectPresenter( query = searchQuery, isSearchActive = isSearchActive, selectedRooms = selectedRooms, - eventSink = ::handleEvents, + eventSink = ::handleEvent, ) } } diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenter.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenter.kt index 07840b023c..d410d85973 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenter.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenter.kt @@ -37,7 +37,7 @@ class TroubleshootNotificationsPresenter( } val testSuiteState by troubleshootTestSuite.state.collectAsState() - fun handleEvents(event: TroubleshootNotificationsEvents) { + fun handleEvent(event: TroubleshootNotificationsEvents) { when (event) { TroubleshootNotificationsEvents.StartTests -> coroutineScope.launch { troubleshootTestSuite.runTestSuite(this) @@ -57,7 +57,7 @@ class TroubleshootNotificationsPresenter( return TroubleshootNotificationsState( testSuiteState = testSuiteState, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } } diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryPresenter.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryPresenter.kt index b98fcee970..db2885e2d5 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryPresenter.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryPresenter.kt @@ -61,7 +61,7 @@ class PushHistoryPresenter( var resetAction: AsyncAction by remember { mutableStateOf(AsyncAction.Uninitialized) } var showNotSameAccountError by remember { mutableStateOf(false) } - fun handleEvents(event: PushHistoryEvents) { + fun handleEvent(event: PushHistoryEvents) { when (event) { is PushHistoryEvents.SetShowOnlyErrors -> { showOnlyErrors = event.showOnlyErrors @@ -97,7 +97,7 @@ class PushHistoryPresenter( showOnlyErrors = showOnlyErrors, resetAction = resetAction, showNotSameAccountError = showNotSameAccountError, - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } } From 19d0eb755759f72fb45aeed4ac0152e450ba44ec Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Nov 2025 12:22:30 +0100 Subject: [PATCH 082/216] Rename eventHandler to handleEvent --- .../choosemode/ChooseSelfVerificationModePresenter.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModePresenter.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModePresenter.kt index 32419a0a59..70b1076c7f 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModePresenter.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModePresenter.kt @@ -59,7 +59,7 @@ class ChooseSelfVerificationModePresenter( val directLogoutState = directLogoutPresenter.present() - fun eventHandler(event: ChooseSelfVerificationModeEvent) { + fun handleEvent(event: ChooseSelfVerificationModeEvent) { when (event) { ChooseSelfVerificationModeEvent.SignOut -> directLogoutState.eventSink(DirectLogoutEvents.Logout(ignoreSdkError = false)) } @@ -68,7 +68,7 @@ class ChooseSelfVerificationModePresenter( return ChooseSelfVerificationModeState( buttonsState = buttonsState, directLogoutState = directLogoutState, - eventSink = ::eventHandler, + eventSink = ::handleEvent, ) } } From ec8262672df7a808b746e02591f133aa68e471bb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Nov 2025 12:23:02 +0100 Subject: [PATCH 083/216] Rename eventSink to handleEvent --- .../impl/members/details/RoomMemberDetailsPresenter.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt index d5cf9e85cb..75cd9c0583 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt @@ -111,7 +111,7 @@ class RoomMemberDetailsPresenter( } } - fun eventSink(event: UserProfileEvents) { + fun handleEvent(event: UserProfileEvents) { when (event) { UserProfileEvents.WithdrawVerification -> coroutineScope.launch { encryptionService.withdrawVerification(roomMemberId) @@ -129,7 +129,7 @@ class RoomMemberDetailsPresenter( avatarUrl = roomUserAvatar ?: userProfileState.avatarUrl, verificationState = verificationState, snackbarMessage = snackbarMessage, - eventSink = ::eventSink + eventSink = ::handleEvent, ) } } From 0cafe8fee060b1e64034f41921f6951869d54d07 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Nov 2025 12:23:46 +0100 Subject: [PATCH 084/216] Rename eventSink to handleEvent --- .../libraries/permissions/test/FakePermissionsPresenter.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenter.kt b/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenter.kt index 2aa1d103db..cbc1caa525 100644 --- a/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenter.kt +++ b/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenter.kt @@ -17,7 +17,7 @@ import io.element.android.libraries.permissions.api.aPermissionsState class FakePermissionsPresenter( private val initialState: PermissionsState = aPermissionsState(showDialog = false), ) : PermissionsPresenter { - private fun eventSink(events: PermissionsEvents) { + private fun handleEvent(events: PermissionsEvents) { when (events) { PermissionsEvents.RequestPermissions -> state.value = state.value.copy(showDialog = true, permissionAlreadyAsked = true) PermissionsEvents.CloseDialog -> state.value = state.value.copy(showDialog = false) @@ -25,7 +25,7 @@ class FakePermissionsPresenter( } } - private val state = mutableStateOf(initialState.copy(eventSink = ::eventSink)) + private val state = mutableStateOf(initialState.copy(eventSink = ::handleEvent)) fun setPermissionGranted() { state.value = state.value.copy(permissionGranted = true) From 9c7b4023675f1a4e5b0cfbc7a626716e6e358049 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Nov 2025 12:25:15 +0100 Subject: [PATCH 085/216] Introduce fun handleEvent for code consistency --- .../impl/userlist/DefaultUserListPresenter.kt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/DefaultUserListPresenter.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/DefaultUserListPresenter.kt index 38d15f6de3..d1d15cbafd 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/DefaultUserListPresenter.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/userlist/DefaultUserListPresenter.kt @@ -75,6 +75,15 @@ class DefaultUserListPresenter( }.launchIn(this) } + fun handleEvent(event: UserListEvents) { + when (event) { + is UserListEvents.OnSearchActiveChanged -> isSearchActive = event.active + is UserListEvents.UpdateSearchQuery -> searchQuery = event.query + is UserListEvents.AddToSelection -> userListDataStore.selectUser(event.matrixUser) + is UserListEvents.RemoveFromSelection -> userListDataStore.removeUserFromSelection(event.matrixUser) + } + } + return UserListState( searchQuery = searchQuery, searchResults = searchResults, @@ -83,14 +92,7 @@ class DefaultUserListPresenter( showSearchLoader = showSearchLoader, selectionMode = args.selectionMode, recentDirectRooms = recentDirectRooms.toImmutableList(), - eventSink = { event -> - when (event) { - is UserListEvents.OnSearchActiveChanged -> isSearchActive = event.active - is UserListEvents.UpdateSearchQuery -> searchQuery = event.query - is UserListEvents.AddToSelection -> userListDataStore.selectUser(event.matrixUser) - is UserListEvents.RemoveFromSelection -> userListDataStore.removeUserFromSelection(event.matrixUser) - } - }, + eventSink = ::handleEvent, ) } } From 2d80e9c40df174137a6f5d3f3cddee7c3953e2f5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Nov 2025 12:28:16 +0100 Subject: [PATCH 086/216] Rename parameter to event for code consistency --- .../impl/list/DependencyLicensesListPresenter.kt | 6 +++--- .../attachments/preview/AttachmentsPreviewPresenter.kt | 4 ++-- .../features/messages/impl/link/LinkPresenter.kt | 10 +++++----- .../permissions/test/FakePermissionsPresenter.kt | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListPresenter.kt b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListPresenter.kt index 0af5d1cc47..65c44fc4c3 100644 --- a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListPresenter.kt +++ b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListPresenter.kt @@ -56,10 +56,10 @@ class DependencyLicensesListPresenter( } } - fun handleEvent(dependencyLicensesListEvent: DependencyLicensesListEvent) { - when (dependencyLicensesListEvent) { + fun handleEvent(event: DependencyLicensesListEvent) { + when (event) { is DependencyLicensesListEvent.SetFilter -> { - filter = dependencyLicensesListEvent.filter + filter = event.filter } } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt index a3662c9e12..bb4f624793 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt @@ -141,8 +141,8 @@ class AttachmentsPreviewPresenter( } } - fun handleEvent(attachmentsPreviewEvents: AttachmentsPreviewEvents) { - when (attachmentsPreviewEvents) { + fun handleEvent(event: AttachmentsPreviewEvents) { + when (event) { is AttachmentsPreviewEvents.SendAttachment -> { ongoingSendAttachmentJob.value = coroutineScope.launch { // If the media optimization selector is displayed, we need to wait for the user to select the options diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/LinkPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/LinkPresenter.kt index a0aa62da56..1f187a9b18 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/LinkPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/link/LinkPresenter.kt @@ -24,16 +24,16 @@ class LinkPresenter( override fun present(): LinkState { val linkClick: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } - fun handleEvent(linkEvents: LinkEvents) { - when (linkEvents) { + fun handleEvent(event: LinkEvents) { + when (event) { is LinkEvents.OnLinkClick -> { linkClick.value = AsyncAction.Loading - val result = linkChecker.isSafe(linkEvents.link) + val result = linkChecker.isSafe(event.link) if (result) { - linkClick.value = AsyncAction.Success(linkEvents.link) + linkClick.value = AsyncAction.Success(event.link) } else { // Confirm first - linkClick.value = ConfirmingLinkClick(linkEvents.link) + linkClick.value = ConfirmingLinkClick(event.link) } } LinkEvents.Confirm -> { diff --git a/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenter.kt b/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenter.kt index cbc1caa525..d960ddeb46 100644 --- a/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenter.kt +++ b/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenter.kt @@ -17,8 +17,8 @@ import io.element.android.libraries.permissions.api.aPermissionsState class FakePermissionsPresenter( private val initialState: PermissionsState = aPermissionsState(showDialog = false), ) : PermissionsPresenter { - private fun handleEvent(events: PermissionsEvents) { - when (events) { + private fun handleEvent(event: PermissionsEvents) { + when (event) { PermissionsEvents.RequestPermissions -> state.value = state.value.copy(showDialog = true, permissionAlreadyAsked = true) PermissionsEvents.CloseDialog -> state.value = state.value.copy(showDialog = false) PermissionsEvents.OpenSystemSettingAndCloseDialog -> state.value = state.value.copy(showDialog = false) From 2625c75ac9a92a27278da72b4095385de0a3f23a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Nov 2025 12:44:21 +0100 Subject: [PATCH 087/216] Update the template. --- .../files/fileTemplates/Template Presentation Classes.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/templates/files/fileTemplates/Template Presentation Classes.kt b/tools/templates/files/fileTemplates/Template Presentation Classes.kt index 91ec2165f7..33eb8f6974 100644 --- a/tools/templates/files/fileTemplates/Template Presentation Classes.kt +++ b/tools/templates/files/fileTemplates/Template Presentation Classes.kt @@ -10,14 +10,14 @@ class ${NAME}Presenter() : Presenter<${NAME}State> { @Composable override fun present(): ${NAME}State { - fun handleEvents(event: ${NAME}Events) { + fun handleEvent(event: ${NAME}Events) { when (event) { ${NAME}Events.MyEvent -> Unit } } return ${NAME}State( - eventSink = ::handleEvents + eventSink = ::handleEvent, ) } } From b8717254239769a8d2a272b26148c3649bc1f79a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Nov 2025 12:51:35 +0100 Subject: [PATCH 088/216] Improve error displayed when the Unconsumed Event cannot be displayed. --- .../android/tests/testutils/PresenterTest.kt | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/PresenterTest.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/PresenterTest.kt index c5acc9d510..2967e4bb67 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/PresenterTest.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/PresenterTest.kt @@ -12,6 +12,7 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.TurbineTestContext import app.cash.turbine.test import io.element.android.libraries.architecture.Presenter +import org.junit.Assert.fail import kotlin.time.Duration suspend fun Presenter.test( @@ -19,7 +20,20 @@ suspend fun Presenter.test( name: String? = null, validate: suspend TurbineTestContext.() -> Unit, ) { - moleculeFlow(RecompositionMode.Immediate) { - present() - }.test(timeout, name, validate) + try { + moleculeFlow(RecompositionMode.Immediate) { + present() + }.test(timeout, name, validate) + } catch (t: Throwable) { + if (t::class.simpleName == "KotlinReflectionInternalError") { + // Give a more explicit error to the developer + fail(""" + It looks like you have an unconsumed event in your test. + If you get this error, it means that your test is missing to consume one of several events. + You can fix by consuming and check the event with `awaitItem()`, or you can also invoke + `cancelAndIgnoreRemainingEvents()`. + """.trimIndent()) + } + throw t + } } From b732ee29a3b168663683b3ffe64ea02ef5a1acb0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Nov 2025 13:35:23 +0100 Subject: [PATCH 089/216] Fix compilation issue --- .../android/features/messages/impl/MessagesPresenter.kt | 5 +++-- .../preferences/impl/developer/DeveloperSettingsPresenter.kt | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index 39f9a07faa..ba385415c0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -291,8 +291,9 @@ class MessagesPresenter( pinnedMessagesBannerState = pinnedMessagesBannerState, dmUserVerificationState = dmUserVerificationState, roomMemberModerationState = roomMemberModerationState, - successorRoom = roomInfo.successorRoom - ) { handleEvents(it) } + successorRoom = roomInfo.successorRoom, + eventSink = ::handleEvent, + ) } @Composable diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt index 618e63ccae..169b937049 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt @@ -118,7 +118,7 @@ class DeveloperSettingsPresenter( enabledFeatures = enabledFeatures, featureKey = event.feature.key, enabled = event.isEnabled, - triggerClearCache = { handleEvents(DeveloperSettingsEvents.ClearCache) } + triggerClearCache = { handleEvent(DeveloperSettingsEvents.ClearCache) } ) is DeveloperSettingsEvents.SetCustomElementCallBaseUrl -> coroutineScope.launch { val urlToSave = event.baseUrl.takeIf { !it.isNullOrEmpty() } From c99068c60ecc77c9d13327377f1a52c2940f10a2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Nov 2025 13:39:56 +0100 Subject: [PATCH 090/216] Update the template to use the singular form for Event interface. --- .../files/fileTemplates/Template Presentation Classes.kt | 4 ++-- .../fileTemplates/Template Presentation Classes.kt.child.3.kt | 2 +- .../fileTemplates/Template Presentation Classes.kt.child.4.kt | 4 ++-- tools/templates/files/options/file.template.settings.xml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/templates/files/fileTemplates/Template Presentation Classes.kt b/tools/templates/files/fileTemplates/Template Presentation Classes.kt index 33eb8f6974..a527a37b57 100644 --- a/tools/templates/files/fileTemplates/Template Presentation Classes.kt +++ b/tools/templates/files/fileTemplates/Template Presentation Classes.kt @@ -10,9 +10,9 @@ class ${NAME}Presenter() : Presenter<${NAME}State> { @Composable override fun present(): ${NAME}State { - fun handleEvent(event: ${NAME}Events) { + fun handleEvent(event: ${NAME}Event) { when (event) { - ${NAME}Events.MyEvent -> Unit + ${NAME}Event.MyEvent -> Unit } } diff --git a/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.3.kt b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.3.kt index d8b57da6b6..f182b62d96 100644 --- a/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.3.kt +++ b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.3.kt @@ -2,5 +2,5 @@ // TODO add your ui models. Remove the eventSink if you don't have events. data class ${NAME}State( - val eventSink: (${NAME}Events) -> Unit + val eventSink: (${NAME}Event) -> Unit ) diff --git a/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.4.kt b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.4.kt index 4b47069304..8ef5400fd9 100644 --- a/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.4.kt +++ b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.4.kt @@ -1,6 +1,6 @@ #if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}#end // TODO Add your events or remove the file completely if no events -sealed interface ${NAME}Events { - data object MyEvent: ${NAME}Events +sealed interface ${NAME}Event { + data object MyEvent: ${NAME}Event } diff --git a/tools/templates/files/options/file.template.settings.xml b/tools/templates/files/options/file.template.settings.xml index c7d26d1fb7..a284c63140 100644 --- a/tools/templates/files/options/file.template.settings.xml +++ b/tools/templates/files/options/file.template.settings.xml @@ -6,13 +6,13 @@