Browse Source

Merge develop into feature/fga/some_room_related_fixes

test/jme/fix-danger-lint-duplicate-reports
ganfra 1 year ago
parent
commit
1d690fdd00
  1. 4
      appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt
  2. 2
      build.gradle.kts
  3. 1
      changelog.d/106.feature
  4. 1
      features/invitelist/api/build.gradle.kts
  5. 3
      features/invitelist/api/src/main/kotlin/io/element/android/features/invitelist/api/InviteListEntryPoint.kt
  6. 32
      features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListEvents.kt
  7. 6
      features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListNode.kt
  8. 73
      features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt
  9. 13
      features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListState.kt
  10. 7
      features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListStateProvider.kt
  11. 58
      features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt
  12. 13
      features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt
  13. 3
      features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/model/InviteListInviteSummary.kt
  14. 355
      features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt
  15. 18
      gradle/libs.versions.toml
  16. 2
      libraries/matrix/api/build.gradle.kts
  17. 4
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt
  18. 2
      libraries/matrix/impl/build.gradle.kts
  19. 2
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryDetailsFactory.kt
  20. 13
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
  21. 7
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt
  22. 27
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt
  23. 2
      libraries/push/impl/build.gradle.kts
  24. 2
      libraries/pushproviders/unifiedpush/build.gradle.kts
  25. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
  26. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
  27. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
  28. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
  29. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
  30. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
  31. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
  32. BIN
      tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_5,NEXUS_5,1.0,en].png

4
appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt

@ -227,6 +227,10 @@ class LoggedInFlowNode @AssistedInject constructor( @@ -227,6 +227,10 @@ class LoggedInFlowNode @AssistedInject constructor(
override fun onBackClicked() {
backstack.pop()
}
override fun onInviteAccepted(roomId: RoomId) {
backstack.push(NavTarget.Room(roomId))
}
}
inviteListEntryPoint.nodeBuilder(this, buildContext)

2
build.gradle.kts

@ -3,7 +3,7 @@ import org.jetbrains.kotlin.cli.common.toBooleanLenient @@ -3,7 +3,7 @@ import org.jetbrains.kotlin.cli.common.toBooleanLenient
buildscript {
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.20")
classpath("com.google.gms:google-services:4.3.15")
}
}

1
changelog.d/106.feature

@ -0,0 +1 @@ @@ -0,0 +1 @@
[Create and join rooms] Accept or decline an invite from invitation list

1
features/invitelist/api/build.gradle.kts

@ -24,4 +24,5 @@ android { @@ -24,4 +24,5 @@ android {
dependencies {
implementation(projects.libraries.architecture)
implementation(projects.libraries.matrix.api)
}

3
features/invitelist/api/src/main/kotlin/io/element/android/features/invitelist/api/InviteListEntryPoint.kt

@ -20,6 +20,7 @@ import com.bumble.appyx.core.modality.BuildContext @@ -20,6 +20,7 @@ import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import io.element.android.libraries.architecture.FeatureEntryPoint
import io.element.android.libraries.matrix.api.core.RoomId
interface InviteListEntryPoint : FeatureEntryPoint {
@ -32,6 +33,8 @@ interface InviteListEntryPoint : FeatureEntryPoint { @@ -32,6 +33,8 @@ interface InviteListEntryPoint : FeatureEntryPoint {
interface Callback : Plugin {
fun onBackClicked()
fun onInviteAccepted(roomId: RoomId)
}
}

32
features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListEvents.kt

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.invitelist.impl
import io.element.android.features.invitelist.impl.model.InviteListInviteSummary
sealed interface InviteListEvents {
data class AcceptInvite(val invite: InviteListInviteSummary) : InviteListEvents
data class DeclineInvite(val invite: InviteListInviteSummary) : InviteListEvents
object ConfirmDeclineInvite: InviteListEvents
object CancelDeclineInvite: InviteListEvents
object DismissAcceptError: InviteListEvents
object DismissDeclineError: InviteListEvents
}

6
features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListNode.kt

@ -27,6 +27,7 @@ import dagger.assisted.AssistedInject @@ -27,6 +27,7 @@ import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.invitelist.api.InviteListEntryPoint
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.core.RoomId
@ContributesNode(SessionScope::class)
class InviteListNode @AssistedInject constructor(
@ -39,12 +40,17 @@ class InviteListNode @AssistedInject constructor( @@ -39,12 +40,17 @@ class InviteListNode @AssistedInject constructor(
plugins<InviteListEntryPoint.Callback>().forEach { it.onBackClicked() }
}
private fun onInviteAccepted(roomId: RoomId) {
plugins<InviteListEntryPoint.Callback>().forEach { it.onInviteAccepted(roomId) }
}
@Composable
override fun View(modifier: Modifier) {
val state = presenter.present()
InviteListView(
state = state,
onBackClicked = ::onBackClicked,
onInviteAccepted = ::onInviteAccepted,
)
}
}

73
features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt

@ -17,15 +17,24 @@ @@ -17,15 +17,24 @@
package io.element.android.features.invitelist.impl
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import io.element.android.features.invitelist.impl.model.InviteListInviteSummary
import io.element.android.features.invitelist.impl.model.InviteSender
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.execute
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.RoomSummary
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import javax.inject.Inject
class InviteListPresenter @Inject constructor(
@ -39,11 +48,71 @@ class InviteListPresenter @Inject constructor( @@ -39,11 +48,71 @@ class InviteListPresenter @Inject constructor(
.roomSummaries()
.collectAsState()
val localCoroutineScope = rememberCoroutineScope()
val acceptedAction: MutableState<Async<RoomId>> = remember { mutableStateOf(Async.Uninitialized) }
val declinedAction: MutableState<Async<Unit>> = remember { mutableStateOf(Async.Uninitialized) }
val decliningInvite: MutableState<InviteListInviteSummary?> = remember { mutableStateOf(null) }
fun handleEvent(event: InviteListEvents) {
when (event) {
is InviteListEvents.AcceptInvite -> {
acceptedAction.value = Async.Uninitialized
localCoroutineScope.acceptInvite(event.invite.roomId, acceptedAction)
}
is InviteListEvents.DeclineInvite -> {
decliningInvite.value = event.invite
}
is InviteListEvents.ConfirmDeclineInvite -> {
declinedAction.value = Async.Uninitialized
decliningInvite.value?.let {
localCoroutineScope.declineInvite(it.roomId, declinedAction)
}
decliningInvite.value = null
}
is InviteListEvents.CancelDeclineInvite -> {
decliningInvite.value = null
}
is InviteListEvents.DismissAcceptError -> {
acceptedAction.value = Async.Uninitialized
}
is InviteListEvents.DismissDeclineError -> {
declinedAction.value = Async.Uninitialized
}
}
}
return InviteListState(
inviteList = invites.mapNotNull(::toInviteSummary).toPersistentList(),
declineConfirmationDialog = decliningInvite.value?.let {
InviteDeclineConfirmationDialog.Visible(
isDirect = it.isDirect,
name = it.roomName,
)
} ?: InviteDeclineConfirmationDialog.Hidden,
acceptedAction = acceptedAction.value,
declinedAction = declinedAction.value,
eventSink = ::handleEvent
)
}
private fun CoroutineScope.acceptInvite(roomId: RoomId, acceptedAction: MutableState<Async<RoomId>>) = launch {
suspend {
client.getRoom(roomId)?.acceptInvitation()?.getOrThrow()
roomId
}.execute(acceptedAction)
}
private fun CoroutineScope.declineInvite(roomId: RoomId, declinedAction: MutableState<Async<Unit>>) = launch {
suspend {
client.getRoom(roomId)?.rejectInvitation()?.getOrThrow() ?: Unit
}.execute(declinedAction)
}
private fun toInviteSummary(roomSummary: RoomSummary): InviteListInviteSummary? {
return when (roomSummary) {
is RoomSummary.Filled -> roomSummary.details.run {
@ -71,6 +140,7 @@ class InviteListPresenter @Inject constructor( @@ -71,6 +140,7 @@ class InviteListPresenter @Inject constructor(
roomName = name,
roomAlias = alias,
roomAvatarData = avatarData,
isDirect = isDirect,
sender = if (isDirect) null else inviter?.let {
InviteSender(
userId = it.userId,
@ -81,9 +151,10 @@ class InviteListPresenter @Inject constructor( @@ -81,9 +151,10 @@ class InviteListPresenter @Inject constructor(
url = it.avatarUrl,
),
)
}
},
)
}
else -> null
}
}

13
features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListState.kt

@ -18,9 +18,20 @@ package io.element.android.features.invitelist.impl @@ -18,9 +18,20 @@ package io.element.android.features.invitelist.impl
import androidx.compose.runtime.Immutable
import io.element.android.features.invitelist.impl.model.InviteListInviteSummary
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.matrix.api.core.RoomId
import kotlinx.collections.immutable.ImmutableList
@Immutable
data class InviteListState(
val inviteList: ImmutableList<InviteListInviteSummary>
val inviteList: ImmutableList<InviteListInviteSummary>,
val declineConfirmationDialog: InviteDeclineConfirmationDialog = InviteDeclineConfirmationDialog.Hidden,
val acceptedAction: Async<RoomId> = Async.Uninitialized,
val declinedAction: Async<Unit> = Async.Uninitialized,
val eventSink: (InviteListEvents) -> Unit = {}
)
sealed interface InviteDeclineConfirmationDialog {
object Hidden : InviteDeclineConfirmationDialog
data class Visible(val isDirect: Boolean, val name: String) : InviteDeclineConfirmationDialog
}

7
features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListStateProvider.kt

@ -19,6 +19,7 @@ package io.element.android.features.invitelist.impl @@ -19,6 +19,7 @@ package io.element.android.features.invitelist.impl
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.invitelist.impl.model.InviteListInviteSummary
import io.element.android.features.invitelist.impl.model.InviteSender
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
import kotlinx.collections.immutable.ImmutableList
@ -28,7 +29,11 @@ open class InviteListStateProvider : PreviewParameterProvider<InviteListState> { @@ -28,7 +29,11 @@ open class InviteListStateProvider : PreviewParameterProvider<InviteListState> {
override val values: Sequence<InviteListState>
get() = sequenceOf(
aInviteListState(),
aInviteListState().copy(inviteList = persistentListOf())
aInviteListState().copy(inviteList = persistentListOf()),
aInviteListState().copy(declineConfirmationDialog = InviteDeclineConfirmationDialog.Visible(true, "Alice")),
aInviteListState().copy(declineConfirmationDialog = InviteDeclineConfirmationDialog.Visible(false, "Some Room")),
aInviteListState().copy(acceptedAction = Async.Failure(Throwable("Whoops"))),
aInviteListState().copy(declinedAction = Async.Failure(Throwable("Whoops"))),
)
}

58
features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt

@ -26,6 +26,7 @@ import androidx.compose.foundation.lazy.items @@ -26,6 +26,7 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
@ -33,7 +34,10 @@ import androidx.compose.ui.tooling.preview.Preview @@ -33,7 +34,10 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.features.invitelist.impl.components.InviteSummaryRow
import io.element.android.libraries.architecture.Async
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.ErrorDialog
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Scaffold
@ -47,16 +51,56 @@ fun InviteListView( @@ -47,16 +51,56 @@ fun InviteListView(
state: InviteListState,
modifier: Modifier = Modifier,
onBackClicked: () -> Unit = {},
onAcceptClicked: (RoomId) -> Unit = {},
onDeclineClicked: (RoomId) -> Unit = {},
onInviteAccepted: (RoomId) -> Unit = {},
) {
if (state.acceptedAction is Async.Success) {
LaunchedEffect(state.acceptedAction) {
onInviteAccepted(state.acceptedAction.state)
}
}
InviteListContent(
state = state,
modifier = modifier,
onBackClicked = onBackClicked,
onAcceptClicked = onAcceptClicked,
onDeclineClicked = onDeclineClicked,
)
if (state.declineConfirmationDialog is InviteDeclineConfirmationDialog.Visible) {
val contentResource = if (state.declineConfirmationDialog.isDirect)
R.string.screen_invites_decline_direct_chat_message
else
R.string.screen_invites_decline_chat_message
val titleResource = if (state.declineConfirmationDialog.isDirect)
R.string.screen_invites_decline_direct_chat_title
else
R.string.screen_invites_decline_chat_title
ConfirmationDialog(
content = stringResource(contentResource, state.declineConfirmationDialog.name),
title = stringResource(titleResource),
onSubmitClicked = { state.eventSink(InviteListEvents.ConfirmDeclineInvite) },
onDismiss = { state.eventSink(InviteListEvents.CancelDeclineInvite) }
)
}
if (state.acceptedAction is Async.Failure) {
ErrorDialog(
content = stringResource(StringR.string.error_unknown),
title = stringResource(StringR.string.common_error),
submitText = stringResource(StringR.string.action_ok),
onDismiss = { state.eventSink(InviteListEvents.DismissAcceptError) }
)
}
if (state.declinedAction is Async.Failure) {
ErrorDialog(
content = stringResource(StringR.string.error_unknown),
title = stringResource(StringR.string.common_error),
submitText = stringResource(StringR.string.action_ok),
onDismiss = { state.eventSink(InviteListEvents.DismissDeclineError) }
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@ -65,8 +109,6 @@ fun InviteListContent( @@ -65,8 +109,6 @@ fun InviteListContent(
state: InviteListState,
modifier: Modifier = Modifier,
onBackClicked: () -> Unit = {},
onAcceptClicked: (RoomId) -> Unit = {},
onDeclineClicked: (RoomId) -> Unit = {},
) {
Scaffold(
modifier = modifier,
@ -102,8 +144,8 @@ fun InviteListContent( @@ -102,8 +144,8 @@ fun InviteListContent(
) { invite ->
InviteSummaryRow(
invite = invite,
onAcceptClicked = onAcceptClicked,
onDeclineClicked = onDeclineClicked,
onAcceptClicked = { state.eventSink(InviteListEvents.AcceptInvite(invite)) },
onDeclineClicked = { state.eventSink(InviteListEvents.DeclineInvite(invite)) },
)
}
}

13
features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt

@ -59,7 +59,6 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight @@ -59,7 +59,6 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.api.core.RoomId
import kotlinx.collections.immutable.persistentMapOf
import io.element.android.libraries.ui.strings.R as StringR
@ -69,8 +68,8 @@ private val minHeight = 72.dp @@ -69,8 +68,8 @@ private val minHeight = 72.dp
internal fun InviteSummaryRow(
invite: InviteListInviteSummary,
modifier: Modifier = Modifier,
onAcceptClicked: (RoomId) -> Unit = {},
onDeclineClicked: (RoomId) -> Unit = {},
onAcceptClicked: () -> Unit = {},
onDeclineClicked: () -> Unit = {},
) {
Box(
modifier = modifier
@ -88,8 +87,8 @@ internal fun InviteSummaryRow( @@ -88,8 +87,8 @@ internal fun InviteSummaryRow(
@Composable
internal fun DefaultInviteSummaryRow(
invite: InviteListInviteSummary,
onAcceptClicked: (RoomId) -> Unit = {},
onDeclineClicked: (RoomId) -> Unit = {},
onAcceptClicked: () -> Unit = {},
onDeclineClicked: () -> Unit = {},
) {
Row(
modifier = Modifier
@ -138,7 +137,7 @@ internal fun DefaultInviteSummaryRow( @@ -138,7 +137,7 @@ internal fun DefaultInviteSummaryRow(
Row(Modifier.padding(top = 12.dp)) {
OutlinedButton(
content = { Text(stringResource(StringR.string.action_decline), style = ElementTextStyles.Button) },
onClick = { onDeclineClicked(invite.roomId) },
onClick = onDeclineClicked,
modifier = Modifier.weight(1f),
contentPadding = PaddingValues(horizontal = 24.dp, vertical = 7.dp),
)
@ -147,7 +146,7 @@ internal fun DefaultInviteSummaryRow( @@ -147,7 +146,7 @@ internal fun DefaultInviteSummaryRow(
Button(
content = { Text(stringResource(StringR.string.action_accept), style = ElementTextStyles.Button) },
onClick = { onAcceptClicked(invite.roomId) },
onClick = onAcceptClicked,
modifier = Modifier.weight(1f),
contentPadding = PaddingValues(horizontal = 24.dp, vertical = 7.dp),
)

3
features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/model/InviteListInviteSummary.kt

@ -27,7 +27,8 @@ data class InviteListInviteSummary( @@ -27,7 +27,8 @@ data class InviteListInviteSummary(
val roomName: String = "",
val roomAlias: String? = null,
val roomAvatarData: AvatarData = AvatarData(roomId.value, roomName),
val sender: InviteSender? = null
val sender: InviteSender? = null,
val isDirect: Boolean = false
)
data class InviteSender(

355
features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt

@ -20,7 +20,9 @@ import app.cash.molecule.RecompositionClock @@ -20,7 +20,9 @@ import app.cash.molecule.RecompositionClock
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.RoomMembershipState
import io.element.android.libraries.matrix.api.room.RoomSummary
@ -32,6 +34,7 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID @@ -32,6 +34,7 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.A_USER_NAME
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
@ -80,32 +83,7 @@ class InviteListPresenterTests { @@ -80,32 +83,7 @@ class InviteListPresenterTests {
@Test
fun `present - uses user ID and avatar for direct invites`() = runTest {
val invitesDataSource = FakeRoomSummaryDataSource()
invitesDataSource.postRoomSummary(
listOf(
RoomSummary.Filled(
RoomSummaryDetails(
roomId = A_ROOM_ID,
name = A_USER_NAME,
avatarURLString = null,
isDirect = true,
lastMessage = null,
lastMessageTimestamp = null,
unreadNotificationCount = 0,
inviter = RoomMember(
userId = A_USER_ID,
displayName = A_USER_NAME,
avatarUrl = AN_AVATAR_URL,
membership = RoomMembershipState.JOIN,
isNameAmbiguous = false,
powerLevel = 0,
normalizedPowerLevel = 0,
isIgnored = false,
)
)
)
)
)
val invitesDataSource = FakeRoomSummaryDataSource().withDirectChatInvitation()
val presenter = InviteListPresenter(
FakeMatrixClient(
sessionId = A_SESSION_ID,
@ -119,7 +97,7 @@ class InviteListPresenterTests { @@ -119,7 +97,7 @@ class InviteListPresenterTests {
Truth.assertThat(withInviteState.inviteList.size).isEqualTo(1)
Truth.assertThat(withInviteState.inviteList[0].roomId).isEqualTo(A_ROOM_ID)
Truth.assertThat(withInviteState.inviteList[0].roomAlias).isEqualTo(A_USER_ID.value)
Truth.assertThat(withInviteState.inviteList[0].roomName).isEqualTo(A_USER_NAME)
Truth.assertThat(withInviteState.inviteList[0].roomName).isEqualTo(A_ROOM_NAME)
Truth.assertThat(withInviteState.inviteList[0].roomAvatarData).isEqualTo(
AvatarData(
id = A_USER_ID.value,
@ -133,13 +111,283 @@ class InviteListPresenterTests { @@ -133,13 +111,283 @@ class InviteListPresenterTests {
@Test
fun `present - includes sender details for room invites`() = runTest {
val invitesDataSource = FakeRoomSummaryDataSource()
invitesDataSource.postRoomSummary(
val invitesDataSource = FakeRoomSummaryDataSource().withRoomInvitation()
val presenter = InviteListPresenter(
FakeMatrixClient(
sessionId = A_SESSION_ID,
invitesDataSource = invitesDataSource,
)
)
moleculeFlow(RecompositionClock.Immediate) {
presenter.present()
}.test {
val withInviteState = awaitItem()
Truth.assertThat(withInviteState.inviteList.size).isEqualTo(1)
Truth.assertThat(withInviteState.inviteList[0].sender?.displayName).isEqualTo(A_USER_NAME)
Truth.assertThat(withInviteState.inviteList[0].sender?.userId).isEqualTo(A_USER_ID)
Truth.assertThat(withInviteState.inviteList[0].sender?.avatarData).isEqualTo(
AvatarData(
id = A_USER_ID.value,
name = A_USER_NAME,
url = AN_AVATAR_URL,
)
)
}
}
@Test
fun `present - shows confirm dialog for declining direct chat invites`() = runTest {
val invitesDataSource = FakeRoomSummaryDataSource().withDirectChatInvitation()
val presenter = InviteListPresenter(
FakeMatrixClient(
sessionId = A_SESSION_ID,
invitesDataSource = invitesDataSource,
)
)
moleculeFlow(RecompositionClock.Immediate) {
presenter.present()
}.test {
val originalState = awaitItem()
originalState.eventSink(InviteListEvents.DeclineInvite(originalState.inviteList[0]))
val newState = awaitItem()
Truth.assertThat(newState.declineConfirmationDialog).isInstanceOf(InviteDeclineConfirmationDialog.Visible::class.java)
val confirmDialog = newState.declineConfirmationDialog as InviteDeclineConfirmationDialog.Visible
Truth.assertThat(confirmDialog.isDirect).isTrue()
Truth.assertThat(confirmDialog.name).isEqualTo(A_ROOM_NAME)
}
}
@Test
fun `present - shows confirm dialog for declining room invites`() = runTest {
val invitesDataSource = FakeRoomSummaryDataSource().withRoomInvitation()
val presenter = InviteListPresenter(
FakeMatrixClient(
sessionId = A_SESSION_ID,
invitesDataSource = invitesDataSource,
)
)
moleculeFlow(RecompositionClock.Immediate) {
presenter.present()
}.test {
val originalState = awaitItem()
originalState.eventSink(InviteListEvents.DeclineInvite(originalState.inviteList[0]))
val newState = awaitItem()
Truth.assertThat(newState.declineConfirmationDialog).isInstanceOf(InviteDeclineConfirmationDialog.Visible::class.java)
val confirmDialog = newState.declineConfirmationDialog as InviteDeclineConfirmationDialog.Visible
Truth.assertThat(confirmDialog.isDirect).isFalse()
Truth.assertThat(confirmDialog.name).isEqualTo(A_ROOM_NAME)
}
}
@Test
fun `present - hides confirm dialog when cancelling`() = runTest {
val invitesDataSource = FakeRoomSummaryDataSource().withRoomInvitation()
val presenter = InviteListPresenter(
FakeMatrixClient(
sessionId = A_SESSION_ID,
invitesDataSource = invitesDataSource,
)
)
moleculeFlow(RecompositionClock.Immediate) {
presenter.present()
}.test {
val originalState = awaitItem()
originalState.eventSink(InviteListEvents.DeclineInvite(originalState.inviteList[0]))
skipItems(1)
originalState.eventSink(InviteListEvents.CancelDeclineInvite)
val newState = awaitItem()
Truth.assertThat(newState.declineConfirmationDialog).isInstanceOf(InviteDeclineConfirmationDialog.Hidden::class.java)
}
}
@Test
fun `present - declines invite after confirming`() = runTest {
val invitesDataSource = FakeRoomSummaryDataSource().withRoomInvitation()
val client = FakeMatrixClient(
sessionId = A_SESSION_ID,
invitesDataSource = invitesDataSource,
)
val room = FakeMatrixRoom()
val presenter = InviteListPresenter(client)
client.givenGetRoomResult(A_ROOM_ID, room)
moleculeFlow(RecompositionClock.Immediate) {
presenter.present()
}.test {
val originalState = awaitItem()
originalState.eventSink(InviteListEvents.DeclineInvite(originalState.inviteList[0]))
skipItems(1)
originalState.eventSink(InviteListEvents.ConfirmDeclineInvite)
skipItems(2)
Truth.assertThat(room.isInviteRejected).isTrue()
}
}
@Test
fun `present - declines invite after confirming and sets state on error`() = runTest {
val invitesDataSource = FakeRoomSummaryDataSource().withRoomInvitation()
val client = FakeMatrixClient(
sessionId = A_SESSION_ID,
invitesDataSource = invitesDataSource,
)
val room = FakeMatrixRoom()
val presenter = InviteListPresenter(client)
val ex = Throwable("Ruh roh!")
room.givenRejectInviteResult(Result.failure(ex))
client.givenGetRoomResult(A_ROOM_ID, room)
moleculeFlow(RecompositionClock.Immediate) {
presenter.present()
}.test {
val originalState = awaitItem()
originalState.eventSink(InviteListEvents.DeclineInvite(originalState.inviteList[0]))
skipItems(1)
originalState.eventSink(InviteListEvents.ConfirmDeclineInvite)
skipItems(1)
val newState = awaitItem()
Truth.assertThat(room.isInviteRejected).isTrue()
Truth.assertThat(newState.declinedAction).isEqualTo(Async.Failure<Unit>(ex))
}
}
@Test
fun `present - dismisses declining error state`() = runTest {
val invitesDataSource = FakeRoomSummaryDataSource().withRoomInvitation()
val client = FakeMatrixClient(
sessionId = A_SESSION_ID,
invitesDataSource = invitesDataSource,
)
val room = FakeMatrixRoom()
val presenter = InviteListPresenter(client)
val ex = Throwable("Ruh roh!")
room.givenRejectInviteResult(Result.failure(ex))
client.givenGetRoomResult(A_ROOM_ID, room)
moleculeFlow(RecompositionClock.Immediate) {
presenter.present()
}.test {
val originalState = awaitItem()
originalState.eventSink(InviteListEvents.DeclineInvite(originalState.inviteList[0]))
skipItems(1)
originalState.eventSink(InviteListEvents.ConfirmDeclineInvite)
skipItems(2)
originalState.eventSink(InviteListEvents.DismissDeclineError)
val newState = awaitItem()
Truth.assertThat(newState.declinedAction).isEqualTo(Async.Uninitialized)
}
}
@Test
fun `present - accepts invites and sets state on success`() = runTest {
val invitesDataSource = FakeRoomSummaryDataSource().withRoomInvitation()
val client = FakeMatrixClient(
sessionId = A_SESSION_ID,
invitesDataSource = invitesDataSource,
)
val room = FakeMatrixRoom()
val presenter = InviteListPresenter(client)
client.givenGetRoomResult(A_ROOM_ID, room)
moleculeFlow(RecompositionClock.Immediate) {
presenter.present()
}.test {
val originalState = awaitItem()
originalState.eventSink(InviteListEvents.AcceptInvite(originalState.inviteList[0]))
val newState = awaitItem()
Truth.assertThat(room.isInviteAccepted).isTrue()
Truth.assertThat(newState.acceptedAction).isEqualTo(Async.Success(A_ROOM_ID))
}
}
@Test
fun `present - accepts invites and sets state on error`() = runTest {
val invitesDataSource = FakeRoomSummaryDataSource().withRoomInvitation()
val client = FakeMatrixClient(
sessionId = A_SESSION_ID,
invitesDataSource = invitesDataSource,
)
val room = FakeMatrixRoom()
val presenter = InviteListPresenter(client)
val ex = Throwable("Ruh roh!")
room.givenAcceptInviteResult(Result.failure(ex))
client.givenGetRoomResult(A_ROOM_ID, room)
moleculeFlow(RecompositionClock.Immediate) {
presenter.present()
}.test {
val originalState = awaitItem()
originalState.eventSink(InviteListEvents.AcceptInvite(originalState.inviteList[0]))
val newState = awaitItem()
Truth.assertThat(room.isInviteAccepted).isTrue()
Truth.assertThat(newState.acceptedAction).isEqualTo(Async.Failure<RoomId>(ex))
}
}
@Test
fun `present - dismisses accepting error state`() = runTest {
val invitesDataSource = FakeRoomSummaryDataSource().withRoomInvitation()
val client = FakeMatrixClient(
sessionId = A_SESSION_ID,
invitesDataSource = invitesDataSource,
)
val room = FakeMatrixRoom()
val presenter = InviteListPresenter(client)
val ex = Throwable("Ruh roh!")
room.givenAcceptInviteResult(Result.failure(ex))
client.givenGetRoomResult(A_ROOM_ID, room)
moleculeFlow(RecompositionClock.Immediate) {
presenter.present()
}.test {
val originalState = awaitItem()
originalState.eventSink(InviteListEvents.AcceptInvite(originalState.inviteList[0]))
skipItems(1)
originalState.eventSink(InviteListEvents.DismissAcceptError)
val newState = awaitItem()
Truth.assertThat(newState.acceptedAction).isEqualTo(Async.Uninitialized)
}
}
private suspend fun FakeRoomSummaryDataSource.withRoomInvitation(): FakeRoomSummaryDataSource {
postRoomSummary(
listOf(
RoomSummary.Filled(
RoomSummaryDetails(
roomId = A_ROOM_ID,
name = A_USER_NAME,
name = A_ROOM_NAME,
avatarURLString = null,
isDirect = false,
lastMessage = null,
@ -159,26 +407,35 @@ class InviteListPresenterTests { @@ -159,26 +407,35 @@ class InviteListPresenterTests {
)
)
)
val presenter = InviteListPresenter(
FakeMatrixClient(
sessionId = A_SESSION_ID,
invitesDataSource = invitesDataSource,
)
)
moleculeFlow(RecompositionClock.Immediate) {
presenter.present()
}.test {
val withInviteState = awaitItem()
Truth.assertThat(withInviteState.inviteList.size).isEqualTo(1)
Truth.assertThat(withInviteState.inviteList[0].sender?.displayName).isEqualTo(A_USER_NAME)
Truth.assertThat(withInviteState.inviteList[0].sender?.userId).isEqualTo(A_USER_ID)
Truth.assertThat(withInviteState.inviteList[0].sender?.avatarData).isEqualTo(
AvatarData(
id = A_USER_ID.value,
name = A_USER_NAME,
url = AN_AVATAR_URL,
return this
}
private suspend fun FakeRoomSummaryDataSource.withDirectChatInvitation(): FakeRoomSummaryDataSource {
postRoomSummary(
listOf(
RoomSummary.Filled(
RoomSummaryDetails(
roomId = A_ROOM_ID,
name = A_ROOM_NAME,
avatarURLString = null,
isDirect = true,
lastMessage = null,
lastMessageTimestamp = null,
unreadNotificationCount = 0,
inviter = RoomMember(
userId = A_USER_ID,
displayName = A_USER_NAME,
avatarUrl = AN_AVATAR_URL,
membership = RoomMembershipState.JOIN,
isNameAmbiguous = false,
powerLevel = 0,
normalizedPowerLevel = 0,
isIgnored = false,
)
)
)
)
}
)
return this
}
}

18
gradle/libs.versions.toml

@ -4,8 +4,8 @@ @@ -4,8 +4,8 @@
[versions]
# Project
android_gradle_plugin = "8.0.0"
kotlin = "1.8.10"
ksp = "1.8.10-1.0.9"
kotlin = "1.8.20"
ksp = "1.8.20-1.0.11"
molecule = "0.9.0"
# AndroidX
@ -19,8 +19,8 @@ activity = "1.7.1" @@ -19,8 +19,8 @@ activity = "1.7.1"
startup = "1.1.1"
# Compose
compose_bom = "2023.04.00"
composecompiler = "1.4.2"
compose_bom = "2023.04.01"
composecompiler = "1.4.6"
# Coroutines
coroutines = "1.6.4"
@ -44,7 +44,7 @@ sqldelight = "1.5.5" @@ -44,7 +44,7 @@ sqldelight = "1.5.5"
# DI
dagger = "2.45"
anvil = "2.4.4"
anvil = "2.4.5"
# quality
detekt = "1.22.0"
@ -67,7 +67,7 @@ androidx_constraintlayout = { module = "androidx.constraintlayout:constraintlayo @@ -67,7 +67,7 @@ androidx_constraintlayout = { module = "androidx.constraintlayout:constraintlayo
androidx_recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }
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.0"
androidx_splash = "androidx.core:core-splashscreen:1.0.1"
androidx_security_crypto = "androidx.security:security-crypto:1.0.0"
androidx_activity_activity = { module = "androidx.activity:activity", version.ref = "activity" }
@ -95,9 +95,9 @@ accompanist_flowlayout = { module = "com.google.accompanist:accompanist-flowlayo @@ -95,9 +95,9 @@ accompanist_flowlayout = { module = "com.google.accompanist:accompanist-flowlayo
squareup_seismic = "com.squareup:seismic:1.0.3"
# network
network_okhttp_bom = "com.squareup.okhttp3:okhttp-bom:4.10.0"
network_okhttp_bom = "com.squareup.okhttp3:okhttp-bom:4.11.0"
network_retrofit = "com.squareup.retrofit2:retrofit:2.9.0"
network_retrofit_converter_serialization = "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0"
network_retrofit_converter_serialization = "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0"
# Test
test_core = { module = "androidx.test:core", version.ref = "test_core" }
@ -128,7 +128,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } @@ -128,7 +128,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" }
timber = "com.jakewharton.timber:timber:5.0.1"
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.9"
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.10"
sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" }
sqldelight-driver-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" }
sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" }

2
libraries/matrix/api/build.gradle.kts

@ -18,7 +18,7 @@ plugins { @@ -18,7 +18,7 @@ plugins {
id("io.element.android-library")
id("kotlin-parcelize")
alias(libs.plugins.anvil)
kotlin("plugin.serialization") version "1.8.10"
kotlin("plugin.serialization") version "1.8.20"
}
android {

4
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt

@ -69,6 +69,10 @@ interface MatrixRoom : Closeable { @@ -69,6 +69,10 @@ interface MatrixRoom : Closeable {
suspend fun redactEvent(eventId: EventId, reason: String? = null): Result<Unit>
suspend fun leave(): Result<Unit>
suspend fun acceptInvitation(): Result<Unit>
suspend fun rejectInvitation(): Result<Unit>
}
fun MatrixRoom.getMemberFlow(userId: UserId): Flow<RoomMember?> {

2
libraries/matrix/impl/build.gradle.kts

@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
plugins {
id("io.element.android-library")
alias(libs.plugins.anvil)
kotlin("plugin.serialization") version "1.8.10"
kotlin("plugin.serialization") version "1.8.20"
}
android {

2
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryDetailsFactory.kt

@ -33,7 +33,7 @@ class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFacto @@ -33,7 +33,7 @@ class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFacto
roomId = RoomId(slidingSyncRoom.roomId()),
name = slidingSyncRoom.name() ?: slidingSyncRoom.roomId(),
canonicalAlias = room?.canonicalAlias(),
isDirect = slidingSyncRoom.isDm() ?: false,
isDirect = room?.isDirect() ?: false,
avatarURLString = room?.avatarUrl(),
unreadNotificationCount = slidingSyncRoom.unreadNotifications().use { it.notificationCount().toInt() },
lastMessage = latestRoomMessage,

13
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt

@ -182,4 +182,17 @@ class RustMatrixRoom( @@ -182,4 +182,17 @@ class RustMatrixRoom(
innerRoom.leave()
}
}
override suspend fun acceptInvitation(): Result<Unit> = withContext(coroutineDispatchers.io) {
kotlin.runCatching {
innerRoom.acceptInvitation()
}
}
override suspend fun rejectInvitation(): Result<Unit> = withContext(coroutineDispatchers.io) {
kotlin.runCatching {
innerRoom.rejectInvitation()
}
}
}

7
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt

@ -52,9 +52,10 @@ class FakeMatrixClient( @@ -52,9 +52,10 @@ class FakeMatrixClient(
private var createDmFailure: Throwable? = null
private var findDmResult: MatrixRoom? = FakeMatrixRoom()
private var logoutFailure: Throwable? = null
private val getRoomResults = mutableMapOf<RoomId, MatrixRoom>()
override fun getRoom(roomId: RoomId): MatrixRoom? {
return FakeMatrixRoom(sessionId = sessionId, roomId = roomId)
return getRoomResults[roomId]
}
override fun findDM(userId: UserId): MatrixRoom? {
@ -136,4 +137,8 @@ class FakeMatrixClient( @@ -136,4 +137,8 @@ class FakeMatrixClient(
fun givenFindDmResult(result: MatrixRoom?) {
findDmResult = result
}
fun givenGetRoomResult(roomId: RoomId, result: MatrixRoom) {
getRoomResults[roomId] = result
}
}

27
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt

@ -22,6 +22,7 @@ import io.element.android.libraries.matrix.api.core.SessionId @@ -22,6 +22,7 @@ import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_SESSION_ID
@ -50,6 +51,14 @@ class FakeMatrixRoom( @@ -50,6 +51,14 @@ class FakeMatrixRoom(
private var userDisplayNameResult = Result.success<String?>(null)
private var userAvatarUrlResult = Result.success<String?>(null)
private var updateMembersResult: Result<Unit> = Result.success(Unit)
private var acceptInviteResult = Result.success(Unit)
private var rejectInviteResult = Result.success(Unit)
var isInviteAccepted: Boolean = false
private set
var isInviteRejected: Boolean = false
private set
private var leaveRoomError: Throwable? = null
@ -108,6 +117,15 @@ class FakeMatrixRoom( @@ -108,6 +117,15 @@ class FakeMatrixRoom(
}
override suspend fun leave(): Result<Unit> = leaveRoomError?.let { Result.failure(it) } ?: Result.success(Unit)
override suspend fun acceptInvitation(): Result<Unit> {
isInviteAccepted = true
return acceptInviteResult
}
override suspend fun rejectInvitation(): Result<Unit> {
isInviteRejected = true
return rejectInviteResult
}
override fun close() = Unit
@ -130,4 +148,13 @@ class FakeMatrixRoom( @@ -130,4 +148,13 @@ class FakeMatrixRoom(
fun givenUserAvatarUrlResult(avatarUrl: Result<String?>) {
userAvatarUrlResult = avatarUrl
}
fun givenAcceptInviteResult(result: Result<Unit>) {
acceptInviteResult = result
}
fun givenRejectInviteResult(result: Result<Unit>) {
rejectInviteResult = result
}
}

2
libraries/push/impl/build.gradle.kts

@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
plugins {
id("io.element.android-library")
alias(libs.plugins.anvil)
kotlin("plugin.serialization") version "1.8.10"
kotlin("plugin.serialization") version "1.8.20"
}
android {

2
libraries/pushproviders/unifiedpush/build.gradle.kts

@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
plugins {
id("io.element.android-library")
alias(libs.plugins.anvil)
kotlin("plugin.serialization") version "1.8.10"
kotlin("plugin.serialization") version "1.8.20"
}
android {

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_5,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.
Loading…
Cancel
Save