From 0550da3e3bc92b0300aea49d18c3ba5ab9888e8b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 15 Apr 2024 20:09:09 +0200 Subject: [PATCH 01/46] Add MatrixClient.resolveRoomAlias method. --- .../io/element/android/libraries/matrix/api/MatrixClient.kt | 1 + .../android/libraries/matrix/impl/RustMatrixClient.kt | 6 ++++++ .../android/libraries/matrix/test/FakeMatrixClient.kt | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index 24a034c070..bf3d75edf6 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -98,4 +98,5 @@ interface MatrixClient : Closeable { suspend fun trackRecentlyVisitedRoom(roomId: RoomId): Result suspend fun getRecentlyVisitedRooms(): Result> + suspend fun resolveRoomAlias(roomAlias: String): Result } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 8a3ff31c62..81e403a65e 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -460,6 +460,12 @@ class RustMatrixClient( } } + override suspend fun resolveRoomAlias(roomAlias: String): Result = withContext(sessionDispatcher) { + runCatching { + client.resolveRoomAlias(roomAlias).let(::RoomId) + } + } + override fun syncService(): SyncService = rustSyncService override fun sessionVerificationService(): SessionVerificationService = verificationService diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index 3feb161cb3..70e5fc97a0 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -73,6 +73,7 @@ class FakeMatrixClient( private val encryptionService: FakeEncryptionService = FakeEncryptionService(), private val roomDirectoryService: RoomDirectoryService = FakeRoomDirectoryService(), private val accountManagementUrlString: Result = Result.success(null), + private val resolveRoomAliasResult: (String) -> Result = { Result.success(A_ROOM_ID) }, ) : MatrixClient { var setDisplayNameCalled: Boolean = false private set @@ -276,6 +277,10 @@ class FakeMatrixClient( return Result.success(Unit) } + override suspend fun resolveRoomAlias(roomAlias: String): Result { + return resolveRoomAliasResult(roomAlias) + } + override suspend fun getRecentlyVisitedRooms(): Result> { return Result.success(visitedRoomsId) } From fb3d49cc2f90c9e36b03c9de0ef1ae87f8d69962 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 15 Apr 2024 20:27:06 +0200 Subject: [PATCH 02/46] Add MatrixClient.getRoomPreview method. --- .../libraries/matrix/api/MatrixClient.kt | 2 + .../matrix/api/room/preview/RoomPreview.kt | 46 +++++++++++++++++++ .../libraries/matrix/impl/RustMatrixClient.kt | 8 ++++ .../impl/room/preview/RoomPreviewMapper.kt | 40 ++++++++++++++++ .../libraries/matrix/test/FakeMatrixClient.kt | 6 +++ 5 files changed, 102 insertions(+) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/preview/RoomPreview.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/preview/RoomPreviewMapper.kt diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index bf3d75edf6..759b6cd47c 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -30,6 +30,7 @@ import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.RoomMembershipObserver +import io.element.android.libraries.matrix.api.room.preview.RoomPreview import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.sync.SyncService @@ -99,4 +100,5 @@ interface MatrixClient : Closeable { suspend fun trackRecentlyVisitedRoom(roomId: RoomId): Result suspend fun getRecentlyVisitedRooms(): Result> suspend fun resolveRoomAlias(roomAlias: String): Result + suspend fun getRoomPreview(roomIdOrAlias: String): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/preview/RoomPreview.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/preview/RoomPreview.kt new file mode 100644 index 0000000000..8ee7550b74 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/preview/RoomPreview.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 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.libraries.matrix.api.room.preview + +import io.element.android.libraries.matrix.api.core.RoomId + +data class RoomPreview( + /** The room id for this room. */ + val roomId: RoomId, + /** The canonical alias for the room. */ + val canonicalAlias: String?, + /** The room's name, if set. */ + val name: String?, + /** The room's topic, if set. */ + val topic: String?, + /** The MXC URI to the room's avatar, if set. */ + val avatarUrl: String?, + /** The number of joined members. */ + val numberOfJoinedMembers: Long, + /** The room type (space, custom) or nothing, if it's a regular room. */ + val roomType: String?, + /** Is the history world-readable for this room? */ + val isHistoryWorldReadable: Boolean, + /** Is the room joined by the current user? */ + val isJoined: Boolean, + /** Is the current user invited to this room? */ + val isInvited: Boolean, + /** is the join rule public for this room? */ + val isPublic: Boolean, + /** Can we knock (or restricted-knock) to this room? */ + val canKnock: Boolean, +) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 81e403a65e..cd7100f667 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -37,6 +37,7 @@ import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.RoomMembershipObserver +import io.element.android.libraries.matrix.api.room.preview.RoomPreview import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.awaitLoaded @@ -58,6 +59,7 @@ import io.element.android.libraries.matrix.impl.room.RoomContentForwarder import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber import io.element.android.libraries.matrix.impl.room.RustMatrixRoom import io.element.android.libraries.matrix.impl.room.map +import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewMapper import io.element.android.libraries.matrix.impl.roomdirectory.RustRoomDirectoryService import io.element.android.libraries.matrix.impl.roomlist.RoomListFactory import io.element.android.libraries.matrix.impl.roomlist.RustRoomListService @@ -466,6 +468,12 @@ class RustMatrixClient( } } + override suspend fun getRoomPreview(roomIdOrAlias: String): Result = withContext(sessionDispatcher) { + runCatching { + client.getRoomPreview(roomIdOrAlias).let(RoomPreviewMapper::map) + } + } + override fun syncService(): SyncService = rustSyncService override fun sessionVerificationService(): SessionVerificationService = verificationService diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/preview/RoomPreviewMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/preview/RoomPreviewMapper.kt new file mode 100644 index 0000000000..fb18205978 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/preview/RoomPreviewMapper.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 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.libraries.matrix.impl.room.preview + +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.preview.RoomPreview +import org.matrix.rustcomponents.sdk.RoomPreview as RustRoomPreview + +object RoomPreviewMapper { + fun map(roomPreview: RustRoomPreview): RoomPreview { + return RoomPreview( + roomId = RoomId(roomPreview.roomId), + canonicalAlias = roomPreview.canonicalAlias, + name = roomPreview.name, + topic = roomPreview.topic, + avatarUrl = roomPreview.avatarUrl, + numberOfJoinedMembers = roomPreview.numJoinedMembers.toLong(), + roomType = roomPreview.roomType, + isHistoryWorldReadable = roomPreview.isHistoryWorldReadable, + isJoined = roomPreview.isJoined, + isInvited = roomPreview.isInvited, + isPublic = roomPreview.isPublic, + canKnock = roomPreview.canKnock + ) + } +} diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index 70e5fc97a0..7bb880986f 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -31,6 +31,7 @@ import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.RoomMembershipObserver +import io.element.android.libraries.matrix.api.room.preview.RoomPreview import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults @@ -74,6 +75,7 @@ class FakeMatrixClient( private val roomDirectoryService: RoomDirectoryService = FakeRoomDirectoryService(), private val accountManagementUrlString: Result = Result.success(null), private val resolveRoomAliasResult: (String) -> Result = { Result.success(A_ROOM_ID) }, + private val getRoomPreviewResult: (String) -> Result = { TODO("Not implemented") }, ) : MatrixClient { var setDisplayNameCalled: Boolean = false private set @@ -281,6 +283,10 @@ class FakeMatrixClient( return resolveRoomAliasResult(roomAlias) } + override suspend fun getRoomPreview(roomIdOrAlias: String): Result { + return getRoomPreviewResult(roomIdOrAlias) + } + override suspend fun getRecentlyVisitedRooms(): Result> { return Result.success(visitedRoomsId) } From 6a46555462183ef2e638473394f86a5e883b18f5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Apr 2024 10:21:26 +0200 Subject: [PATCH 03/46] Handle permalink navigation - WIP - prepare navigating to an Event - add NodeBuilder to MessagesEntryPoint --- .../android/appnav/LoggedInFlowNode.kt | 37 ++++++++++++++++++- .../android/appnav/room/RoomFlowNode.kt | 2 +- .../appnav/room/RoomNavigationTarget.kt | 17 +++++++-- .../appnav/room/joined/JoinedRoomFlowNode.kt | 2 +- .../room/joined/JoinedRoomLoadedFlowNode.kt | 22 ++++++++--- .../appnav/JoinRoomLoadedFlowNodeTest.kt | 30 +++++++++++---- .../joinroom/impl/JoinRoomPresenter.kt | 31 +++++++++++++++- .../messages/api/MessagesEntryPoint.kt | 19 +++++++--- .../impl/DefaultMessagesEntryPoint.kt | 25 ++++++++++--- .../messages/impl/MessagesFlowNode.kt | 19 ++++++++-- .../features/messages/impl/MessagesNode.kt | 25 +++++++++---- 11 files changed, 186 insertions(+), 43 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index 1bde7c5b28..190623c9b6 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -66,6 +66,7 @@ import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.MAIN_SPACE import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.services.appnavstate.api.AppNavigationStateService import kotlinx.coroutines.CoroutineScope @@ -191,7 +192,7 @@ class LoggedInFlowNode @AssistedInject constructor( data class Room( val roomId: RoomId, val roomDescription: RoomDescription? = null, - val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages + val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages(null) ) : NavTarget @Parcelize @@ -270,6 +271,40 @@ class LoggedInFlowNode @AssistedInject constructor( coroutineScope.launch { attachRoom(roomId) } } + override fun onPermalinkClicked(data: PermalinkData) { + coroutineScope.launch { + when (data) { + is PermalinkData.UserLink -> { + // FIXME: Add a user profile screen. + Timber.e("User link clicked: ${data.userId}. TODO Add a user profile screen") + } + is PermalinkData.RoomIdLink -> { + backstack.push(NavTarget.Room(data.roomId)) + } + is PermalinkData.RoomAliasLink -> { + // FIXME Implement room alias navigation + Timber.e("Room alias link clicked: ${data.roomAlias}. TODO Handle a room alias navigation") + } + is PermalinkData.EventIdAliasLink -> { + // FIXME Implement event alias navigation + Timber.e("Event with room alias link clicked: ${data.eventId}. TODO Handle an event with room alias navigation") + } + is PermalinkData.EventIdLink -> { + backstack.push( + NavTarget.Room( + data.roomId, + initialElement = RoomNavigationTarget.Messages(data.eventId) + ) + ) + } + is PermalinkData.FallbackLink, + is PermalinkData.RoomEmailInviteLink -> { + // Should not happen (handled by MessagesNode) + } + } + } + } + override fun onOpenGlobalNotificationSettings() { backstack.push(NavTarget.Settings(PreferencesEntryPoint.InitialTarget.NotificationSettings)) } 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 db3664e631..2f62045b22 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 @@ -75,7 +75,7 @@ class RoomFlowNode @AssistedInject constructor( data class Inputs( val roomId: RoomId, val roomDescription: Optional, - val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages, + val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages(), ) : NodeInputs private val inputs: Inputs = inputs() diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomNavigationTarget.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomNavigationTarget.kt index 901b2667be..776171d141 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomNavigationTarget.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomNavigationTarget.kt @@ -16,8 +16,17 @@ package io.element.android.appnav.room -enum class RoomNavigationTarget { - Messages, - Details, - NotificationSettings, +import android.os.Parcelable +import io.element.android.libraries.matrix.api.core.EventId +import kotlinx.parcelize.Parcelize + +sealed interface RoomNavigationTarget : Parcelable { + @Parcelize + data class Messages(val eventId: EventId? = null) : RoomNavigationTarget + + @Parcelize + data object Details : RoomNavigationTarget + + @Parcelize + data object NotificationSettings : RoomNavigationTarget } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomFlowNode.kt index 36def888ac..ae8d03be33 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomFlowNode.kt @@ -69,7 +69,7 @@ class JoinedRoomFlowNode @AssistedInject constructor( ) { data class Inputs( val roomId: RoomId, - val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages, + val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages(), ) : NodeInputs private val inputs: Inputs = inputs() 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 a5d7893c91..7a6c736385 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 @@ -42,8 +42,10 @@ import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.DaggerComponentOwner import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.services.appnavstate.api.AppNavigationStateService import kotlinx.coroutines.CoroutineScope @@ -63,8 +65,8 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor( roomComponentFactory: RoomComponentFactory, ) : BaseFlowNode( backstack = BackStack( - initialElement = when (plugins.filterIsInstance(Inputs::class.java).first().initialElement) { - RoomNavigationTarget.Messages -> NavTarget.Messages + initialElement = when (val input = plugins.filterIsInstance(Inputs::class.java).first().initialElement) { + is RoomNavigationTarget.Messages -> NavTarget.Messages(input.eventId) RoomNavigationTarget.Details -> NavTarget.RoomDetails RoomNavigationTarget.NotificationSettings -> NavTarget.RoomNotificationSettings }, @@ -75,13 +77,14 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor( ), DaggerComponentOwner { interface Callback : Plugin { fun onOpenRoom(roomId: RoomId) + fun onPermalinkClicked(data: PermalinkData) fun onForwardedToSingleRoom(roomId: RoomId) fun onOpenGlobalNotificationSettings() } data class Inputs( val room: MatrixRoom, - val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages, + val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages(), ) : NodeInputs private val inputs: Inputs = inputs() @@ -139,7 +142,7 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor( override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { - NavTarget.Messages -> { + is NavTarget.Messages -> { val callback = object : MessagesEntryPoint.Callback { override fun onRoomDetailsClicked() { backstack.push(NavTarget.RoomDetails) @@ -149,11 +152,18 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor( backstack.push(NavTarget.RoomMemberDetails(userId)) } + override fun onPermalinkClicked(data: PermalinkData) { + callbacks.forEach { it.onPermalinkClicked(data) } + } + override fun onForwardedToSingleRoom(roomId: RoomId) { callbacks.forEach { it.onForwardedToSingleRoom(roomId) } } } - messagesEntryPoint.createNode(this, buildContext, callback) + messagesEntryPoint.nodeBuilder(this, buildContext) + .params(MessagesEntryPoint.Params(navTarget.eventId)) + .callback(callback) + .build() } NavTarget.RoomDetails -> { createRoomDetailsNode(buildContext, RoomDetailsEntryPoint.InitialTarget.RoomDetails) @@ -169,7 +179,7 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor( sealed interface NavTarget : Parcelable { @Parcelize - data object Messages : NavTarget + data class Messages(val eventId: EventId? = null) : NavTarget @Parcelize data object RoomDetails : NavTarget diff --git a/appnav/src/test/kotlin/io/element/android/appnav/JoinRoomLoadedFlowNodeTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/JoinRoomLoadedFlowNodeTest.kt index 08702eeb5a..2809fc95ee 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/JoinRoomLoadedFlowNodeTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/JoinRoomLoadedFlowNodeTest.kt @@ -47,14 +47,30 @@ class JoinRoomLoadedFlowNodeTest { @get:Rule val mainDispatcherRule = MainDispatcherRule() - private class FakeMessagesEntryPoint : MessagesEntryPoint { + private class FakeMessagesEntryPoint : MessagesEntryPoint, MessagesEntryPoint.NodeBuilder { + var buildContext: BuildContext? = null var nodeId: String? = null + var parameters: MessagesEntryPoint.Params? = null var callback: MessagesEntryPoint.Callback? = null - override fun createNode(parentNode: Node, buildContext: BuildContext, callback: MessagesEntryPoint.Callback): Node { - return node(buildContext) {}.also { + override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): MessagesEntryPoint.NodeBuilder { + this.buildContext = buildContext + return this + } + + override fun params(params: MessagesEntryPoint.Params): MessagesEntryPoint.NodeBuilder { + parameters = params + return this + } + + override fun callback(callback: MessagesEntryPoint.Callback): MessagesEntryPoint.NodeBuilder { + this.callback = callback + return this + } + + override fun build(): Node { + return node(buildContext!!) {}.also { nodeId = it.id - this.callback = callback } } } @@ -118,9 +134,9 @@ class JoinRoomLoadedFlowNodeTest { val roomFlowNodeTestHelper = roomFlowNode.parentNodeTestHelper() // THEN - assertThat(roomFlowNode.backstack.activeElement).isEqualTo(JoinedRoomLoadedFlowNode.NavTarget.Messages) - roomFlowNodeTestHelper.assertChildHasLifecycle(JoinedRoomLoadedFlowNode.NavTarget.Messages, Lifecycle.State.CREATED) - val messagesNode = roomFlowNode.childNode(JoinedRoomLoadedFlowNode.NavTarget.Messages)!! + assertThat(roomFlowNode.backstack.activeElement).isEqualTo(JoinedRoomLoadedFlowNode.NavTarget.Messages()) + roomFlowNodeTestHelper.assertChildHasLifecycle(JoinedRoomLoadedFlowNode.NavTarget.Messages(), Lifecycle.State.CREATED) + val messagesNode = roomFlowNode.childNode(JoinedRoomLoadedFlowNode.NavTarget.Messages())!! assertThat(messagesNode.id).isEqualTo(fakeMessagesEntryPoint.nodeId) } 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 bdef36d6a8..f088603d3b 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 @@ -21,6 +21,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState +import androidx.compose.runtime.rememberCoroutineScope import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents @@ -32,6 +33,8 @@ 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.CurrentUserMembership import io.element.android.libraries.matrix.api.room.MatrixRoomInfo +import io.element.android.libraries.matrix.api.room.preview.RoomPreview +import kotlinx.coroutines.launch import java.util.Optional class JoinRoomPresenter @AssistedInject constructor( @@ -46,6 +49,7 @@ class JoinRoomPresenter @AssistedInject constructor( @Composable override fun present(): JoinRoomState { + val coroutineScope = rememberCoroutineScope() val roomInfo by matrixClient.getRoomInfoFlow(roomId).collectAsState(initial = Optional.empty()) val contentState by produceState(initialValue = ContentState.Loading(roomId), key1 = roomInfo) { value = when { @@ -56,6 +60,12 @@ class JoinRoomPresenter @AssistedInject constructor( roomDescription.get().toContentState() } else -> { + coroutineScope.launch { + val result = matrixClient.getRoomPreview(roomId.value) + value = result.getOrNull() + ?.toContentState() + ?: ContentState.UnknownRoom(roomId) + } ContentState.Loading(roomId) } } @@ -64,7 +74,8 @@ class JoinRoomPresenter @AssistedInject constructor( fun handleEvents(event: JoinRoomEvents) { when (event) { - JoinRoomEvents.AcceptInvite, JoinRoomEvents.JoinRoom -> { + JoinRoomEvents.AcceptInvite, + JoinRoomEvents.JoinRoom -> { val inviteData = contentState.toInviteData() ?: return acceptDeclineInviteState.eventSink( AcceptDeclineInviteEvents.AcceptInvite(inviteData) @@ -87,6 +98,24 @@ class JoinRoomPresenter @AssistedInject constructor( } } +private fun RoomPreview.toContentState(): ContentState { + return ContentState.Loaded( + roomId = roomId, + name = name, + topic = topic, + alias = canonicalAlias, + numberOfMembers = numberOfJoinedMembers, + isDirect = false, + roomAvatarUrl = avatarUrl, + joinAuthorisationStatus = when { + isInvited -> JoinAuthorisationStatus.IsInvited + canKnock -> JoinAuthorisationStatus.CanKnock + isPublic -> JoinAuthorisationStatus.CanJoin + else -> JoinAuthorisationStatus.Unknown + } + ) +} + @VisibleForTesting internal fun RoomDescription.toContentState(): ContentState { return ContentState.Loaded( diff --git a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt index 482dfad8ea..9012d3a776 100644 --- a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt +++ b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt @@ -20,19 +20,28 @@ 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.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.permalink.PermalinkData interface MessagesEntryPoint : FeatureEntryPoint { - fun createNode( - parentNode: Node, - buildContext: BuildContext, - callback: Callback, - ): Node + fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder + + interface NodeBuilder { + fun params(params: Params): NodeBuilder + fun callback(callback: Callback): NodeBuilder + fun build(): Node + } + + data class Params( + val eventId: EventId?, + ) interface Callback : Plugin { fun onRoomDetailsClicked() fun onUserDataClicked(userId: UserId) + fun onPermalinkClicked(data: PermalinkData) fun onForwardedToSingleRoom(roomId: RoomId) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPoint.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPoint.kt index abf451b4b6..b45c7fc6b5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPoint.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPoint.kt @@ -18,6 +18,7 @@ package io.element.android.features.messages.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin import com.squareup.anvil.annotations.ContributesBinding import io.element.android.features.messages.api.MessagesEntryPoint import io.element.android.libraries.architecture.createNode @@ -26,11 +27,23 @@ import javax.inject.Inject @ContributesBinding(AppScope::class) class DefaultMessagesEntryPoint @Inject constructor() : MessagesEntryPoint { - override fun createNode( - parentNode: Node, - buildContext: BuildContext, - callback: MessagesEntryPoint.Callback - ): Node { - return parentNode.createNode(buildContext, listOf(callback)) + override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): MessagesEntryPoint.NodeBuilder { + val plugins = ArrayList() + + return object : MessagesEntryPoint.NodeBuilder { + override fun params(params: MessagesEntryPoint.Params): MessagesEntryPoint.NodeBuilder { + plugins += MessagesNode.Inputs(eventId = params.eventId) + return this + } + + override fun callback(callback: MessagesEntryPoint.Callback): MessagesEntryPoint.NodeBuilder { + plugins += callback + return this + } + + override fun build(): Node { + return parentNode.createNode(buildContext, plugins) + } + } } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index 3cf480f7d1..c8a0a753c4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -52,6 +52,7 @@ import io.element.android.features.poll.api.create.CreatePollEntryPoint import io.element.android.features.poll.api.create.CreatePollMode import io.element.android.libraries.architecture.BackstackWithOverlayBox import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.overlay.Overlay import io.element.android.libraries.architecture.overlay.operation.show @@ -62,6 +63,7 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.mediaviewer.api.local.MediaInfo import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode @@ -79,7 +81,7 @@ class MessagesFlowNode @AssistedInject constructor( private val createPollEntryPoint: CreatePollEntryPoint, ) : BaseFlowNode( backstack = BackStack( - initialElement = NavTarget.Messages, + initialElement = NavTarget.Messages(plugins.filterIsInstance().firstOrNull()?.eventId), savedStateMap = buildContext.savedStateMap, ), overlay = Overlay( @@ -88,12 +90,16 @@ class MessagesFlowNode @AssistedInject constructor( buildContext = buildContext, plugins = plugins ) { + data class Inputs(val eventId: EventId?) : NodeInputs + sealed interface NavTarget : Parcelable { @Parcelize data object Empty : NavTarget @Parcelize - data object Messages : NavTarget + data class Messages( + val eventId: EventId? = null, + ) : NavTarget @Parcelize data class MediaViewer( @@ -149,6 +155,10 @@ class MessagesFlowNode @AssistedInject constructor( callback?.onUserDataClicked(userId) } + override fun onPermalinkClicked(data: PermalinkData) { + callback?.onPermalinkClicked(data) + } + override fun onShowEventDebugInfoClicked(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { backstack.push(NavTarget.EventDebugInfo(eventId, debugInfo)) } @@ -181,7 +191,10 @@ class MessagesFlowNode @AssistedInject constructor( ElementCallActivity.start(context, inputs) } } - createNode(buildContext, listOf(callback)) + val params = MessagesNode.Inputs( + eventId = navTarget.eventId, + ) + createNode(buildContext, listOf(callback, params)) } is NavTarget.MediaViewer -> { val inputs = MediaViewerNode.Inputs( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index ff8c727f9c..0a4d1f039d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -34,6 +34,7 @@ import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPr import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.androidutils.system.openUrlInExternalApp +import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.EventId @@ -42,6 +43,7 @@ import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkParser import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.roomMembers import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.mediaplayer.api.MediaPlayer import io.element.android.services.analytics.api.AnalyticsService @@ -62,11 +64,15 @@ class MessagesNode @AssistedInject constructor( private val presenter = presenterFactory.create(this) private val callback = plugins().firstOrNull() + // TODO Handle navigation to the Event + data class Inputs(val eventId: EventId?) : NodeInputs + interface Callback : Plugin { fun onRoomDetailsClicked() fun onEventClicked(event: TimelineItem.Event): Boolean fun onPreviewAttachments(attachments: ImmutableList) fun onUserDataClicked(userId: UserId) + fun onPermalinkClicked(data: PermalinkData) fun onShowEventDebugInfoClicked(eventId: EventId?, debugInfo: TimelineItemDebugInfo) fun onForwardEventClicked(eventId: EventId) fun onReportMessage(eventId: EventId, senderId: UserId) @@ -109,16 +115,19 @@ class MessagesNode @AssistedInject constructor( ) { when (val permalink = permalinkParser.parse(url)) { is PermalinkData.UserLink -> { - callback?.onUserDataClicked(permalink.userId) - } - is PermalinkData.RoomLink -> { - // TODO Implement room link handling - } - is PermalinkData.EventIdAliasLink -> { - // TODO Implement room and Event link handling + if (permalink.userId in room.membersStateFlow.value.roomMembers().orEmpty().map { it.userId }) { + // Open the room member profile + callback?.onUserDataClicked(permalink.userId) + } else { + // The user is not a member of the room + callback?.onPermalinkClicked(permalink) + } } + is PermalinkData.RoomIdLink, + is PermalinkData.RoomAliasLink, + is PermalinkData.EventIdAliasLink, is PermalinkData.EventIdLink -> { - // TODO Implement room and Event link handling + callback?.onPermalinkClicked(permalink) } is PermalinkData.FallbackLink, is PermalinkData.RoomEmailInviteLink -> { From dca3d56aa7d473275a8181cc1814ed899e1033d8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Apr 2024 10:39:45 +0200 Subject: [PATCH 04/46] Introduce value class RoomAlias --- .../libraries/matrix/api/MatrixClient.kt | 3 +- .../libraries/matrix/api/core/RoomAlias.kt | 31 +++++++++++++++++++ .../matrix/api/permalink/PermalinkData.kt | 5 +-- .../libraries/matrix/impl/RustMatrixClient.kt | 5 +-- .../impl/permalink/DefaultPermalinkParser.kt | 5 +-- .../libraries/matrix/test/FakeMatrixClient.kt | 5 +-- .../mentions/MentionSpanProvider.kt | 3 +- .../impl/mentions/MentionSpanProviderTest.kt | 5 +-- 8 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/RoomAlias.kt diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index 759b6cd47c..c79edd8465 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -17,6 +17,7 @@ package io.element.android.libraries.matrix.api import io.element.android.libraries.matrix.api.core.ProgressCallback +import io.element.android.libraries.matrix.api.core.RoomAlias 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.UserId @@ -99,6 +100,6 @@ interface MatrixClient : Closeable { suspend fun trackRecentlyVisitedRoom(roomId: RoomId): Result suspend fun getRecentlyVisitedRooms(): Result> - suspend fun resolveRoomAlias(roomAlias: String): Result + suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result suspend fun getRoomPreview(roomIdOrAlias: String): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/RoomAlias.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/RoomAlias.kt new file mode 100644 index 0000000000..46a457aa30 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/RoomAlias.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 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.libraries.matrix.api.core + +import io.element.android.libraries.androidutils.metadata.isInDebug +import java.io.Serializable + +@JvmInline +value class RoomAlias(val value: String) : Serializable { + init { + if (isInDebug && !MatrixPatterns.isRoomAlias(value)) { + error("`$value` is not a valid room alias.\n Example room alias: `#room_alias:domain`.") + } + } + + override fun toString(): String = value +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt index d09394e8dd..fff7e22380 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt @@ -19,6 +19,7 @@ package io.element.android.libraries.matrix.api.permalink import android.net.Uri import androidx.compose.runtime.Immutable import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import kotlinx.collections.immutable.ImmutableList @@ -39,7 +40,7 @@ sealed interface PermalinkData { ) : RoomLink data class RoomAliasLink( - val roomAlias: String, + val roomAlias: RoomAlias, override val viaParameters: ImmutableList ) : RoomLink @@ -55,7 +56,7 @@ sealed interface PermalinkData { ) : EventLink data class EventIdAliasLink( - val roomAlias: String, + val roomAlias: RoomAlias, override val eventId: EventId, override val viaParameters: ImmutableList ) : EventLink diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index cd7100f667..1cae69bff7 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -23,6 +23,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.childScope import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.ProgressCallback +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters @@ -462,9 +463,9 @@ class RustMatrixClient( } } - override suspend fun resolveRoomAlias(roomAlias: String): Result = withContext(sessionDispatcher) { + override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result = withContext(sessionDispatcher) { runCatching { - client.resolveRoomAlias(roomAlias).let(::RoomId) + client.resolveRoomAlias(roomAlias.value).let(::RoomId) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParser.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParser.kt index 7bf095a9f8..f47247c0f5 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParser.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParser.kt @@ -20,6 +20,7 @@ import android.net.Uri import com.squareup.anvil.annotations.ContributesBinding import io.element.android.libraries.di.AppScope import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.permalink.MatrixToConverter @@ -67,7 +68,7 @@ class DefaultPermalinkParser @Inject constructor( userId = UserId(id.id), ) is MatrixId.RoomAlias -> PermalinkData.RoomAliasLink( - roomAlias = id.alias, + roomAlias = RoomAlias(id.alias), viaParameters = viaParameters, ) is MatrixId.EventOnRoomId -> PermalinkData.EventIdLink( @@ -76,7 +77,7 @@ class DefaultPermalinkParser @Inject constructor( viaParameters = viaParameters, ) is MatrixId.EventOnRoomAlias -> PermalinkData.EventIdAliasLink( - roomAlias = id.alias, + roomAlias = RoomAlias(id.alias), eventId = EventId(id.eventId), viaParameters = viaParameters, ) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index 7bb880986f..4f49ac844d 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.test import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.ProgressCallback +import io.element.android.libraries.matrix.api.core.RoomAlias 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.UserId @@ -74,7 +75,7 @@ class FakeMatrixClient( private val encryptionService: FakeEncryptionService = FakeEncryptionService(), private val roomDirectoryService: RoomDirectoryService = FakeRoomDirectoryService(), private val accountManagementUrlString: Result = Result.success(null), - private val resolveRoomAliasResult: (String) -> Result = { Result.success(A_ROOM_ID) }, + private val resolveRoomAliasResult: (RoomAlias) -> Result = { Result.success(A_ROOM_ID) }, private val getRoomPreviewResult: (String) -> Result = { TODO("Not implemented") }, ) : MatrixClient { var setDisplayNameCalled: Boolean = false @@ -279,7 +280,7 @@ class FakeMatrixClient( return Result.success(Unit) } - override suspend fun resolveRoomAlias(roomAlias: String): Result { + override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result { return resolveRoomAliasResult(roomAlias) } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt index 4d705983ff..7972b4d6a4 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt @@ -39,6 +39,7 @@ import io.element.android.libraries.designsystem.theme.currentUserMentionPillBac import io.element.android.libraries.designsystem.theme.currentUserMentionPillText import io.element.android.libraries.designsystem.theme.mentionPillBackground import io.element.android.libraries.designsystem.theme.mentionPillText +import io.element.android.libraries.matrix.api.core.RoomAlias 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.permalink.PermalinkData @@ -140,7 +141,7 @@ internal fun MentionSpanPreview() { "https://matrix.to/#/@me:matrix.org" -> PermalinkData.UserLink(UserId("@me:matrix.org")) "https://matrix.to/#/@other:matrix.org" -> PermalinkData.UserLink(UserId("@other:matrix.org")) "https://matrix.to/#/#room:matrix.org" -> PermalinkData.RoomAliasLink( - roomAlias = "#room:matrix.org", + roomAlias = RoomAlias("#room:matrix.org"), viaParameters = persistentListOf(), ) else -> TODO() diff --git a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt index a5f31718cd..d7e2bff7b1 100644 --- a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt +++ b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.textcomposer.impl.mentions import android.graphics.Color import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.test.A_SESSION_ID @@ -69,7 +70,7 @@ class MentionSpanProviderTest { fun `getting mention span for a room should return a MentionSpan with normal colors`() { permalinkParser.givenResult( PermalinkData.RoomAliasLink( - roomAlias = "#room:matrix.org", + roomAlias = RoomAlias("#room:matrix.org"), viaParameters = persistentListOf(), ) ) @@ -82,7 +83,7 @@ class MentionSpanProviderTest { fun `getting mention span for @room should return a MentionSpan with normal colors`() { permalinkParser.givenResult( PermalinkData.RoomAliasLink( - roomAlias = "#", + roomAlias = RoomAlias("#"), viaParameters = persistentListOf(), ) ) From fe7dfcb09b6fd2f850383953375b95a33b52fb70 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Apr 2024 11:35:05 +0200 Subject: [PATCH 05/46] Use RoomId and RoomAlias in MatrixRoomInfo --- .../android/features/messages/impl/MessagesPresenterTest.kt | 2 +- .../messages/impl/typing/TypingNotificationPresenterTest.kt | 2 +- .../android/libraries/matrix/api/room/MatrixRoomInfo.kt | 6 ++++-- .../libraries/matrix/impl/room/MatrixRoomInfoMapper.kt | 6 ++++-- .../android/libraries/matrix/test/room/FakeMatrixRoom.kt | 5 +++-- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt index 002b34f0eb..8df2c6d705 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt @@ -720,7 +720,7 @@ class MessagesPresenterTest { private fun TestScope.createMessagesPresenter( coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(), matrixRoom: MatrixRoom = FakeMatrixRoom().apply { - givenRoomInfo(aRoomInfo(id = roomId.value, name = "")) + givenRoomInfo(aRoomInfo(id = roomId, name = "")) }, navigator: FakeMessagesNavigator = FakeMessagesNavigator(), clipboardHelper: FakeClipboardHelper = FakeClipboardHelper(), diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt index aaf0f9f774..540d9f67c4 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt @@ -201,7 +201,7 @@ class TypingNotificationPresenterTest { private fun createPresenter( matrixRoom: MatrixRoom = FakeMatrixRoom().apply { - givenRoomInfo(aRoomInfo(id = roomId.value, name = "")) + givenRoomInfo(aRoomInfo(id = roomId, name = "")) }, sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore( isRenderTypingNotificationsEnabled = true diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt index 8ad8260a7d..eea1cc9c1b 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt @@ -17,6 +17,8 @@ package io.element.android.libraries.matrix.api.room import androidx.compose.runtime.Immutable +import io.element.android.libraries.matrix.api.core.RoomAlias +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem import kotlinx.collections.immutable.ImmutableList @@ -24,7 +26,7 @@ import kotlinx.collections.immutable.ImmutableMap @Immutable data class MatrixRoomInfo( - val id: String, + val id: RoomId, val name: String?, val topic: String?, val avatarUrl: String?, @@ -33,7 +35,7 @@ data class MatrixRoomInfo( val isSpace: Boolean, val isTombstoned: Boolean, val isFavorite: Boolean, - val canonicalAlias: String?, + val canonicalAlias: RoomAlias?, val alternativeAliases: ImmutableList, val currentUserMembership: CurrentUserMembership, val latestEvent: EventTimelineItem?, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt index 3331771f79..d3ef3283bd 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt @@ -16,6 +16,8 @@ package io.element.android.libraries.matrix.impl.room +import io.element.android.libraries.matrix.api.core.RoomAlias +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.MatrixRoomInfo @@ -35,7 +37,7 @@ class MatrixRoomInfoMapper( ) { fun map(rustRoomInfo: RustRoomInfo): MatrixRoomInfo = rustRoomInfo.use { return MatrixRoomInfo( - id = it.id, + id = RoomId(it.id), name = it.name, topic = it.topic, avatarUrl = it.avatarUrl, @@ -44,7 +46,7 @@ class MatrixRoomInfoMapper( isSpace = it.isSpace, isTombstoned = it.isTombstoned, isFavorite = it.isFavourite, - canonicalAlias = it.canonicalAlias, + canonicalAlias = it.canonicalAlias?.let(::RoomAlias), alternativeAliases = it.alternativeAliases.toImmutableList(), currentUserMembership = it.membership.map(), latestEvent = it.latestEvent?.use(timelineItemMapper::map), diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index 5938cd300b..ee3e1137bb 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.test.room import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback +import io.element.android.libraries.matrix.api.core.RoomAlias 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.TransactionId @@ -751,7 +752,7 @@ data class EndPollInvocation( ) fun aRoomInfo( - id: String = A_ROOM_ID.value, + id: RoomId = A_ROOM_ID, name: String? = A_ROOM_NAME, topic: String? = "A topic", avatarUrl: String? = AN_AVATAR_URL, @@ -760,7 +761,7 @@ fun aRoomInfo( isSpace: Boolean = false, isTombstoned: Boolean = false, isFavorite: Boolean = false, - canonicalAlias: String? = null, + canonicalAlias: RoomAlias? = null, alternativeAliases: List = emptyList(), currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED, latestEvent: EventTimelineItem? = null, From a602849ec5b44b90e9a75b650a915beeddedfd3d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Apr 2024 12:27:26 +0200 Subject: [PATCH 06/46] Increase usage of `RoomAlias` --- .../features/joinroom/impl/JoinRoomPresenter.kt | 2 +- .../android/features/joinroom/impl/JoinRoomState.kt | 5 +++-- .../features/joinroom/impl/JoinRoomStateProvider.kt | 3 ++- .../features/messages/impl/MessagesPresenter.kt | 2 +- .../features/roomdetails/impl/RoomDetailsPresenter.kt | 2 +- .../features/roomdetails/impl/RoomDetailsState.kt | 6 ++++-- .../roomdetails/impl/RoomDetailsStateProvider.kt | 6 ++++-- .../features/roomdetails/impl/RoomDetailsView.kt | 10 ++++++---- .../features/roomdirectory/api/RoomDescription.kt | 7 ++++--- .../impl/root/RoomDirectoryStateProvider.kt | 5 +++-- .../android/libraries/matrix/api/room/MatrixRoom.kt | 5 +++-- .../libraries/matrix/api/room/preview/RoomPreview.kt | 3 ++- .../matrix/api/roomdirectory/RoomDescription.kt | 3 ++- .../libraries/matrix/api/roomlist/RoomSummary.kt | 3 ++- .../libraries/matrix/impl/room/RustMatrixRoom.kt | 9 +++++---- .../matrix/impl/room/preview/RoomPreviewMapper.kt | 3 ++- .../matrix/impl/roomdirectory/RoomDescriptionMapper.kt | 3 ++- .../matrix/impl/roomlist/RoomSummaryDetailsFactory.kt | 3 ++- .../libraries/matrix/test/room/FakeMatrixRoom.kt | 4 ++-- .../libraries/matrix/test/room/RoomSummaryFixture.kt | 3 ++- .../test/roomdirectory/RoomDescriptionFixture.kt | 3 ++- .../libraries/matrix/ui/components/SelectedRoom.kt | 3 ++- .../roomselect/impl/RoomSelectStateProvider.kt | 3 ++- .../libraries/roomselect/impl/RoomSelectView.kt | 2 +- 24 files changed, 60 insertions(+), 38 deletions(-) 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 f088603d3b..f196b2495e 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 @@ -137,7 +137,7 @@ internal fun RoomDescription.toContentState(): ContentState { @VisibleForTesting internal fun MatrixRoomInfo.toContentState(): ContentState { return ContentState.Loaded( - roomId = RoomId(id), + roomId = id, name = name, topic = topic, alias = canonicalAlias, diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt index 08591c068e..98962b9b8d 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt @@ -20,6 +20,7 @@ import androidx.compose.runtime.Immutable import io.element.android.features.invite.api.response.AcceptDeclineInviteState import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId @Immutable @@ -41,7 +42,7 @@ sealed interface ContentState { val roomId: RoomId, val name: String?, val topic: String?, - val alias: String?, + val alias: RoomAlias?, val numberOfMembers: Long?, val isDirect: Boolean, val roomAvatarUrl: String?, @@ -50,7 +51,7 @@ sealed interface ContentState { val computedTitle = name ?: roomId.value val computedSubtitle = when { - alias != null -> alias + alias != null -> alias.value name == null -> "" else -> roomId.value } diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt index 82b81d8e7b..2249b2865e 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt @@ -19,6 +19,7 @@ package io.element.android.features.joinroom.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.invite.api.response.AcceptDeclineInviteState import io.element.android.features.invite.api.response.anAcceptDeclineInviteState +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId open class JoinRoomStateProvider : PreviewParameterProvider { @@ -49,7 +50,7 @@ fun aLoadingContentState(roomId: RoomId = A_ROOM_ID) = ContentState.Loading(room fun aLoadedContentState( roomId: RoomId = A_ROOM_ID, name: String = "Element X android", - alias: String? = "#exa:matrix.org", + alias: RoomAlias? = RoomAlias("#exa:matrix.org"), topic: String? = "Element X is a secure, private and decentralized messenger.", numberOfMembers: Long? = null, isDirect: Boolean = false, 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 4edc943a73..2b74cf1072 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 @@ -258,7 +258,7 @@ class MessagesPresenter @AssistedInject constructor( private fun MatrixRoomInfo.avatarData(): AvatarData { return AvatarData( - id = id, + id = id.value, name = name, url = avatarUrl ?: room.avatarUrl, size = AvatarSize.TimelineRoom 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 f0c96e74e2..c8b0f49346 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 @@ -128,7 +128,7 @@ class RoomDetailsPresenter @Inject constructor( val roomMemberDetailsState = roomMemberDetailsPresenter?.present() return RoomDetailsState( - roomId = room.roomId.value, + roomId = room.roomId, roomName = roomName, roomAlias = room.alias, roomAvatarUrl = roomAvatar, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt index f5abc5bcce..ebdd524ed6 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt @@ -18,13 +18,15 @@ package io.element.android.features.roomdetails.impl import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState +import io.element.android.libraries.matrix.api.core.RoomAlias +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.RoomNotificationSettings data class RoomDetailsState( - val roomId: String, + val roomId: RoomId, val roomName: String, - val roomAlias: String?, + val roomAlias: RoomAlias?, val roomAvatarUrl: String?, val roomTopic: RoomTopicState, val memberCount: Long, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt index 06b7ea7be7..f398a6704b 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt @@ -21,6 +21,8 @@ import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.leaveroom.api.aLeaveRoomState import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState import io.element.android.features.roomdetails.impl.members.details.aRoomMemberDetailsState +import io.element.android.libraries.matrix.api.core.RoomAlias +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembershipState @@ -71,9 +73,9 @@ fun aDmRoomMember( ) fun aRoomDetailsState( - roomId: String = "a room id", + roomId: RoomId = RoomId("!aRoomId:domain.com"), roomName: String = "Marketing", - roomAlias: String? = "#marketing:domain.com", + roomAlias: RoomAlias? = RoomAlias("#marketing:domain.com"), roomAvatarUrl: String? = null, roomTopic: RoomTopicState = RoomTopicState.ExistingTopic( "Welcome to #marketing, home of the Marketing team " + diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index ff4ece7bde..fc49bb383a 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -78,6 +78,8 @@ 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.designsystem.utils.CommonDrawables +import io.element.android.libraries.matrix.api.core.RoomAlias +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.RoomNotificationMode import io.element.android.libraries.matrix.api.room.getBestName @@ -302,9 +304,9 @@ private fun MainActionsSection( @Composable private fun RoomHeaderSection( avatarUrl: String?, - roomId: String, + roomId: RoomId, roomName: String, - roomAlias: String?, + roomAlias: RoomAlias?, openAvatarPreview: (url: String) -> Unit, ) { Column( @@ -314,7 +316,7 @@ private fun RoomHeaderSection( horizontalAlignment = Alignment.CenterHorizontally, ) { Avatar( - avatarData = AvatarData(roomId, roomName, avatarUrl, AvatarSize.RoomHeader), + avatarData = AvatarData(roomId.value, roomName, avatarUrl, AvatarSize.RoomHeader), modifier = Modifier .size(70.dp) .clickable(enabled = avatarUrl != null) { openAvatarPreview(avatarUrl!!) } @@ -329,7 +331,7 @@ private fun RoomHeaderSection( if (roomAlias != null) { Spacer(modifier = Modifier.height(6.dp)) Text( - text = roomAlias, + text = roomAlias.value, style = ElementTheme.typography.fontBodyLgRegular, color = MaterialTheme.colorScheme.secondary, textAlign = TextAlign.Center, diff --git a/features/roomdirectory/api/src/main/kotlin/io/element/android/features/roomdirectory/api/RoomDescription.kt b/features/roomdirectory/api/src/main/kotlin/io/element/android/features/roomdirectory/api/RoomDescription.kt index a27f413e9b..909c34f959 100644 --- a/features/roomdirectory/api/src/main/kotlin/io/element/android/features/roomdirectory/api/RoomDescription.kt +++ b/features/roomdirectory/api/src/main/kotlin/io/element/android/features/roomdirectory/api/RoomDescription.kt @@ -20,6 +20,7 @@ import android.os.Parcelable import androidx.compose.runtime.Immutable import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize @@ -29,7 +30,7 @@ import kotlinx.parcelize.Parcelize data class RoomDescription( val roomId: RoomId, val name: String?, - val alias: String?, + val alias: RoomAlias?, val topic: String?, val avatarUrl: String?, val joinRule: JoinRule, @@ -42,14 +43,14 @@ data class RoomDescription( } @IgnoredOnParcel - val computedName = name ?: alias ?: roomId.value + val computedName = name ?: alias?.value ?: roomId.value @IgnoredOnParcel val computedDescription: String get() { return when { topic != null -> topic - name != null && alias != null -> alias + name != null && alias != null -> alias.value name == null && alias == null -> "" else -> roomId.value } diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStateProvider.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStateProvider.kt index e94271cfb8..bf682fc15b 100644 --- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStateProvider.kt +++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStateProvider.kt @@ -19,6 +19,7 @@ package io.element.android.features.roomdirectory.impl.root import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.roomdirectory.api.RoomDescription import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -69,7 +70,7 @@ fun aRoomDescriptionList(): ImmutableList { roomId = RoomId("!exa:matrix.org"), name = "Element X Android", topic = "Element X is a secure, private and decentralized messenger.", - alias = "#element-x-android:matrix.org", + alias = RoomAlias("#element-x-android:matrix.org"), avatarUrl = null, joinRule = RoomDescription.JoinRule.PUBLIC, numberOfMembers = 2765, @@ -78,7 +79,7 @@ fun aRoomDescriptionList(): ImmutableList { roomId = RoomId("!exi:matrix.org"), name = "Element X iOS", topic = "Element X is a secure, private and decentralized messenger.", - alias = "#element-x-ios:matrix.org", + alias = RoomAlias("#element-x-ios:matrix.org"), avatarUrl = null, joinRule = RoomDescription.JoinRule.UNKNOWN, numberOfMembers = 356, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt index 761d87445a..5c28b84c01 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.api.room import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback +import io.element.android.libraries.matrix.api.core.RoomAlias 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.TransactionId @@ -45,8 +46,8 @@ interface MatrixRoom : Closeable { val roomId: RoomId val name: String? val displayName: String - val alias: String? - val alternativeAliases: List + val alias: RoomAlias? + val alternativeAliases: List val topic: String? val avatarUrl: String? val isEncrypted: Boolean diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/preview/RoomPreview.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/preview/RoomPreview.kt index 8ee7550b74..ad16561380 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/preview/RoomPreview.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/preview/RoomPreview.kt @@ -16,13 +16,14 @@ package io.element.android.libraries.matrix.api.room.preview +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId data class RoomPreview( /** The room id for this room. */ val roomId: RoomId, /** The canonical alias for the room. */ - val canonicalAlias: String?, + val canonicalAlias : RoomAlias?, /** The room's name, if set. */ val name: String?, /** The room's topic, if set. */ diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDescription.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDescription.kt index 78d6cb0c94..75d203e0b7 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDescription.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDescription.kt @@ -16,13 +16,14 @@ package io.element.android.libraries.matrix.api.roomdirectory +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId data class RoomDescription( val roomId: RoomId, val name: String?, val topic: String?, - val alias: String?, + val alias: RoomAlias?, val avatarUrl: String?, val joinRule: JoinRule, val isWorldReadable: Boolean, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt index b7955a9c79..2ab50630a1 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt @@ -16,6 +16,7 @@ package io.element.android.libraries.matrix.api.roomlist +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.RoomMember @@ -37,7 +38,7 @@ sealed interface RoomSummary { data class RoomSummaryDetails( val roomId: RoomId, val name: String, - val canonicalAlias: String?, + val canonicalAlias: RoomAlias?, val isDirect: Boolean, val avatarUrl: String?, val lastMessage: RoomMessage?, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 59e5c99efa..97035f0583 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -20,6 +20,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.childScope import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback +import io.element.android.libraries.matrix.api.core.RoomAlias 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.TransactionId @@ -205,11 +206,11 @@ class RustMatrixRoom( override val isEncrypted: Boolean get() = runCatching { innerRoom.isEncrypted() }.getOrDefault(false) - override val alias: String? - get() = runCatching { innerRoom.canonicalAlias() }.getOrDefault(null) + override val alias: RoomAlias? + get() = runCatching { innerRoom.canonicalAlias()?.let(::RoomAlias) }.getOrDefault(null) - override val alternativeAliases: List - get() = runCatching { innerRoom.alternativeAliases() }.getOrDefault(emptyList()) + override val alternativeAliases: List + get() = runCatching { innerRoom.alternativeAliases().map { RoomAlias(it) } }.getOrDefault(emptyList()) override val isPublic: Boolean get() = runCatching { innerRoom.isPublic() }.getOrDefault(false) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/preview/RoomPreviewMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/preview/RoomPreviewMapper.kt index fb18205978..75286becda 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/preview/RoomPreviewMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/preview/RoomPreviewMapper.kt @@ -16,6 +16,7 @@ package io.element.android.libraries.matrix.impl.room.preview +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.preview.RoomPreview import org.matrix.rustcomponents.sdk.RoomPreview as RustRoomPreview @@ -24,7 +25,7 @@ object RoomPreviewMapper { fun map(roomPreview: RustRoomPreview): RoomPreview { return RoomPreview( roomId = RoomId(roomPreview.roomId), - canonicalAlias = roomPreview.canonicalAlias, + canonicalAlias = roomPreview.canonicalAlias?.let(::RoomAlias), name = roomPreview.name, topic = roomPreview.topic, avatarUrl = roomPreview.avatarUrl, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RoomDescriptionMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RoomDescriptionMapper.kt index 876a58d3a5..b256f589f4 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RoomDescriptionMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RoomDescriptionMapper.kt @@ -16,6 +16,7 @@ package io.element.android.libraries.matrix.impl.roomdirectory +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.roomdirectory.RoomDescription import org.matrix.rustcomponents.sdk.PublicRoomJoinRule @@ -28,7 +29,7 @@ class RoomDescriptionMapper { name = roomDescription.name, topic = roomDescription.topic, avatarUrl = roomDescription.avatarUrl, - alias = roomDescription.alias, + alias = roomDescription.alias?.let(::RoomAlias), joinRule = when (roomDescription.joinRule) { PublicRoomJoinRule.PUBLIC -> RoomDescription.JoinRule.PUBLIC PublicRoomJoinRule.KNOCK -> RoomDescription.JoinRule.KNOCK diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt index 834c49ac2a..a70030cc70 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt @@ -16,6 +16,7 @@ package io.element.android.libraries.matrix.impl.roomlist +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails import io.element.android.libraries.matrix.impl.notificationsettings.RoomNotificationSettingsMapper @@ -33,7 +34,7 @@ class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFacto return RoomSummaryDetails( roomId = RoomId(roomInfo.id), name = roomInfo.name ?: roomInfo.id, - canonicalAlias = roomInfo.canonicalAlias, + canonicalAlias = roomInfo.canonicalAlias?.let(::RoomAlias), isDirect = roomInfo.isDirect, avatarUrl = roomInfo.avatarUrl, numUnreadMentions = roomInfo.numUnreadMentions.toInt(), diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index ee3e1137bb..830ac3a6e2 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -75,8 +75,8 @@ class FakeMatrixRoom( override val topic: String? = null, override val avatarUrl: String? = null, override val isEncrypted: Boolean = false, - override val alias: String? = null, - override val alternativeAliases: List = emptyList(), + override val alias: RoomAlias? = null, + override val alternativeAliases: List = emptyList(), override val isPublic: Boolean = true, override val isSpace: Boolean = false, override val isDirect: Boolean = false, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt index b9b078c5f3..6b9383ef05 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt @@ -17,6 +17,7 @@ package io.element.android.libraries.matrix.test.room import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.CurrentUserMembership @@ -72,7 +73,7 @@ fun aRoomSummaryDetails( isMarkedUnread: Boolean = false, notificationMode: RoomNotificationMode? = null, inviter: RoomMember? = null, - canonicalAlias: String? = null, + canonicalAlias: RoomAlias? = null, hasRoomCall: Boolean = false, isDm: Boolean = false, isFavorite: Boolean = false, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/RoomDescriptionFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/RoomDescriptionFixture.kt index 6e96ca4452..174f5f7f0c 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/RoomDescriptionFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/RoomDescriptionFixture.kt @@ -16,6 +16,7 @@ package io.element.android.libraries.matrix.test.roomdirectory +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.roomdirectory.RoomDescription import io.element.android.libraries.matrix.test.A_ROOM_ID @@ -24,7 +25,7 @@ fun aRoomDescription( roomId: RoomId = A_ROOM_ID, name: String? = null, topic: String? = null, - alias: String? = null, + alias: RoomAlias? = null, avatarUrl: String? = null, joinRule: RoomDescription.JoinRule = RoomDescription.JoinRule.UNKNOWN, isWorldReadable: Boolean = true, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt index 4caad93b46..4b7c17a1ec 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt @@ -43,6 +43,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.RoomMember @@ -106,7 +107,7 @@ internal fun SelectedRoomPreview() = ElementPreview { fun aRoomSummaryDetails( roomId: RoomId = RoomId("!room:domain"), name: String = "roomName", - canonicalAlias: String? = null, + canonicalAlias: RoomAlias? = null, isDirect: Boolean = true, avatarUrl: String? = null, lastMessage: RoomMessage? = null, diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt index ecd13338ca..f0daf8b7fa 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.roomselect.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.designsystem.theme.components.SearchBarResultState +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails import io.element.android.libraries.matrix.ui.components.aRoomSummaryDetails @@ -65,6 +66,6 @@ private fun aForwardMessagesRoomList() = persistentListOf( aRoomSummaryDetails( roomId = RoomId("!room2:domain"), name = "Room with alias", - canonicalAlias = "#alias:example.org", + canonicalAlias = RoomAlias("#alias:example.org"), ), ) diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt index b2c703fdb1..eb6143107f 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt @@ -243,7 +243,7 @@ private fun RoomSummaryView( // Alias summary.canonicalAlias?.let { alias -> Text( - text = alias, + text = alias.value, color = ElementTheme.colors.textSecondary, style = ElementTheme.typography.fontBodySmRegular, maxLines = 1, From c1188ebb2da02525a176271557943bd7a7605a91 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Apr 2024 12:20:25 +0200 Subject: [PATCH 07/46] Alias permalink navigation - WIP --- .../android/appnav/LoggedInFlowNode.kt | 37 +++++++++-------- .../android/appnav/room/RoomFlowNode.kt | 37 +++++++++-------- .../joinroom/api/JoinRoomEntryPoint.kt | 4 +- .../features/joinroom/impl/JoinRoomNode.kt | 2 +- .../joinroom/impl/JoinRoomPresenter.kt | 16 ++++---- .../features/joinroom/impl/JoinRoomState.kt | 5 ++- .../joinroom/impl/JoinRoomStateProvider.kt | 5 ++- .../joinroom/impl/di/JoinRoomModule.kt | 6 +-- .../libraries/matrix/api/MatrixClient.kt | 5 ++- .../matrix/api/core/RoomIdOrAlias.kt | 39 ++++++++++++++++++ .../libraries/matrix/impl/RustMatrixClient.kt | 40 +++++++++++++------ .../libraries/matrix/test/FakeMatrixClient.kt | 9 +++-- 12 files changed, 137 insertions(+), 68 deletions(-) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/RoomIdOrAlias.kt diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index 190623c9b6..1fb3c5648e 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -66,6 +66,8 @@ import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.MAIN_SPACE import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomIdOrAlias +import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.services.appnavstate.api.AppNavigationStateService @@ -190,7 +192,7 @@ class LoggedInFlowNode @AssistedInject constructor( @Parcelize data class Room( - val roomId: RoomId, + val roomIdOrAlias: RoomIdOrAlias, val roomDescription: RoomDescription? = null, val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages(null) ) : NavTarget @@ -229,7 +231,7 @@ class LoggedInFlowNode @AssistedInject constructor( NavTarget.RoomList -> { val callback = object : RoomListEntryPoint.Callback { override fun onRoomClicked(roomId: RoomId) { - backstack.push(NavTarget.Room(roomId)) + backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias())) } override fun onSettingsClicked() { @@ -245,7 +247,7 @@ class LoggedInFlowNode @AssistedInject constructor( } override fun onRoomSettingsClicked(roomId: RoomId) { - backstack.push(NavTarget.Room(roomId, initialElement = RoomNavigationTarget.Details)) + backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Details)) } override fun onReportBugClicked() { @@ -264,7 +266,7 @@ class LoggedInFlowNode @AssistedInject constructor( is NavTarget.Room -> { val callback = object : JoinedRoomLoadedFlowNode.Callback { override fun onOpenRoom(roomId: RoomId) { - backstack.push(NavTarget.Room(roomId)) + backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias())) } override fun onForwardedToSingleRoom(roomId: RoomId) { @@ -279,20 +281,23 @@ class LoggedInFlowNode @AssistedInject constructor( Timber.e("User link clicked: ${data.userId}. TODO Add a user profile screen") } is PermalinkData.RoomIdLink -> { - backstack.push(NavTarget.Room(data.roomId)) + backstack.push(NavTarget.Room(data.roomId.toRoomIdOrAlias())) } is PermalinkData.RoomAliasLink -> { - // FIXME Implement room alias navigation - Timber.e("Room alias link clicked: ${data.roomAlias}. TODO Handle a room alias navigation") + backstack.push(NavTarget.Room(data.roomAlias.toRoomIdOrAlias())) } is PermalinkData.EventIdAliasLink -> { - // FIXME Implement event alias navigation - Timber.e("Event with room alias link clicked: ${data.eventId}. TODO Handle an event with room alias navigation") + backstack.push( + NavTarget.Room( + data.roomAlias.toRoomIdOrAlias(), + initialElement = RoomNavigationTarget.Messages(data.eventId) + ) + ) } is PermalinkData.EventIdLink -> { backstack.push( NavTarget.Room( - data.roomId, + data.roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Messages(data.eventId) ) ) @@ -310,7 +315,7 @@ class LoggedInFlowNode @AssistedInject constructor( } } val inputs = RoomFlowNode.Inputs( - roomId = navTarget.roomId, + roomIdOrAlias = navTarget.roomIdOrAlias, roomDescription = Optional.ofNullable(navTarget.roomDescription), initialElement = navTarget.initialElement ) @@ -327,7 +332,7 @@ class LoggedInFlowNode @AssistedInject constructor( } override fun onOpenRoomNotificationSettings(roomId: RoomId) { - backstack.push(NavTarget.Room(roomId, initialElement = RoomNavigationTarget.NotificationSettings)) + backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.NotificationSettings)) } } val inputs = PreferencesEntryPoint.Params(navTarget.initialElement) @@ -339,7 +344,7 @@ class LoggedInFlowNode @AssistedInject constructor( NavTarget.CreateRoom -> { val callback = object : CreateRoomEntryPoint.Callback { override fun onSuccess(roomId: RoomId) { - backstack.replace(NavTarget.Room(roomId)) + backstack.replace(NavTarget.Room(roomId.toRoomIdOrAlias())) } } @@ -366,11 +371,11 @@ class LoggedInFlowNode @AssistedInject constructor( roomDirectoryEntryPoint.nodeBuilder(this, buildContext) .callback(object : RoomDirectoryEntryPoint.Callback { override fun onRoomJoined(roomId: RoomId) { - backstack.push(NavTarget.Room(roomId)) + backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias())) } override fun onResultClicked(roomDescription: RoomDescription) { - backstack.push(NavTarget.Room(roomDescription.roomId, roomDescription)) + backstack.push(NavTarget.Room(roomDescription.roomId.toRoomIdOrAlias(), roomDescription)) } }) .build() @@ -389,7 +394,7 @@ class LoggedInFlowNode @AssistedInject constructor( if (!canShowRoomList()) return attachChild { backstack.singleTop(NavTarget.RoomList) - backstack.push(NavTarget.Room(roomId)) + backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias())) } } 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 2f62045b22..048c1826f2 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 @@ -47,8 +47,10 @@ import io.element.android.libraries.designsystem.theme.components.CircularProgre import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.RoomMembershipObserver +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -73,7 +75,7 @@ class RoomFlowNode @AssistedInject constructor( plugins = plugins ) { data class Inputs( - val roomId: RoomId, + val roomIdOrAlias: RoomIdOrAlias, val roomDescription: Optional, val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages(), ) : NodeInputs @@ -88,42 +90,45 @@ class RoomFlowNode @AssistedInject constructor( data object JoinRoom : NavTarget @Parcelize - data object JoinedRoom : NavTarget + data class JoinedRoom(val roomId: RoomId) : NavTarget } + private var roomMembershipJob: Job? = null + override fun onBuilt() { super.onBuilt() client.getRoomInfoFlow( - inputs.roomId + inputs.roomIdOrAlias ).onEach { roomInfo -> Timber.d("Room membership: ${roomInfo.map { it.currentUserMembership }}") - if (roomInfo.getOrNull()?.currentUserMembership == CurrentUserMembership.JOINED) { - backstack.newRoot(NavTarget.JoinedRoom) + val info = roomInfo.getOrNull() + if (info?.currentUserMembership == CurrentUserMembership.JOINED) { + backstack.newRoot(NavTarget.JoinedRoom(info.id)) + // When leaving the room from this session only, navigate up. + roomMembershipJob?.cancel() + roomMembershipJob = roomMembershipObserver.updates + .filter { update -> update.roomId == info.id && !update.isUserInRoom } + .onEach { + navigateUp() + } + .launchIn(lifecycleScope) } else { backstack.newRoot(NavTarget.JoinRoom) } } .launchIn(lifecycleScope) - - // When leaving the room from this session only, navigate up. - roomMembershipObserver.updates - .filter { update -> update.roomId == inputs.roomId && !update.isUserInRoom } - .onEach { - navigateUp() - } - .launchIn(lifecycleScope) } override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { NavTarget.Loading -> loadingNode(buildContext) NavTarget.JoinRoom -> { - val inputs = JoinRoomEntryPoint.Inputs(inputs.roomId, roomDescription = inputs.roomDescription) + val inputs = JoinRoomEntryPoint.Inputs(inputs.roomIdOrAlias, roomDescription = inputs.roomDescription) joinRoomEntryPoint.createNode(this, buildContext, inputs) } - NavTarget.JoinedRoom -> { + is NavTarget.JoinedRoom -> { val roomFlowNodeCallback = plugins() - val inputs = JoinedRoomFlowNode.Inputs(inputs.roomId, initialElement = inputs.initialElement) + val inputs = JoinedRoomFlowNode.Inputs(navTarget.roomId, initialElement = inputs.initialElement) createNode(buildContext, plugins = listOf(inputs) + roomFlowNodeCallback) } } diff --git a/features/joinroom/api/src/main/kotlin/io/element/android/features/joinroom/api/JoinRoomEntryPoint.kt b/features/joinroom/api/src/main/kotlin/io/element/android/features/joinroom/api/JoinRoomEntryPoint.kt index 60f49b9d36..0986af81d3 100644 --- a/features/joinroom/api/src/main/kotlin/io/element/android/features/joinroom/api/JoinRoomEntryPoint.kt +++ b/features/joinroom/api/src/main/kotlin/io/element/android/features/joinroom/api/JoinRoomEntryPoint.kt @@ -21,14 +21,14 @@ import com.bumble.appyx.core.node.Node import io.element.android.features.roomdirectory.api.RoomDescription 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.core.RoomIdOrAlias import java.util.Optional interface JoinRoomEntryPoint : FeatureEntryPoint { fun createNode(parentNode: Node, buildContext: BuildContext, inputs: Inputs): Node data class Inputs( - val roomId: RoomId, + val roomIdOrAlias: RoomIdOrAlias, val roomDescription: Optional, ) : NodeInputs } diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomNode.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomNode.kt index eaa195d88d..516c789079 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomNode.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomNode.kt @@ -37,7 +37,7 @@ class JoinRoomNode @AssistedInject constructor( private val acceptDeclineInviteView: AcceptDeclineInviteView, ) : Node(buildContext, plugins = plugins) { private val inputs: JoinRoomEntryPoint.Inputs = inputs() - private val presenter = presenterFactory.create(inputs.roomId, inputs.roomDescription) + private val presenter = presenterFactory.create(inputs.roomIdOrAlias, inputs.roomDescription) @Composable override fun View(modifier: Modifier) { 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 f196b2495e..818857c69a 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 @@ -30,7 +30,7 @@ import io.element.android.features.invite.api.response.InviteData import io.element.android.features.roomdirectory.api.RoomDescription import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.MatrixClient -import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.preview.RoomPreview @@ -38,20 +38,20 @@ import kotlinx.coroutines.launch import java.util.Optional class JoinRoomPresenter @AssistedInject constructor( - @Assisted private val roomId: RoomId, + @Assisted private val roomIdOrAlias: RoomIdOrAlias, @Assisted private val roomDescription: Optional, private val matrixClient: MatrixClient, private val acceptDeclineInvitePresenter: Presenter, ) : Presenter { interface Factory { - fun create(roomId: RoomId, roomDescription: Optional): JoinRoomPresenter + fun create(roomIdOrAlias: RoomIdOrAlias, roomDescription: Optional): JoinRoomPresenter } @Composable override fun present(): JoinRoomState { val coroutineScope = rememberCoroutineScope() - val roomInfo by matrixClient.getRoomInfoFlow(roomId).collectAsState(initial = Optional.empty()) - val contentState by produceState(initialValue = ContentState.Loading(roomId), key1 = roomInfo) { + val roomInfo by matrixClient.getRoomInfoFlow(roomIdOrAlias).collectAsState(initial = Optional.empty()) + val contentState by produceState(initialValue = ContentState.Loading(roomIdOrAlias), key1 = roomInfo) { value = when { roomInfo.isPresent -> { roomInfo.get().toContentState() @@ -61,12 +61,12 @@ class JoinRoomPresenter @AssistedInject constructor( } else -> { coroutineScope.launch { - val result = matrixClient.getRoomPreview(roomId.value) + val result = matrixClient.getRoomPreview(roomIdOrAlias) value = result.getOrNull() ?.toContentState() - ?: ContentState.UnknownRoom(roomId) + ?: ContentState.UnknownRoom(roomIdOrAlias) } - ContentState.Loading(roomId) + ContentState.Loading(roomIdOrAlias) } } } diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt index 98962b9b8d..bf57a0fdcf 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt @@ -22,6 +22,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomIdOrAlias @Immutable data class JoinRoomState( @@ -36,8 +37,8 @@ data class JoinRoomState( } sealed interface ContentState { - data class Loading(val roomId: RoomId) : ContentState - data class UnknownRoom(val roomId: RoomId) : ContentState + data class Loading(val roomIdOrAlias: RoomIdOrAlias) : ContentState + data class UnknownRoom(val roomIdOrAlias: RoomIdOrAlias) : ContentState data class Loaded( val roomId: RoomId, val name: String?, diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt index 2249b2865e..3eea5e723b 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt @@ -21,6 +21,7 @@ import io.element.android.features.invite.api.response.AcceptDeclineInviteState import io.element.android.features.invite.api.response.anAcceptDeclineInviteState import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias open class JoinRoomStateProvider : PreviewParameterProvider { override val values: Sequence @@ -43,9 +44,9 @@ open class JoinRoomStateProvider : PreviewParameterProvider { ) } -fun anUnknownContentState(roomId: RoomId = A_ROOM_ID) = ContentState.UnknownRoom(roomId) +fun anUnknownContentState(roomId: RoomId = A_ROOM_ID) = ContentState.UnknownRoom(roomId.toRoomIdOrAlias()) -fun aLoadingContentState(roomId: RoomId = A_ROOM_ID) = ContentState.Loading(roomId) +fun aLoadingContentState(roomId: RoomId = A_ROOM_ID) = ContentState.Loading(roomId.toRoomIdOrAlias()) fun aLoadedContentState( roomId: RoomId = A_ROOM_ID, diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/JoinRoomModule.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/JoinRoomModule.kt index 6c1dfd491d..0bce71b885 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/JoinRoomModule.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/JoinRoomModule.kt @@ -25,7 +25,7 @@ import io.element.android.features.roomdirectory.api.RoomDescription import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient -import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import java.util.Optional @Module @@ -37,9 +37,9 @@ object JoinRoomModule { acceptDeclineInvitePresenter: Presenter, ): JoinRoomPresenter.Factory { return object : JoinRoomPresenter.Factory { - override fun create(roomId: RoomId, roomDescription: Optional): JoinRoomPresenter { + override fun create(roomIdOrAlias: RoomIdOrAlias, roomDescription: Optional): JoinRoomPresenter { return JoinRoomPresenter( - roomId = roomId, + roomIdOrAlias = roomIdOrAlias, roomDescription = roomDescription, matrixClient = client, acceptDeclineInvitePresenter = acceptDeclineInvitePresenter, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index c79edd8465..e6bbee4f9a 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -19,6 +19,7 @@ package io.element.android.libraries.matrix.api import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomIdOrAlias 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.createroom.CreateRoomParameters @@ -94,12 +95,12 @@ interface MatrixClient : Closeable { suspend fun getAccountManagementUrl(action: AccountManagementAction?): Result suspend fun uploadMedia(mimeType: String, data: ByteArray, progressCallback: ProgressCallback?): Result fun roomMembershipObserver(): RoomMembershipObserver - fun getRoomInfoFlow(roomId: RoomId): Flow> + fun getRoomInfoFlow(roomIdOrAlias: RoomIdOrAlias): Flow> fun isMe(userId: UserId?) = userId == sessionId suspend fun trackRecentlyVisitedRoom(roomId: RoomId): Result suspend fun getRecentlyVisitedRooms(): Result> suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result - suspend fun getRoomPreview(roomIdOrAlias: String): Result + suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/RoomIdOrAlias.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/RoomIdOrAlias.kt new file mode 100644 index 0000000000..5dd4117b0a --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/RoomIdOrAlias.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024 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.libraries.matrix.api.core + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +sealed interface RoomIdOrAlias : Parcelable { + @Parcelize + @JvmInline + value class Id(val roomId: RoomId) : RoomIdOrAlias + + @Parcelize + @JvmInline + value class Alias(val roomAlias: RoomAlias) : RoomIdOrAlias + + val identifier: String + get() = when (this) { + is Id -> roomId.value + is Alias -> roomAlias.value + } +} + +fun RoomId.toRoomIdOrAlias() = RoomIdOrAlias.Id(this) +fun RoomAlias.toRoomIdOrAlias() = RoomIdOrAlias.Alias(this) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 1cae69bff7..adc96d0b84 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -25,6 +25,7 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters import io.element.android.libraries.matrix.api.createroom.RoomPreset @@ -469,9 +470,9 @@ class RustMatrixClient( } } - override suspend fun getRoomPreview(roomIdOrAlias: String): Result = withContext(sessionDispatcher) { + override suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias): Result = withContext(sessionDispatcher) { runCatching { - client.getRoomPreview(roomIdOrAlias).let(RoomPreviewMapper::map) + client.getRoomPreview(roomIdOrAlias.identifier).let(RoomPreviewMapper::map) } } @@ -561,18 +562,33 @@ class RustMatrixClient( override fun roomMembershipObserver(): RoomMembershipObserver = roomMembershipObserver - override fun getRoomInfoFlow(roomId: RoomId): Flow> { + override fun getRoomInfoFlow(roomIdOrAlias: RoomIdOrAlias): Flow> { return flow { - var room = getRoom(roomId) - if (room == null) { - emit(Optional.empty()) - awaitRoom(roomId, INFINITE) - room = getRoom(roomId) + val roomId = when (roomIdOrAlias) { + is RoomIdOrAlias.Alias -> { + resolveRoomAlias(roomIdOrAlias.roomAlias) + .onFailure { + // TODO Get a way to emit an error + Timber.e("Unable to resolve room alias ${roomIdOrAlias.roomAlias}") + emit(Optional.empty()) + return@flow + } + .getOrNull() + } + is RoomIdOrAlias.Id -> roomIdOrAlias.roomId } - room?.use { - room.roomInfoFlow - .map { roomInfo -> Optional.of(roomInfo) } - .collect(this) + if (roomId != null) { + var room = getRoom(roomId) + if (room == null) { + emit(Optional.empty()) + awaitRoom(roomId, INFINITE) + room = getRoom(roomId) + } + room?.use { + room.roomInfoFlow + .map { roomInfo -> Optional.of(roomInfo) } + .collect(this) + } } }.distinctUntilChanged() } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index 4f49ac844d..1fc51cda94 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomIdOrAlias 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.createroom.CreateRoomParameters @@ -76,7 +77,7 @@ class FakeMatrixClient( private val roomDirectoryService: RoomDirectoryService = FakeRoomDirectoryService(), private val accountManagementUrlString: Result = Result.success(null), private val resolveRoomAliasResult: (RoomAlias) -> Result = { Result.success(A_ROOM_ID) }, - private val getRoomPreviewResult: (String) -> Result = { TODO("Not implemented") }, + private val getRoomPreviewResult: (RoomIdOrAlias) -> Result = { TODO("Not implemented") }, ) : MatrixClient { var setDisplayNameCalled: Boolean = false private set @@ -106,7 +107,7 @@ class FakeMatrixClient( Result.success(it) } - var getRoomInfoFlowLambda = { _: RoomId -> + var getRoomInfoFlowLambda = { _: RoomIdOrAlias -> flowOf>(Optional.empty()) } @@ -284,7 +285,7 @@ class FakeMatrixClient( return resolveRoomAliasResult(roomAlias) } - override suspend fun getRoomPreview(roomIdOrAlias: String): Result { + override suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias): Result { return getRoomPreviewResult(roomIdOrAlias) } @@ -292,5 +293,5 @@ class FakeMatrixClient( return Result.success(visitedRoomsId) } - override fun getRoomInfoFlow(roomId: RoomId) = getRoomInfoFlowLambda(roomId) + override fun getRoomInfoFlow(roomIdOrAlias: RoomIdOrAlias) = getRoomInfoFlowLambda(roomIdOrAlias) } From c1bb910dbd156df37069656a41eba59ba8b48e3e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Apr 2024 12:45:24 +0200 Subject: [PATCH 08/46] Simplify PermalinkData, now that we have RoomIdOrAlias interface. --- .../android/appnav/LoggedInFlowNode.kt | 21 ++-------- .../features/messages/impl/MessagesNode.kt | 5 +-- .../matrix/api/permalink/PermalinkData.kt | 38 ++++--------------- .../impl/permalink/DefaultPermalinkParser.kt | 21 +++++----- .../mentions/MentionSpanProvider.kt | 6 ++- .../impl/mentions/MentionSpanProviderTest.kt | 12 +++--- 6 files changed, 32 insertions(+), 71 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index 1fb3c5648e..2e71759269 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -280,25 +280,12 @@ class LoggedInFlowNode @AssistedInject constructor( // FIXME: Add a user profile screen. Timber.e("User link clicked: ${data.userId}. TODO Add a user profile screen") } - is PermalinkData.RoomIdLink -> { - backstack.push(NavTarget.Room(data.roomId.toRoomIdOrAlias())) - } - is PermalinkData.RoomAliasLink -> { - backstack.push(NavTarget.Room(data.roomAlias.toRoomIdOrAlias())) - } - is PermalinkData.EventIdAliasLink -> { + is PermalinkData.RoomLink -> { backstack.push( NavTarget.Room( - data.roomAlias.toRoomIdOrAlias(), - initialElement = RoomNavigationTarget.Messages(data.eventId) - ) - ) - } - is PermalinkData.EventIdLink -> { - backstack.push( - NavTarget.Room( - data.roomId.toRoomIdOrAlias(), - initialElement = RoomNavigationTarget.Messages(data.eventId) + data.roomIdOrAlias, + initialElement = RoomNavigationTarget.Messages(data.eventId), + // TODO Use the viaParameters ) ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index 0a4d1f039d..b87e1cb343 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -123,10 +123,7 @@ class MessagesNode @AssistedInject constructor( callback?.onPermalinkClicked(permalink) } } - is PermalinkData.RoomIdLink, - is PermalinkData.RoomAliasLink, - is PermalinkData.EventIdAliasLink, - is PermalinkData.EventIdLink -> { + is PermalinkData.RoomLink -> { callback?.onPermalinkClicked(permalink) } is PermalinkData.FallbackLink, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt index fff7e22380..ab979320d7 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt @@ -19,10 +19,11 @@ package io.element.android.libraries.matrix.api.permalink import android.net.Uri import androidx.compose.runtime.Immutable import io.element.android.libraries.matrix.api.core.EventId -import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.core.UserId import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf /** * This sealed class represents all the permalink cases. @@ -30,36 +31,11 @@ import kotlinx.collections.immutable.ImmutableList */ @Immutable sealed interface PermalinkData { - sealed interface RoomLink : PermalinkData { - val viaParameters: ImmutableList - } - - data class RoomIdLink( - val roomId: RoomId, - override val viaParameters: ImmutableList - ) : RoomLink - - data class RoomAliasLink( - val roomAlias: RoomAlias, - override val viaParameters: ImmutableList - ) : RoomLink - - sealed interface EventLink : PermalinkData { - val eventId: EventId - val viaParameters: ImmutableList - } - - data class EventIdLink( - val roomId: RoomId, - override val eventId: EventId, - override val viaParameters: ImmutableList - ) : EventLink - - data class EventIdAliasLink( - val roomAlias: RoomAlias, - override val eventId: EventId, - override val viaParameters: ImmutableList - ) : EventLink + data class RoomLink( + val roomIdOrAlias: RoomIdOrAlias, + val eventId: EventId? = null, + val viaParameters: ImmutableList = persistentListOf() + ) : PermalinkData /* * &room_name=Team2 diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParser.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParser.kt index f47247c0f5..b3b7d1fe38 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParser.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParser.kt @@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.permalink.MatrixToConverter import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkParser @@ -60,24 +61,24 @@ class DefaultPermalinkParser @Inject constructor( } else { val viaParameters = result.via.toImmutableList() when (val id = result.id) { - is MatrixId.Room -> PermalinkData.RoomIdLink( - roomId = RoomId(id.id), - viaParameters = viaParameters, - ) is MatrixId.User -> PermalinkData.UserLink( userId = UserId(id.id), ) - is MatrixId.RoomAlias -> PermalinkData.RoomAliasLink( - roomAlias = RoomAlias(id.alias), + is MatrixId.Room -> PermalinkData.RoomLink( + roomIdOrAlias = RoomId(id.id).toRoomIdOrAlias(), viaParameters = viaParameters, ) - is MatrixId.EventOnRoomId -> PermalinkData.EventIdLink( - roomId = RoomId(id.roomId), + is MatrixId.RoomAlias -> PermalinkData.RoomLink( + roomIdOrAlias = RoomAlias(id.alias).toRoomIdOrAlias(), + viaParameters = viaParameters, + ) + is MatrixId.EventOnRoomId -> PermalinkData.RoomLink( + roomIdOrAlias = RoomId(id.roomId).toRoomIdOrAlias(), eventId = EventId(id.eventId), viaParameters = viaParameters, ) - is MatrixId.EventOnRoomAlias -> PermalinkData.EventIdAliasLink( - roomAlias = RoomAlias(id.alias), + is MatrixId.EventOnRoomAlias -> PermalinkData.RoomLink( + roomIdOrAlias = RoomAlias(id.alias).toRoomIdOrAlias(), eventId = EventId(id.eventId), viaParameters = viaParameters, ) diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt index 7972b4d6a4..7d8bfd34ce 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt @@ -42,6 +42,7 @@ import io.element.android.libraries.designsystem.theme.mentionPillText import io.element.android.libraries.matrix.api.core.RoomAlias 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.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkParser import kotlinx.collections.immutable.persistentListOf @@ -140,8 +141,9 @@ internal fun MentionSpanPreview() { return when (uriString) { "https://matrix.to/#/@me:matrix.org" -> PermalinkData.UserLink(UserId("@me:matrix.org")) "https://matrix.to/#/@other:matrix.org" -> PermalinkData.UserLink(UserId("@other:matrix.org")) - "https://matrix.to/#/#room:matrix.org" -> PermalinkData.RoomAliasLink( - roomAlias = RoomAlias("#room:matrix.org"), + "https://matrix.to/#/#room:matrix.org" -> PermalinkData.RoomLink( + roomIdOrAlias = RoomAlias("#room:matrix.org").toRoomIdOrAlias(), + eventId = null, viaParameters = persistentListOf(), ) else -> TODO() diff --git a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt index d7e2bff7b1..a3a4ee7884 100644 --- a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt +++ b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt @@ -20,12 +20,12 @@ import android.graphics.Color import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser import io.element.android.libraries.textcomposer.mentions.MentionSpanProvider import io.element.android.tests.testutils.WarmUpRule -import kotlinx.collections.immutable.persistentListOf import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -69,9 +69,8 @@ class MentionSpanProviderTest { @Test fun `getting mention span for a room should return a MentionSpan with normal colors`() { permalinkParser.givenResult( - PermalinkData.RoomAliasLink( - roomAlias = RoomAlias("#room:matrix.org"), - viaParameters = persistentListOf(), + PermalinkData.RoomLink( + roomIdOrAlias = RoomAlias("#room:matrix.org").toRoomIdOrAlias(), ) ) val mentionSpan = mentionSpanProvider.getMentionSpanFor("#room:matrix.org", "https://matrix.to/#/#room:matrix.org") @@ -82,9 +81,8 @@ class MentionSpanProviderTest { @Test fun `getting mention span for @room should return a MentionSpan with normal colors`() { permalinkParser.givenResult( - PermalinkData.RoomAliasLink( - roomAlias = RoomAlias("#"), - viaParameters = persistentListOf(), + PermalinkData.RoomLink( + roomIdOrAlias = RoomAlias("#").toRoomIdOrAlias(), ) ) val mentionSpan = mentionSpanProvider.getMentionSpanFor("@room", "#") From c0bd527486566ca5ab10718c35f9d9862288209f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Apr 2024 15:16:53 +0200 Subject: [PATCH 09/46] Resolve RoomId in RoomFlowNode. --- .../android/appnav/room/RoomFlowNode.kt | 75 ++++++++++++++----- .../joinroom/api/JoinRoomEntryPoint.kt | 2 + .../features/joinroom/impl/JoinRoomNode.kt | 6 +- .../joinroom/impl/JoinRoomPresenter.kt | 10 ++- .../joinroom/impl/di/JoinRoomModule.kt | 8 +- .../joinroom/impl/JoinRoomPresenterTest.kt | 7 +- .../libraries/matrix/api/MatrixClient.kt | 2 +- .../libraries/matrix/impl/RustMatrixClient.kt | 35 +++------ .../libraries/matrix/test/FakeMatrixClient.kt | 4 +- 9 files changed, 98 insertions(+), 51 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 048c1826f2..a752920800 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 @@ -43,17 +43,19 @@ import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs +import io.element.android.libraries.designsystem.components.async.AsyncFailure import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.RoomMembershipObserver -import kotlinx.coroutines.Job import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize import timber.log.Timber import java.util.Optional @@ -87,43 +89,72 @@ class RoomFlowNode @AssistedInject constructor( data object Loading : NavTarget @Parcelize - data object JoinRoom : NavTarget + data class JoinRoom(val roomId: RoomId) : NavTarget + + @Parcelize + data class RoomAliasError(val roomAlias: RoomAlias) : NavTarget @Parcelize data class JoinedRoom(val roomId: RoomId) : NavTarget } - private var roomMembershipJob: Job? = null - override fun onBuilt() { super.onBuilt() + resolveRoomId() + } + + private fun resolveRoomId() { + lifecycleScope.launch { + when (val i = inputs.roomIdOrAlias) { + is RoomIdOrAlias.Alias -> { + client.resolveRoomAlias(i.roomAlias) + .onFailure { + Timber.e(it, "Failed to resolve room alias") + backstack.newRoot(NavTarget.RoomAliasError(i.roomAlias)) + } + .onSuccess { + subscribeToRoomInfoFlow(it) + } + } + is RoomIdOrAlias.Id -> { + subscribeToRoomInfoFlow(i.roomId) + } + } + } + } + + private fun subscribeToRoomInfoFlow(roomId: RoomId) { client.getRoomInfoFlow( - inputs.roomIdOrAlias + roomId ).onEach { roomInfo -> Timber.d("Room membership: ${roomInfo.map { it.currentUserMembership }}") val info = roomInfo.getOrNull() if (info?.currentUserMembership == CurrentUserMembership.JOINED) { - backstack.newRoot(NavTarget.JoinedRoom(info.id)) - // When leaving the room from this session only, navigate up. - roomMembershipJob?.cancel() - roomMembershipJob = roomMembershipObserver.updates - .filter { update -> update.roomId == info.id && !update.isUserInRoom } - .onEach { - navigateUp() - } - .launchIn(lifecycleScope) + backstack.newRoot(NavTarget.JoinedRoom(roomId)) } else { - backstack.newRoot(NavTarget.JoinRoom) + backstack.newRoot(NavTarget.JoinRoom(roomId)) } } .launchIn(lifecycleScope) + + // When leaving the room from this session only, navigate up. + roomMembershipObserver.updates + .filter { update -> update.roomId == roomId && !update.isUserInRoom } + .onEach { + navigateUp() + } + .launchIn(lifecycleScope) } override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { NavTarget.Loading -> loadingNode(buildContext) - NavTarget.JoinRoom -> { - val inputs = JoinRoomEntryPoint.Inputs(inputs.roomIdOrAlias, roomDescription = inputs.roomDescription) + is NavTarget.JoinRoom -> { + val inputs = JoinRoomEntryPoint.Inputs( + roomId = navTarget.roomId, + roomIdOrAlias = inputs.roomIdOrAlias, + roomDescription = inputs.roomDescription, + ) joinRoomEntryPoint.createNode(this, buildContext, inputs) } is NavTarget.JoinedRoom -> { @@ -131,6 +162,7 @@ class RoomFlowNode @AssistedInject constructor( val inputs = JoinedRoomFlowNode.Inputs(navTarget.roomId, initialElement = inputs.initialElement) createNode(buildContext, plugins = listOf(inputs) + roomFlowNodeCallback) } + is NavTarget.RoomAliasError -> roomAliasErrorNode(buildContext, navTarget.roomAlias) } } @@ -140,6 +172,15 @@ class RoomFlowNode @AssistedInject constructor( } } + private fun roomAliasErrorNode(buildContext: BuildContext, roomAlias: RoomAlias) = node(buildContext) { + Box(modifier = it.fillMaxSize(), contentAlignment = Alignment.Center) { + AsyncFailure( + throwable = Exception("Unable to resolve alias ${roomAlias.value}"), + onRetry = { resolveRoomId() }, + ) + } + } + @Composable override fun View(modifier: Modifier) { BackstackView(transitionHandler = JumpToEndTransitionHandler()) diff --git a/features/joinroom/api/src/main/kotlin/io/element/android/features/joinroom/api/JoinRoomEntryPoint.kt b/features/joinroom/api/src/main/kotlin/io/element/android/features/joinroom/api/JoinRoomEntryPoint.kt index 0986af81d3..d62a9819c9 100644 --- a/features/joinroom/api/src/main/kotlin/io/element/android/features/joinroom/api/JoinRoomEntryPoint.kt +++ b/features/joinroom/api/src/main/kotlin/io/element/android/features/joinroom/api/JoinRoomEntryPoint.kt @@ -21,6 +21,7 @@ import com.bumble.appyx.core.node.Node import io.element.android.features.roomdirectory.api.RoomDescription 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.core.RoomIdOrAlias import java.util.Optional @@ -28,6 +29,7 @@ interface JoinRoomEntryPoint : FeatureEntryPoint { fun createNode(parentNode: Node, buildContext: BuildContext, inputs: Inputs): Node data class Inputs( + val roomId: RoomId, val roomIdOrAlias: RoomIdOrAlias, val roomDescription: Optional, ) : NodeInputs diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomNode.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomNode.kt index 516c789079..d83a5f4b20 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomNode.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomNode.kt @@ -37,7 +37,11 @@ class JoinRoomNode @AssistedInject constructor( private val acceptDeclineInviteView: AcceptDeclineInviteView, ) : Node(buildContext, plugins = plugins) { private val inputs: JoinRoomEntryPoint.Inputs = inputs() - private val presenter = presenterFactory.create(inputs.roomIdOrAlias, inputs.roomDescription) + private val presenter = presenterFactory.create( + inputs.roomId, + inputs.roomIdOrAlias, + inputs.roomDescription, + ) @Composable override fun View(modifier: Modifier) { 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 818857c69a..7bc0f0facf 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 @@ -30,6 +30,7 @@ import io.element.android.features.invite.api.response.InviteData import io.element.android.features.roomdirectory.api.RoomDescription import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.MatrixRoomInfo @@ -38,19 +39,24 @@ import kotlinx.coroutines.launch import java.util.Optional class JoinRoomPresenter @AssistedInject constructor( + @Assisted private val roomId: RoomId, @Assisted private val roomIdOrAlias: RoomIdOrAlias, @Assisted private val roomDescription: Optional, private val matrixClient: MatrixClient, private val acceptDeclineInvitePresenter: Presenter, ) : Presenter { interface Factory { - fun create(roomIdOrAlias: RoomIdOrAlias, roomDescription: Optional): JoinRoomPresenter + fun create( + roomId: RoomId, + roomIdOrAlias: RoomIdOrAlias, + roomDescription: Optional, + ): JoinRoomPresenter } @Composable override fun present(): JoinRoomState { val coroutineScope = rememberCoroutineScope() - val roomInfo by matrixClient.getRoomInfoFlow(roomIdOrAlias).collectAsState(initial = Optional.empty()) + val roomInfo by matrixClient.getRoomInfoFlow(roomId).collectAsState(initial = Optional.empty()) val contentState by produceState(initialValue = ContentState.Loading(roomIdOrAlias), key1 = roomInfo) { value = when { roomInfo.isPresent -> { diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/JoinRoomModule.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/JoinRoomModule.kt index 0bce71b885..3a9e0aec48 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/JoinRoomModule.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/di/JoinRoomModule.kt @@ -25,6 +25,7 @@ import io.element.android.features.roomdirectory.api.RoomDescription import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import java.util.Optional @@ -37,8 +38,13 @@ object JoinRoomModule { acceptDeclineInvitePresenter: Presenter, ): JoinRoomPresenter.Factory { return object : JoinRoomPresenter.Factory { - override fun create(roomIdOrAlias: RoomIdOrAlias, roomDescription: Optional): JoinRoomPresenter { + override fun create( + roomId: RoomId, + roomIdOrAlias: RoomIdOrAlias, + roomDescription: Optional, + ): JoinRoomPresenter { return JoinRoomPresenter( + roomId = roomId, roomIdOrAlias = roomIdOrAlias, roomDescription = roomDescription, matrixClient = client, diff --git a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt index e61dd18aad..543a8ad9d7 100644 --- a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt +++ b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt @@ -23,7 +23,9 @@ import io.element.android.features.invite.api.response.anAcceptDeclineInviteStat import io.element.android.features.roomdirectory.api.RoomDescription import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_NAME @@ -49,7 +51,7 @@ class JoinRoomPresenterTest { val presenter = createJoinRoomPresenter() presenter.test { awaitItem().also { state -> - assertThat(state.contentState).isEqualTo(ContentState.Loading(A_ROOM_ID)) + assertThat(state.contentState).isEqualTo(ContentState.Loading(A_ROOM_ID.toRoomIdOrAlias())) assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.Unknown) assertThat(state.acceptDeclineInviteState).isEqualTo(anAcceptDeclineInviteState()) } @@ -245,6 +247,7 @@ class JoinRoomPresenterTest { ): JoinRoomPresenter { return JoinRoomPresenter( roomId = roomId, + roomIdOrAlias = roomId.toRoomIdOrAlias(), roomDescription = roomDescription, matrixClient = matrixClient, acceptDeclineInvitePresenter = acceptDeclineInvitePresenter @@ -255,7 +258,7 @@ class JoinRoomPresenterTest { roomId: RoomId = A_ROOM_ID, name: String? = A_ROOM_NAME, topic: String? = "A room about something", - alias: String? = "#alias:matrix.org", + alias: RoomAlias? = RoomAlias("#alias:matrix.org"), avatarUrl: String? = null, joinRule: RoomDescription.JoinRule = RoomDescription.JoinRule.UNKNOWN, numberOfMembers: Long = 2L diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index e6bbee4f9a..ac2a00dbdc 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -95,7 +95,7 @@ interface MatrixClient : Closeable { suspend fun getAccountManagementUrl(action: AccountManagementAction?): Result suspend fun uploadMedia(mimeType: String, data: ByteArray, progressCallback: ProgressCallback?): Result fun roomMembershipObserver(): RoomMembershipObserver - fun getRoomInfoFlow(roomIdOrAlias: RoomIdOrAlias): Flow> + fun getRoomInfoFlow(roomId: RoomId): Flow> fun isMe(userId: UserId?) = userId == sessionId diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index adc96d0b84..48683d77ba 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -562,33 +562,18 @@ class RustMatrixClient( override fun roomMembershipObserver(): RoomMembershipObserver = roomMembershipObserver - override fun getRoomInfoFlow(roomIdOrAlias: RoomIdOrAlias): Flow> { + override fun getRoomInfoFlow(roomId: RoomId): Flow> { return flow { - val roomId = when (roomIdOrAlias) { - is RoomIdOrAlias.Alias -> { - resolveRoomAlias(roomIdOrAlias.roomAlias) - .onFailure { - // TODO Get a way to emit an error - Timber.e("Unable to resolve room alias ${roomIdOrAlias.roomAlias}") - emit(Optional.empty()) - return@flow - } - .getOrNull() - } - is RoomIdOrAlias.Id -> roomIdOrAlias.roomId + var room = getRoom(roomId) + if (room == null) { + emit(Optional.empty()) + awaitRoom(roomId, INFINITE) + room = getRoom(roomId) } - if (roomId != null) { - var room = getRoom(roomId) - if (room == null) { - emit(Optional.empty()) - awaitRoom(roomId, INFINITE) - room = getRoom(roomId) - } - room?.use { - room.roomInfoFlow - .map { roomInfo -> Optional.of(roomInfo) } - .collect(this) - } + room?.use { + room.roomInfoFlow + .map { roomInfo -> Optional.of(roomInfo) } + .collect(this) } }.distinctUntilChanged() } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index 1fc51cda94..42557db1dd 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -107,7 +107,7 @@ class FakeMatrixClient( Result.success(it) } - var getRoomInfoFlowLambda = { _: RoomIdOrAlias -> + var getRoomInfoFlowLambda = { _: RoomId -> flowOf>(Optional.empty()) } @@ -293,5 +293,5 @@ class FakeMatrixClient( return Result.success(visitedRoomsId) } - override fun getRoomInfoFlow(roomIdOrAlias: RoomIdOrAlias) = getRoomInfoFlowLambda(roomIdOrAlias) + override fun getRoomInfoFlow(roomId: RoomId) = getRoomInfoFlowLambda(roomId) } From e1564e5a2b5d75cd61fee868a62d7d60146f2b2d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Apr 2024 16:53:05 +0200 Subject: [PATCH 10/46] Introduce RoomAliasResolverNode with error and retry handling. --- .../android/appnav/room/RoomFlowNode.kt | 55 ++--- .../appnav/room/resolver/ResolveRoomModule.kt | 42 ++++ .../room/resolver/RoomAliasResolverEvents.kt | 21 ++ .../room/resolver/RoomAliasResolverNode.kt | 68 ++++++ .../resolver/RoomAliasResolverPresenter.kt | 72 ++++++ .../room/resolver/RoomAliasResolverState.kt | 29 +++ .../RoomAliasResolverStateProvider.kt | 47 ++++ .../room/resolver/RoomAliasResolverView.kt | 205 ++++++++++++++++++ .../features/messages/impl/MessagesNode.kt | 1 + 9 files changed, 509 insertions(+), 31 deletions(-) create mode 100644 appnav/src/main/kotlin/io/element/android/appnav/room/resolver/ResolveRoomModule.kt create mode 100644 appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverEvents.kt create mode 100644 appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverNode.kt create mode 100644 appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverPresenter.kt create mode 100644 appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverState.kt create mode 100644 appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverStateProvider.kt create mode 100644 appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverView.kt 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 a752920800..d0fc830892 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 @@ -36,6 +36,7 @@ import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.appnav.room.joined.JoinedRoomFlowNode import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode +import io.element.android.appnav.room.resolver.RoomAliasResolverNode import io.element.android.features.joinroom.api.JoinRoomEntryPoint import io.element.android.features.roomdirectory.api.RoomDescription import io.element.android.libraries.architecture.BackstackView @@ -43,7 +44,6 @@ import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs -import io.element.android.libraries.designsystem.components.async.AsyncFailure import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient @@ -89,10 +89,10 @@ class RoomFlowNode @AssistedInject constructor( data object Loading : NavTarget @Parcelize - data class JoinRoom(val roomId: RoomId) : NavTarget + data class Resolving(val roomAlias: RoomAlias) : NavTarget @Parcelize - data class RoomAliasError(val roomAlias: RoomAlias) : NavTarget + data class JoinRoom(val roomId: RoomId) : NavTarget @Parcelize data class JoinedRoom(val roomId: RoomId) : NavTarget @@ -107,14 +107,7 @@ class RoomFlowNode @AssistedInject constructor( lifecycleScope.launch { when (val i = inputs.roomIdOrAlias) { is RoomIdOrAlias.Alias -> { - client.resolveRoomAlias(i.roomAlias) - .onFailure { - Timber.e(it, "Failed to resolve room alias") - backstack.newRoot(NavTarget.RoomAliasError(i.roomAlias)) - } - .onSuccess { - subscribeToRoomInfoFlow(it) - } + backstack.newRoot(NavTarget.Resolving(i.roomAlias)) } is RoomIdOrAlias.Id -> { subscribeToRoomInfoFlow(i.roomId) @@ -125,16 +118,17 @@ class RoomFlowNode @AssistedInject constructor( private fun subscribeToRoomInfoFlow(roomId: RoomId) { client.getRoomInfoFlow( - roomId - ).onEach { roomInfo -> - Timber.d("Room membership: ${roomInfo.map { it.currentUserMembership }}") - val info = roomInfo.getOrNull() - if (info?.currentUserMembership == CurrentUserMembership.JOINED) { - backstack.newRoot(NavTarget.JoinedRoom(roomId)) - } else { - backstack.newRoot(NavTarget.JoinRoom(roomId)) + roomId = roomId + ) + .onEach { roomInfo -> + Timber.d("Room membership: ${roomInfo.map { it.currentUserMembership }}") + val info = roomInfo.getOrNull() + if (info?.currentUserMembership == CurrentUserMembership.JOINED) { + backstack.newRoot(NavTarget.JoinedRoom(roomId)) + } else { + backstack.newRoot(NavTarget.JoinRoom(roomId)) + } } - } .launchIn(lifecycleScope) // When leaving the room from this session only, navigate up. @@ -148,7 +142,16 @@ class RoomFlowNode @AssistedInject constructor( override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { - NavTarget.Loading -> loadingNode(buildContext) + is NavTarget.Loading -> loadingNode(buildContext) + is NavTarget.Resolving -> { + val callback = object : RoomAliasResolverNode.Callback { + override fun onAliasResolved(roomId: RoomId) { + backstack.newRoot(NavTarget.JoinRoom(roomId)) + } + } + val params = RoomAliasResolverNode.Inputs(navTarget.roomAlias) + createNode(buildContext, listOf(callback, params)) + } is NavTarget.JoinRoom -> { val inputs = JoinRoomEntryPoint.Inputs( roomId = navTarget.roomId, @@ -162,7 +165,6 @@ class RoomFlowNode @AssistedInject constructor( val inputs = JoinedRoomFlowNode.Inputs(navTarget.roomId, initialElement = inputs.initialElement) createNode(buildContext, plugins = listOf(inputs) + roomFlowNodeCallback) } - is NavTarget.RoomAliasError -> roomAliasErrorNode(buildContext, navTarget.roomAlias) } } @@ -172,15 +174,6 @@ class RoomFlowNode @AssistedInject constructor( } } - private fun roomAliasErrorNode(buildContext: BuildContext, roomAlias: RoomAlias) = node(buildContext) { - Box(modifier = it.fillMaxSize(), contentAlignment = Alignment.Center) { - AsyncFailure( - throwable = Exception("Unable to resolve alias ${roomAlias.value}"), - onRetry = { resolveRoomId() }, - ) - } - } - @Composable override fun View(modifier: Modifier) { BackstackView(transitionHandler = JumpToEndTransitionHandler()) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/ResolveRoomModule.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/ResolveRoomModule.kt new file mode 100644 index 0000000000..687fefc5c5 --- /dev/null +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/ResolveRoomModule.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 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.appnav.room.resolver + +import com.squareup.anvil.annotations.ContributesTo +import dagger.Module +import dagger.Provides +import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomAlias + +@Module +@ContributesTo(SessionScope::class) +object ResolveRoomModule { + @Provides + fun providesJoinRoomPresenterFactory( + client: MatrixClient, + ): RoomAliasResolverPresenter.Factory { + return object : RoomAliasResolverPresenter.Factory { + override fun create(roomAlias: RoomAlias): RoomAliasResolverPresenter { + return RoomAliasResolverPresenter( + roomAlias = roomAlias, + matrixClient = client, + ) + } + } + } +} diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverEvents.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverEvents.kt new file mode 100644 index 0000000000..1eb3c8ffe9 --- /dev/null +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverEvents.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024 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.appnav.room.resolver + +sealed interface RoomAliasResolverEvents { + data object Retry : RoomAliasResolverEvents +} diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverNode.kt new file mode 100644 index 0000000000..c429ec1a95 --- /dev/null +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverNode.kt @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024 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.appnav.room.resolver + +import androidx.compose.runtime.Composable +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 dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import io.element.android.anvilannotations.ContributesNode +import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.inputs +import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.matrix.api.core.RoomAlias +import io.element.android.libraries.matrix.api.core.RoomId + +@ContributesNode(SessionScope::class) +class RoomAliasResolverNode @AssistedInject constructor( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, + presenterFactory: RoomAliasResolverPresenter.Factory, +) : Node(buildContext, plugins = plugins) { + data class Inputs( + val roomAlias: RoomAlias + ) : NodeInputs + + private val inputs = inputs() + + private val presenter = presenterFactory.create( + inputs.roomAlias + ) + + interface Callback : Plugin { + fun onAliasResolved(roomId: RoomId) + } + + private fun onAliasResolved(roomId: RoomId) { + plugins().forEach { it.onAliasResolved(roomId) } + } + + @Composable + override fun View(modifier: Modifier) { + val state = presenter.present() + RoomAliasResolverView( + state = state, + onAliasResolved = ::onAliasResolved, + onBackPressed = ::navigateUp, + modifier = modifier + ) + } +} diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverPresenter.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverPresenter.kt new file mode 100644 index 0000000000..5e9e3382d5 --- /dev/null +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverPresenter.kt @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024 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.appnav.room.resolver + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.architecture.runCatchingUpdatingState +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomAlias +import io.element.android.libraries.matrix.api.core.RoomId +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +class RoomAliasResolverPresenter @AssistedInject constructor( + @Assisted private val roomAlias: RoomAlias, + private val matrixClient: MatrixClient, +) : Presenter { + interface Factory { + fun create( + roomAlias: RoomAlias, + ): RoomAliasResolverPresenter + } + + @Composable + override fun present(): RoomAliasResolverState { + val coroutineScope = rememberCoroutineScope() + val resolveState: MutableState> = remember { mutableStateOf(AsyncData.Uninitialized) } + LaunchedEffect(Unit) { + resolveAlias(resolveState) + } + + fun handleEvents(event: RoomAliasResolverEvents) { + when (event) { + RoomAliasResolverEvents.Retry -> coroutineScope.resolveAlias(resolveState) + } + } + + return RoomAliasResolverState( + roomAlias = roomAlias, + resolveState = resolveState.value, + eventSink = ::handleEvents + ) + } + + private fun CoroutineScope.resolveAlias(resolveState: MutableState>) = launch { + suspend { + matrixClient.resolveRoomAlias(roomAlias).getOrThrow() + }.runCatchingUpdatingState(resolveState) + } +} diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverState.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverState.kt new file mode 100644 index 0000000000..4f800a76cd --- /dev/null +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverState.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 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.appnav.room.resolver + +import androidx.compose.runtime.Immutable +import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.matrix.api.core.RoomAlias +import io.element.android.libraries.matrix.api.core.RoomId + +@Immutable +data class RoomAliasResolverState( + val roomAlias: RoomAlias, + val resolveState: AsyncData, + val eventSink: (RoomAliasResolverEvents) -> Unit +) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverStateProvider.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverStateProvider.kt new file mode 100644 index 0000000000..9584bb21b8 --- /dev/null +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverStateProvider.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 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.appnav.room.resolver + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.matrix.api.core.RoomAlias +import io.element.android.libraries.matrix.api.core.RoomId + +open class RoomAliasResolverStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aRoomAliasResolverState(), + aRoomAliasResolverState( + resolveState = AsyncData.Loading(), + ), + aRoomAliasResolverState( + resolveState = AsyncData.Failure(Exception("Error")), + ), + ) +} + +fun aRoomAliasResolverState( + roomAlias: RoomAlias = A_ROOM_ALIAS, + resolveState: AsyncData = AsyncData.Uninitialized, + eventSink: (RoomAliasResolverEvents) -> Unit = {} +) = RoomAliasResolverState( + roomAlias = roomAlias, + resolveState = resolveState, + eventSink = eventSink, +) + +private val A_ROOM_ALIAS = RoomAlias("#exa:matrix.org") diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverView.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverView.kt new file mode 100644 index 0000000000..e208119a16 --- /dev/null +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverView.kt @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2024 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.appnav.room.resolver + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +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 +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom +import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage +import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.preview.ElementPreview +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.CircularProgressIndicator +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 RoomAliasResolverView( + state: RoomAliasResolverState, + onBackPressed: () -> Unit, + onAliasResolved: (RoomId) -> Unit, + modifier: Modifier = Modifier, +) { + val latestOnAliasResolved by rememberUpdatedState(onAliasResolved) + LaunchedEffect(state.resolveState) { + if (state.resolveState is AsyncData.Success) { + latestOnAliasResolved(state.resolveState.data) + } + } + + HeaderFooterPage( + modifier = modifier, + paddingValues = PaddingValues(16.dp), + topBar = { + RoomAliasResolverTopBar(onBackClicked = onBackPressed) + }, + content = { + RoomAliasResolverContent(state = state) + }, + footer = { + RoomAliasResolverFooter( + state = state, + ) + } + ) +} + +@Composable +private fun RoomAliasResolverFooter( + state: RoomAliasResolverState, + modifier: Modifier = Modifier, +) { + when (state.resolveState) { + is AsyncData.Failure -> { + Button( + text = stringResource(CommonStrings.action_retry), + onClick = { + state.eventSink(RoomAliasResolverEvents.Retry) + }, + modifier = modifier.fillMaxWidth(), + size = ButtonSize.Medium, + ) + } + is AsyncData.Loading -> { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + ) { + CircularProgressIndicator() + } + } + AsyncData.Uninitialized, + is AsyncData.Success -> Unit + } +} + +@Composable +private fun RoomAliasResolverContent( + state: RoomAliasResolverState, + modifier: Modifier = Modifier, +) { + ContentScaffold( + modifier = modifier, + avatar = { + PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp) + }, + title = { + }, + subtitle = { + Title(state.roomAlias.value) + }, + description = { + if (state.resolveState.isFailure()) { + Text( + text = "Failed to resolve room alias", + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.error, + ) + } + }, + memberCount = { + } + ) +} + +@Composable +private fun ContentScaffold( + avatar: @Composable () -> Unit, + title: @Composable () -> Unit, + subtitle: @Composable () -> Unit, + modifier: Modifier = Modifier, + description: @Composable (() -> Unit)? = null, + memberCount: @Composable (() -> Unit)? = null, +) { + Column( + modifier = modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + avatar() + Spacer(modifier = Modifier.height(16.dp)) + title() + Spacer(modifier = Modifier.height(8.dp)) + subtitle() + Spacer(modifier = Modifier.height(8.dp)) + if (memberCount != null) { + memberCount() + } + Spacer(modifier = Modifier.height(8.dp)) + if (description != null) { + description() + } + Spacer(modifier = Modifier.height(24.dp)) + } +} + +@Composable +private fun Title(title: String, modifier: Modifier = Modifier) { + Text( + modifier = modifier, + text = title, + style = ElementTheme.typography.fontHeadingMdBold, + textAlign = TextAlign.Center, + color = ElementTheme.colors.textPrimary, + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun RoomAliasResolverTopBar( + onBackClicked: () -> Unit, +) { + TopAppBar( + navigationIcon = { + BackButton(onClick = onBackClicked) + }, + title = {}, + ) +} + +@PreviewLightDark +@Composable +internal fun RoomAliasResolverViewPreview(@PreviewParameter(RoomAliasResolverStateProvider::class) state: RoomAliasResolverState) = ElementPreview { + RoomAliasResolverView( + state = state, + onAliasResolved = { }, + onBackPressed = { } + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index b87e1cb343..b7958f792c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -124,6 +124,7 @@ class MessagesNode @AssistedInject constructor( } } is PermalinkData.RoomLink -> { + // TODO Handle click on current Room callback?.onPermalinkClicked(permalink) } is PermalinkData.FallbackLink, From 0e8e6050dd78f58e194bb4ce40b7223cc19934d0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Apr 2024 17:55:33 +0200 Subject: [PATCH 11/46] Add Retry on Join room node. --- .../features/joinroom/impl/JoinRoomEvents.kt | 1 + .../joinroom/impl/JoinRoomPresenter.kt | 29 ++++- .../features/joinroom/impl/JoinRoomState.kt | 1 + .../joinroom/impl/JoinRoomStateProvider.kt | 17 +++ .../features/joinroom/impl/JoinRoomView.kt | 104 ++++++++++++------ 5 files changed, 116 insertions(+), 36 deletions(-) diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomEvents.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomEvents.kt index 999030cd50..2dddb22e13 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomEvents.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomEvents.kt @@ -17,6 +17,7 @@ package io.element.android.features.joinroom.impl sealed interface JoinRoomEvents { + data object Retry : JoinRoomEvents data object JoinRoom : JoinRoomEvents data object AcceptInvite : JoinRoomEvents data object DeclineInvite : JoinRoomEvents 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 7bc0f0facf..504294037f 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 @@ -20,8 +20,11 @@ import androidx.annotation.VisibleForTesting import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.produceState +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents @@ -32,6 +35,7 @@ import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomIdOrAlias +import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.preview.RoomPreview @@ -56,8 +60,13 @@ class JoinRoomPresenter @AssistedInject constructor( @Composable override fun present(): JoinRoomState { val coroutineScope = rememberCoroutineScope() + var retryCount by remember { mutableIntStateOf(0) } val roomInfo by matrixClient.getRoomInfoFlow(roomId).collectAsState(initial = Optional.empty()) - val contentState by produceState(initialValue = ContentState.Loading(roomIdOrAlias), key1 = roomInfo) { + val contentState by produceState( + initialValue = ContentState.Loading(roomIdOrAlias), + key1 = roomInfo, + key2 = retryCount, + ) { value = when { roomInfo.isPresent -> { roomInfo.get().toContentState() @@ -67,10 +76,17 @@ class JoinRoomPresenter @AssistedInject constructor( } else -> { coroutineScope.launch { - val result = matrixClient.getRoomPreview(roomIdOrAlias) - value = result.getOrNull() - ?.toContentState() - ?: ContentState.UnknownRoom(roomIdOrAlias) + val result = matrixClient.getRoomPreview(roomId.toRoomIdOrAlias()) + value = result.fold( + onSuccess = { it.toContentState() }, + onFailure = { throwable -> + if (throwable.message?.contains("403") == true) { + ContentState.UnknownRoom(roomIdOrAlias) + } else { + ContentState.Failure(roomIdOrAlias, throwable) + } + } + ) } ContentState.Loading(roomIdOrAlias) } @@ -93,6 +109,9 @@ class JoinRoomPresenter @AssistedInject constructor( AcceptDeclineInviteEvents.DeclineInvite(inviteData) ) } + JoinRoomEvents.Retry -> { + retryCount++ + } } } diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt index bf57a0fdcf..4d91135e9a 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt @@ -38,6 +38,7 @@ data class JoinRoomState( sealed interface ContentState { data class Loading(val roomIdOrAlias: RoomIdOrAlias) : ContentState + data class Failure(val roomIdOrAlias: RoomIdOrAlias, val error: Throwable) : ContentState data class UnknownRoom(val roomIdOrAlias: RoomIdOrAlias) : ContentState data class Loaded( val roomId: RoomId, diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt index 3eea5e723b..7c34a05c6b 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt @@ -21,6 +21,7 @@ import io.element.android.features.invite.api.response.AcceptDeclineInviteState import io.element.android.features.invite.api.response.anAcceptDeclineInviteState import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias open class JoinRoomStateProvider : PreviewParameterProvider { @@ -41,9 +42,24 @@ open class JoinRoomStateProvider : PreviewParameterProvider { aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited) ), + aJoinRoomState( + contentState = aFailureContentState() + ), + aJoinRoomState( + contentState = aFailureContentState(roomIdOrAlias = A_ROOM_ALIAS.toRoomIdOrAlias()) + ), ) } +fun aFailureContentState( + roomIdOrAlias: RoomIdOrAlias = A_ROOM_ID.toRoomIdOrAlias() +): ContentState { + return ContentState.Failure( + roomIdOrAlias = roomIdOrAlias, + error = Exception("Error"), + ) +} + fun anUnknownContentState(roomId: RoomId = A_ROOM_ID) = ContentState.UnknownRoom(roomId.toRoomIdOrAlias()) fun aLoadingContentState(roomId: RoomId = A_ROOM_ID) = ContentState.Loading(roomId.toRoomIdOrAlias()) @@ -79,3 +95,4 @@ fun aJoinRoomState( ) private val A_ROOM_ID = RoomId("!exa:matrix.org") +private val A_ROOM_ALIAS = RoomAlias("#exa:matrix.org") diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt index 31f065c0e5..74c78d09a5 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt @@ -28,6 +28,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -52,6 +53,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.OutlinedButton import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar +import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -71,7 +73,7 @@ fun JoinRoomView( }, footer = { JoinRoomFooter( - joinAuthorisationStatus = state.joinAuthorisationStatus, + state = state, onAcceptInvite = { state.eventSink(JoinRoomEvents.AcceptInvite) }, @@ -81,6 +83,9 @@ fun JoinRoomView( onJoinRoom = { state.eventSink(JoinRoomEvents.JoinRoom) }, + onRetry = { + state.eventSink(JoinRoomEvents.Retry) + } ) } ) @@ -88,46 +93,57 @@ fun JoinRoomView( @Composable private fun JoinRoomFooter( - joinAuthorisationStatus: JoinAuthorisationStatus, + state: JoinRoomState, onAcceptInvite: () -> Unit, onDeclineInvite: () -> Unit, onJoinRoom: () -> Unit, + onRetry: () -> Unit, modifier: Modifier = Modifier, ) { - when (joinAuthorisationStatus) { - JoinAuthorisationStatus.IsInvited -> { - ButtonRowMolecule(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(20.dp)) { - OutlinedButton( - text = stringResource(CommonStrings.action_decline), - onClick = onDeclineInvite, - modifier = Modifier.weight(1f), - size = ButtonSize.Medium, - ) + if (state.contentState is ContentState.Failure) { + Button( + text = stringResource(CommonStrings.action_retry), + onClick = onRetry, + modifier = modifier.fillMaxWidth(), + size = ButtonSize.Medium, + ) + } else { + val joinAuthorisationStatus = state.joinAuthorisationStatus + when (joinAuthorisationStatus) { + JoinAuthorisationStatus.IsInvited -> { + ButtonRowMolecule(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(20.dp)) { + OutlinedButton( + text = stringResource(CommonStrings.action_decline), + onClick = onDeclineInvite, + modifier = Modifier.weight(1f), + size = ButtonSize.Medium, + ) + Button( + text = stringResource(CommonStrings.action_accept), + onClick = onAcceptInvite, + modifier = Modifier.weight(1f), + size = ButtonSize.Medium, + ) + } + } + JoinAuthorisationStatus.CanJoin -> { Button( - text = stringResource(CommonStrings.action_accept), - onClick = onAcceptInvite, - modifier = Modifier.weight(1f), + text = stringResource(R.string.screen_join_room_join_action), + onClick = onJoinRoom, + modifier = modifier.fillMaxWidth(), size = ButtonSize.Medium, ) } + JoinAuthorisationStatus.CanKnock -> { + Button( + text = stringResource(R.string.screen_join_room_knock_action), + onClick = onJoinRoom, + modifier = modifier.fillMaxWidth(), + size = ButtonSize.Medium, + ) + } + JoinAuthorisationStatus.Unknown -> Unit } - JoinAuthorisationStatus.CanJoin -> { - Button( - text = stringResource(R.string.screen_join_room_join_action), - onClick = onJoinRoom, - modifier = modifier.fillMaxWidth(), - size = ButtonSize.Medium, - ) - } - JoinAuthorisationStatus.CanKnock -> { - Button( - text = stringResource(R.string.screen_join_room_knock_action), - onClick = onJoinRoom, - modifier = modifier.fillMaxWidth(), - size = ButtonSize.Medium, - ) - } - JoinAuthorisationStatus.Unknown -> Unit } } @@ -187,6 +203,32 @@ private fun JoinRoomContent( }, ) } + is ContentState.Failure -> { + ContentScaffold( + modifier = modifier, + avatar = { + PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp) + }, + title = { + when (contentState.roomIdOrAlias) { + is RoomIdOrAlias.Alias -> { + Title(contentState.roomIdOrAlias.identifier) + } + is RoomIdOrAlias.Id -> { + PlaceholderAtom(width = 200.dp, height = 22.dp) + } + } + }, + subtitle = { + Text( + text = "Failed to get information about the room", + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.error, + ) + }, + ) + } + } } From 95fb8019478244388562f1ce8e09d5f6eac81924 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Apr 2024 17:57:52 +0200 Subject: [PATCH 12/46] FIXME --- .../main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index 2e71759269..960b393266 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -277,7 +277,7 @@ class LoggedInFlowNode @AssistedInject constructor( coroutineScope.launch { when (data) { is PermalinkData.UserLink -> { - // FIXME: Add a user profile screen. + // FIXME Add a user profile screen. Timber.e("User link clicked: ${data.userId}. TODO Add a user profile screen") } is PermalinkData.RoomLink -> { From f37ef1e0cdd66f568ade48be3682b72e03f0e946 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 10:32:10 +0200 Subject: [PATCH 13/46] Handle navigation to the same room. --- .../features/messages/impl/MessagesNode.kt | 22 ++++++++++-- .../matrix/api/room/navigation/Navigation.kt | 34 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/navigation/Navigation.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index b7958f792c..658149489c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -34,8 +34,10 @@ import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPr import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.androidutils.system.openUrlInExternalApp +import io.element.android.libraries.androidutils.system.toast import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.core.bool.orFalse +import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId @@ -43,6 +45,7 @@ import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkParser import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.navigation.isSameRoom import io.element.android.libraries.matrix.api.room.roomMembers import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.mediaplayer.api.MediaPlayer @@ -60,6 +63,8 @@ class MessagesNode @AssistedInject constructor( private val timelineItemPresenterFactories: TimelineItemPresenterFactories, private val mediaPlayer: MediaPlayer, private val permalinkParser: PermalinkParser, + @ApplicationContext + private val context: Context, ) : Node(buildContext, plugins = plugins), MessagesNavigator { private val presenter = presenterFactory.create(this) private val callback = plugins().firstOrNull() @@ -124,8 +129,7 @@ class MessagesNode @AssistedInject constructor( } } is PermalinkData.RoomLink -> { - // TODO Handle click on current Room - callback?.onPermalinkClicked(permalink) + handleRoomLinkClicked(permalink) } is PermalinkData.FallbackLink, is PermalinkData.RoomEmailInviteLink -> { @@ -134,6 +138,20 @@ class MessagesNode @AssistedInject constructor( } } + private fun handleRoomLinkClicked(roomLink: PermalinkData.RoomLink) { + if (room.isSameRoom(roomLink.roomIdOrAlias)) { + if (roomLink.eventId != null) { + // TODO Handle navigation to the Event + context.toast("TODO Handle navigation to the Event ${roomLink.eventId}") + } else { + // Click on the same room, ignore + context.toast("Already viewing this room!") + } + } else { + callback?.onPermalinkClicked(roomLink) + } + } + override fun onShowEventDebugInfoClicked(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { callback?.onShowEventDebugInfoClicked(eventId, debugInfo) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/navigation/Navigation.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/navigation/Navigation.kt new file mode 100644 index 0000000000..3bc46df4fc --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/navigation/Navigation.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 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.libraries.matrix.api.room.navigation + +import io.element.android.libraries.matrix.api.core.RoomIdOrAlias +import io.element.android.libraries.matrix.api.room.MatrixRoom + +/** + * Return true if the given roomIdOrAlias is the same room as this room. + */ +fun MatrixRoom.isSameRoom(roomIdOrAlias: RoomIdOrAlias): Boolean { + return when (roomIdOrAlias) { + is RoomIdOrAlias.Id -> { + roomIdOrAlias.roomId == roomId + } + is RoomIdOrAlias.Alias -> { + roomIdOrAlias.roomAlias == alias || roomIdOrAlias.roomAlias in alternativeAliases + } + } +} From 5e28bfef33ea97f3ce7b84ea57326f9148bab288 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 10:59:11 +0200 Subject: [PATCH 14/46] Add name to call arguments. --- .../impl/room/member/RoomMemberMapper.kt | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/member/RoomMemberMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/member/RoomMemberMapper.kt index 492fdee814..32d19a3afb 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/member/RoomMemberMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/member/RoomMemberMapper.kt @@ -25,16 +25,16 @@ import org.matrix.rustcomponents.sdk.RoomMember as RustRoomMember object RoomMemberMapper { fun map(roomMember: RustRoomMember): RoomMember = RoomMember( - UserId(roomMember.userId), - roomMember.displayName, - roomMember.avatarUrl, - mapMembership(roomMember.membership), - roomMember.isNameAmbiguous, - roomMember.powerLevel, - roomMember.normalizedPowerLevel, - roomMember.isIgnored, - mapRole(roomMember.suggestedRoleForPowerLevel), - ) + userId = UserId(roomMember.userId), + displayName = roomMember.displayName, + avatarUrl = roomMember.avatarUrl, + membership = mapMembership(roomMember.membership), + isNameAmbiguous = roomMember.isNameAmbiguous, + powerLevel = roomMember.powerLevel, + normalizedPowerLevel = roomMember.normalizedPowerLevel, + isIgnored = roomMember.isIgnored, + role = mapRole(roomMember.suggestedRoleForPowerLevel), + ) fun mapRole(role: RoomMemberRole): RoomMember.Role = when (role) { From 57d5ffa97a265dd8ef3a5ee6b396eea702cdf737 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 16:22:50 +0200 Subject: [PATCH 15/46] Update extension. --- .../io/element/android/features/messages/impl/MessagesNode.kt | 4 ++-- .../{navigation/Navigation.kt => alias/MatrixRoomAlias.kt} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/{navigation/Navigation.kt => alias/MatrixRoomAlias.kt} (89%) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index 658149489c..357e68b3d2 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -45,7 +45,7 @@ import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkParser import io.element.android.libraries.matrix.api.room.MatrixRoom -import io.element.android.libraries.matrix.api.room.navigation.isSameRoom +import io.element.android.libraries.matrix.api.room.alias.matches import io.element.android.libraries.matrix.api.room.roomMembers import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.mediaplayer.api.MediaPlayer @@ -139,7 +139,7 @@ class MessagesNode @AssistedInject constructor( } private fun handleRoomLinkClicked(roomLink: PermalinkData.RoomLink) { - if (room.isSameRoom(roomLink.roomIdOrAlias)) { + if (room.matches(roomLink.roomIdOrAlias)) { if (roomLink.eventId != null) { // TODO Handle navigation to the Event context.toast("TODO Handle navigation to the Event ${roomLink.eventId}") diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/navigation/Navigation.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/alias/MatrixRoomAlias.kt similarity index 89% rename from libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/navigation/Navigation.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/alias/MatrixRoomAlias.kt index 3bc46df4fc..6fb2246d1a 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/navigation/Navigation.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/alias/MatrixRoomAlias.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.api.room.navigation +package io.element.android.libraries.matrix.api.room.alias import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.room.MatrixRoom @@ -22,7 +22,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom /** * Return true if the given roomIdOrAlias is the same room as this room. */ -fun MatrixRoom.isSameRoom(roomIdOrAlias: RoomIdOrAlias): Boolean { +fun MatrixRoom.matches(roomIdOrAlias: RoomIdOrAlias): Boolean { return when (roomIdOrAlias) { is RoomIdOrAlias.Id -> { roomIdOrAlias.roomId == roomId From 9950d3c0faa9d5e3c926d9471223d4dd26f33dd1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 16:44:37 +0200 Subject: [PATCH 16/46] Comment out SDK usage. --- .../libraries/matrix/impl/RustMatrixClient.kt | 10 ++-- .../impl/room/preview/RoomPreviewMapper.kt | 47 ++++++++++--------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 48683d77ba..82619352c0 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -61,7 +61,7 @@ import io.element.android.libraries.matrix.impl.room.RoomContentForwarder import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber import io.element.android.libraries.matrix.impl.room.RustMatrixRoom import io.element.android.libraries.matrix.impl.room.map -import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewMapper +// TODO import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewMapper import io.element.android.libraries.matrix.impl.roomdirectory.RustRoomDirectoryService import io.element.android.libraries.matrix.impl.roomlist.RoomListFactory import io.element.android.libraries.matrix.impl.roomlist.RustRoomListService @@ -466,13 +466,17 @@ class RustMatrixClient( override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result = withContext(sessionDispatcher) { runCatching { - client.resolveRoomAlias(roomAlias.value).let(::RoomId) + // TODO Waiting for SDK to be released + throw Exception("Not implemented") + // client.resolveRoomAlias(roomAlias.value).let(::RoomId) } } override suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias): Result = withContext(sessionDispatcher) { runCatching { - client.getRoomPreview(roomIdOrAlias.identifier).let(RoomPreviewMapper::map) + // TODO Waiting for SDK to be released + throw Exception("Not implemented") + // client.getRoomPreview(roomIdOrAlias.identifier).let(RoomPreviewMapper::map) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/preview/RoomPreviewMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/preview/RoomPreviewMapper.kt index 75286becda..0497d8aeff 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/preview/RoomPreviewMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/preview/RoomPreviewMapper.kt @@ -16,26 +16,27 @@ package io.element.android.libraries.matrix.impl.room.preview -import io.element.android.libraries.matrix.api.core.RoomAlias -import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.room.preview.RoomPreview -import org.matrix.rustcomponents.sdk.RoomPreview as RustRoomPreview - -object RoomPreviewMapper { - fun map(roomPreview: RustRoomPreview): RoomPreview { - return RoomPreview( - roomId = RoomId(roomPreview.roomId), - canonicalAlias = roomPreview.canonicalAlias?.let(::RoomAlias), - name = roomPreview.name, - topic = roomPreview.topic, - avatarUrl = roomPreview.avatarUrl, - numberOfJoinedMembers = roomPreview.numJoinedMembers.toLong(), - roomType = roomPreview.roomType, - isHistoryWorldReadable = roomPreview.isHistoryWorldReadable, - isJoined = roomPreview.isJoined, - isInvited = roomPreview.isInvited, - isPublic = roomPreview.isPublic, - canKnock = roomPreview.canKnock - ) - } -} +// TODO Restore +// import io.element.android.libraries.matrix.api.core.RoomAlias +// import io.element.android.libraries.matrix.api.core.RoomId +// import io.element.android.libraries.matrix.api.room.preview.RoomPreview +// import org.matrix.rustcomponents.sdk.RoomPreview as RustRoomPreview +// +// object RoomPreviewMapper { +// fun map(roomPreview: RustRoomPreview): RoomPreview { +// return RoomPreview( +// roomId = RoomId(roomPreview.roomId), +// canonicalAlias = roomPreview.canonicalAlias?.let(::RoomAlias), +// name = roomPreview.name, +// topic = roomPreview.topic, +// avatarUrl = roomPreview.avatarUrl, +// numberOfJoinedMembers = roomPreview.numJoinedMembers.toLong(), +// roomType = roomPreview.roomType, +// isHistoryWorldReadable = roomPreview.isHistoryWorldReadable, +// isJoined = roomPreview.isJoined, +// isInvited = roomPreview.isInvited, +// isPublic = roomPreview.isPublic, +// canKnock = roomPreview.canKnock +// ) +// } +// } From b6c7a2fbe3601582c2987675b919359b7b5ad471 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 16:51:38 +0200 Subject: [PATCH 17/46] Open any user permalink. --- .../android/features/messages/impl/MessagesNode.kt | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index 357e68b3d2..37292f5e08 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -46,7 +46,6 @@ import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkParser import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.alias.matches -import io.element.android.libraries.matrix.api.room.roomMembers import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.mediaplayer.api.MediaPlayer import io.element.android.services.analytics.api.AnalyticsService @@ -120,13 +119,9 @@ class MessagesNode @AssistedInject constructor( ) { when (val permalink = permalinkParser.parse(url)) { is PermalinkData.UserLink -> { - if (permalink.userId in room.membersStateFlow.value.roomMembers().orEmpty().map { it.userId }) { - // Open the room member profile - callback?.onUserDataClicked(permalink.userId) - } else { - // The user is not a member of the room - callback?.onPermalinkClicked(permalink) - } + // Open the room member profile, it will fallback to + // the user profile if the user is not in the room + callback?.onUserDataClicked(permalink.userId) } is PermalinkData.RoomLink -> { handleRoomLinkClicked(permalink) From 579c90e2fb0b773f10a8e5440bf89bd928240291 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 17:40:03 +0200 Subject: [PATCH 18/46] Add test on RoomAliasResolverPresenter --- .../RoomAliasResolverPresenterTest.kt | 94 +++++++++++++++++++ .../libraries/matrix/test/FakeMatrixClient.kt | 2 +- .../android/libraries/matrix/test/TestData.kt | 2 + 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 appnav/src/test/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverPresenterTest.kt diff --git a/appnav/src/test/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverPresenterTest.kt new file mode 100644 index 0000000000..79e44cd627 --- /dev/null +++ b/appnav/src/test/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverPresenterTest.kt @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2024 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.appnav.room.resolver + +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.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomAlias +import io.element.android.libraries.matrix.test.AN_EXCEPTION +import io.element.android.libraries.matrix.test.A_ROOM_ALIAS +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.tests.testutils.WarmUpRule +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test + +class RoomAliasResolverPresenterTest { + @get:Rule + val warmUpRule = WarmUpRule() + + @Test + fun `present - initial state`() = runTest { + val presenter = createPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + assertThat(awaitItem().resolveState.isUninitialized()).isTrue() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - resolve alias to roomId`() = runTest { + val client = FakeMatrixClient( + resolveRoomAliasResult = { Result.success(A_ROOM_ID) } + ) + val presenter = createPresenter(matrixClient = client) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + assertThat(awaitItem().resolveState.isUninitialized()).isTrue() + assertThat(awaitItem().resolveState.isLoading()).isTrue() + val resultState = awaitItem() + assertThat(resultState.roomAlias).isEqualTo(A_ROOM_ALIAS) + assertThat(resultState.resolveState.dataOrNull()).isEqualTo(A_ROOM_ID) + } + } + + @Test + fun `present - resolve alias error and retry`() = runTest { + val client = FakeMatrixClient( + resolveRoomAliasResult = { Result.failure(AN_EXCEPTION) } + ) + val presenter = createPresenter(matrixClient = client) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + assertThat(awaitItem().resolveState.isUninitialized()).isTrue() + assertThat(awaitItem().resolveState.isLoading()).isTrue() + val resultState = awaitItem() + assertThat(resultState.resolveState.errorOrNull()).isEqualTo(AN_EXCEPTION) + resultState.eventSink(RoomAliasResolverEvents.Retry) + val retryLoadingState = awaitItem() + assertThat(retryLoadingState.resolveState.isLoading()).isTrue() + val retryState = awaitItem() + assertThat(retryState.resolveState.errorOrNull()).isEqualTo(AN_EXCEPTION) + } + } + + private fun createPresenter( + roomAlias: RoomAlias = A_ROOM_ALIAS, + matrixClient: MatrixClient = FakeMatrixClient(), + ) = RoomAliasResolverPresenter( + roomAlias = roomAlias, + matrixClient = matrixClient, + ) +} diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index 42557db1dd..f53bea0fe7 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -281,7 +281,7 @@ class FakeMatrixClient( return Result.success(Unit) } - override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result { + override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result = simulateLongTask { return resolveRoomAliasResult(roomAlias) } 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 ca55e6d514..96346574ce 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 @@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.test import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomAlias 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.SpaceId @@ -50,6 +51,7 @@ val A_THREAD_ID = ThreadId("\$aThreadId") val A_THREAD_ID_2 = ThreadId("\$aThreadId2") val AN_EVENT_ID = EventId("\$anEventId") val AN_EVENT_ID_2 = EventId("\$anEventId2") +val A_ROOM_ALIAS = RoomAlias("#alias1:domain") val A_TRANSACTION_ID = TransactionId("aTransactionId") const val A_UNIQUE_ID = "aUniqueId" From 50f4a1a5a5f262795dea7198e3bd6d68f460e5c2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 17:41:25 +0200 Subject: [PATCH 19/46] Format file. --- .../element/android/features/joinroom/impl/JoinRoomView.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt index 74c78d09a5..a22b114d08 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt @@ -228,7 +228,6 @@ private fun JoinRoomContent( }, ) } - } } @@ -301,9 +300,9 @@ private fun Description(description: String, modifier: Modifier = Modifier) { private fun MembersCount(memberCount: Long) { Row( modifier = Modifier - .background(color = ElementTheme.colors.bgSubtleSecondary, shape = CircleShape) - .widthIn(min = 48.dp) - .padding(all = 2.dp), + .background(color = ElementTheme.colors.bgSubtleSecondary, shape = CircleShape) + .widthIn(min = 48.dp) + .padding(all = 2.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp) ) { From 2a256f4a7a6afa83bb1e7c150650ef38c12aae89 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 17:58:12 +0200 Subject: [PATCH 20/46] Fix JoinRoomPresenterTest --- .../android/features/joinroom/impl/JoinRoomPresenterTest.kt | 1 + .../element/android/libraries/matrix/test/FakeMatrixClient.kt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt index 543a8ad9d7..9488e2f142 100644 --- a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt +++ b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt @@ -54,6 +54,7 @@ class JoinRoomPresenterTest { assertThat(state.contentState).isEqualTo(ContentState.Loading(A_ROOM_ID.toRoomIdOrAlias())) assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.Unknown) assertThat(state.acceptDeclineInviteState).isEqualTo(anAcceptDeclineInviteState()) + cancelAndIgnoreRemainingEvents() } } } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index f53bea0fe7..2e24139042 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -77,7 +77,7 @@ class FakeMatrixClient( private val roomDirectoryService: RoomDirectoryService = FakeRoomDirectoryService(), private val accountManagementUrlString: Result = Result.success(null), private val resolveRoomAliasResult: (RoomAlias) -> Result = { Result.success(A_ROOM_ID) }, - private val getRoomPreviewResult: (RoomIdOrAlias) -> Result = { TODO("Not implemented") }, + private val getRoomPreviewResult: (RoomIdOrAlias) -> Result = { Result.failure(AN_EXCEPTION) }, ) : MatrixClient { var setDisplayNameCalled: Boolean = false private set From 1ed3e0c36516692c9d5b8120b29e6c0fdb2825fc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 18:06:56 +0200 Subject: [PATCH 21/46] Add test to JoinRoomPresenter to cover RoomPreview --- .../joinroom/impl/JoinRoomPresenterTest.kt | 106 ++++++++++++++++++ .../libraries/matrix/test/FakeMatrixClient.kt | 6 +- 2 files changed, 109 insertions(+), 3 deletions(-) diff --git a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt index 9488e2f142..539600d9db 100644 --- a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt +++ b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt @@ -27,6 +27,8 @@ import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.room.CurrentUserMembership +import io.element.android.libraries.matrix.api.room.preview.RoomPreview +import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_NAME import io.element.android.libraries.matrix.test.FakeMatrixClient @@ -240,6 +242,110 @@ class JoinRoomPresenterTest { } } + @Test + fun `present - when room is not known RoomPreview is loaded`() = runTest { + val client = FakeMatrixClient( + getRoomPreviewResult = { + Result.success( + RoomPreview( + roomId = A_ROOM_ID, + canonicalAlias = RoomAlias("#alias:matrix.org"), + name = "Room name", + topic = "Room topic", + avatarUrl = "avatarUrl", + numberOfJoinedMembers = 2, + roomType = null, + isHistoryWorldReadable = false, + isJoined = false, + isInvited = false, + isPublic = true, + canKnock = false, + ) + ) + } + ) + val presenter = createJoinRoomPresenter( + matrixClient = client + ) + presenter.test { + skipItems(1) + awaitItem().also { state -> + assertThat(state.contentState).isEqualTo( + ContentState.Loaded( + roomId = A_ROOM_ID, + name = "Room name", + topic = "Room topic", + alias = RoomAlias("#alias:matrix.org"), + numberOfMembers = 2, + isDirect = false, + roomAvatarUrl = "avatarUrl", + joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin + ) + ) + } + } + } + + @Test + fun `present - when room is not known RoomPreview is loaded with error`() = runTest { + val client = FakeMatrixClient( + getRoomPreviewResult = { + Result.failure(AN_EXCEPTION) + } + ) + val presenter = createJoinRoomPresenter( + matrixClient = client + ) + presenter.test { + skipItems(1) + awaitItem().also { state -> + assertThat(state.contentState).isEqualTo( + ContentState.Failure( + roomIdOrAlias = A_ROOM_ID.toRoomIdOrAlias(), + error = AN_EXCEPTION + ) + ) + state.eventSink(JoinRoomEvents.Retry) + } + skipItems(1) + awaitItem().also { state -> + assertThat(state.contentState).isEqualTo( + ContentState.Loading(A_ROOM_ID.toRoomIdOrAlias()) + ) + } + awaitItem().also { state -> + assertThat(state.contentState).isEqualTo( + ContentState.Failure( + roomIdOrAlias = A_ROOM_ID.toRoomIdOrAlias(), + error = AN_EXCEPTION + ) + ) + } + } + } + + @Test + fun `present - when room is not known RoomPreview is loaded with error 403`() = runTest { + val client = FakeMatrixClient( + getRoomPreviewResult = { + Result.failure(Exception("403")) + } + ) + val presenter = createJoinRoomPresenter( + matrixClient = client + ) + presenter.test { + skipItems(1) + awaitItem().also { state -> + assertThat(state.contentState).isEqualTo( + ContentState.UnknownRoom( + roomIdOrAlias = A_ROOM_ID.toRoomIdOrAlias(), + ) + ) + } + } + } + private fun createJoinRoomPresenter( roomId: RoomId = A_ROOM_ID, roomDescription: Optional = Optional.empty(), diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index 2e24139042..e43d7655f0 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -282,11 +282,11 @@ class FakeMatrixClient( } override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result = simulateLongTask { - return resolveRoomAliasResult(roomAlias) + resolveRoomAliasResult(roomAlias) } - override suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias): Result { - return getRoomPreviewResult(roomIdOrAlias) + override suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias): Result = simulateLongTask { + getRoomPreviewResult(roomIdOrAlias) } override suspend fun getRecentlyVisitedRooms(): Result> { From 0310b5df0f253a37cd1d3fae464ed58aeb4b60de Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 23:01:37 +0200 Subject: [PATCH 22/46] Move RoomAliasResolver classes to their own module. --- .../android/appnav/room/RoomFlowNode.kt | 12 +++-- .../roomaliasresolver/api/build.gradle.kts | 28 ++++++++++ .../api/RoomAliasResolverEntryPoint.kt | 43 +++++++++++++++ .../roomaliasresolver/impl/build.gradle.kts | 53 +++++++++++++++++++ .../DefaultRoomAliasResolverEntryPoint.kt | 49 +++++++++++++++++ .../impl}/RoomAliasResolverEvents.kt | 2 +- .../impl}/RoomAliasResolverNode.kt | 17 ++---- .../impl}/RoomAliasResolverPresenter.kt | 2 +- .../impl}/RoomAliasResolverState.kt | 2 +- .../impl}/RoomAliasResolverStateProvider.kt | 2 +- .../impl}/RoomAliasResolverView.kt | 2 +- .../impl/di/RoomAliasResolverModule.kt | 5 +- .../impl}/RoomAliasResolverPresenterTest.kt | 2 +- 13 files changed, 194 insertions(+), 25 deletions(-) create mode 100644 features/roomaliasresolver/api/build.gradle.kts create mode 100644 features/roomaliasresolver/api/src/main/kotlin/io/element/android/features/roomaliasesolver/api/RoomAliasResolverEntryPoint.kt create mode 100644 features/roomaliasresolver/impl/build.gradle.kts create mode 100644 features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPoint.kt rename {appnav/src/main/kotlin/io/element/android/appnav/room/resolver => features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl}/RoomAliasResolverEvents.kt (92%) rename {appnav/src/main/kotlin/io/element/android/appnav/room/resolver => features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl}/RoomAliasResolverNode.kt (80%) rename {appnav/src/main/kotlin/io/element/android/appnav/room/resolver => features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl}/RoomAliasResolverPresenter.kt (97%) rename {appnav/src/main/kotlin/io/element/android/appnav/room/resolver => features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl}/RoomAliasResolverState.kt (94%) rename {appnav/src/main/kotlin/io/element/android/appnav/room/resolver => features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl}/RoomAliasResolverStateProvider.kt (96%) rename {appnav/src/main/kotlin/io/element/android/appnav/room/resolver => features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl}/RoomAliasResolverView.kt (99%) rename appnav/src/main/kotlin/io/element/android/appnav/room/resolver/ResolveRoomModule.kt => features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/di/RoomAliasResolverModule.kt (88%) rename {appnav/src/test/kotlin/io/element/android/appnav/room/resolver => features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl}/RoomAliasResolverPresenterTest.kt (98%) 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 d0fc830892..932e36811b 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 @@ -36,8 +36,8 @@ import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.appnav.room.joined.JoinedRoomFlowNode import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode -import io.element.android.appnav.room.resolver.RoomAliasResolverNode import io.element.android.features.joinroom.api.JoinRoomEntryPoint +import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint import io.element.android.features.roomdirectory.api.RoomDescription import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode @@ -68,6 +68,7 @@ class RoomFlowNode @AssistedInject constructor( private val client: MatrixClient, private val roomMembershipObserver: RoomMembershipObserver, private val joinRoomEntryPoint: JoinRoomEntryPoint, + private val roomAliasResolverEntryPoint: RoomAliasResolverEntryPoint, ) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Loading, @@ -144,13 +145,16 @@ class RoomFlowNode @AssistedInject constructor( return when (navTarget) { is NavTarget.Loading -> loadingNode(buildContext) is NavTarget.Resolving -> { - val callback = object : RoomAliasResolverNode.Callback { + val callback = object : RoomAliasResolverEntryPoint.Callback { override fun onAliasResolved(roomId: RoomId) { backstack.newRoot(NavTarget.JoinRoom(roomId)) } } - val params = RoomAliasResolverNode.Inputs(navTarget.roomAlias) - createNode(buildContext, listOf(callback, params)) + val params = RoomAliasResolverEntryPoint.Params(navTarget.roomAlias) + roomAliasResolverEntryPoint.nodeBuilder(this, buildContext) + .callback(callback) + .params(params) + .build() } is NavTarget.JoinRoom -> { val inputs = JoinRoomEntryPoint.Inputs( diff --git a/features/roomaliasresolver/api/build.gradle.kts b/features/roomaliasresolver/api/build.gradle.kts new file mode 100644 index 0000000000..631c9e2dbf --- /dev/null +++ b/features/roomaliasresolver/api/build.gradle.kts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 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. + */ + +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.features.roomaliasresolver.api" +} + +dependencies { + implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) +} diff --git a/features/roomaliasresolver/api/src/main/kotlin/io/element/android/features/roomaliasesolver/api/RoomAliasResolverEntryPoint.kt b/features/roomaliasresolver/api/src/main/kotlin/io/element/android/features/roomaliasesolver/api/RoomAliasResolverEntryPoint.kt new file mode 100644 index 0000000000..8c0cc10f64 --- /dev/null +++ b/features/roomaliasresolver/api/src/main/kotlin/io/element/android/features/roomaliasesolver/api/RoomAliasResolverEntryPoint.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024 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.roomaliasesolver.api + +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.architecture.NodeInputs +import io.element.android.libraries.matrix.api.core.RoomAlias +import io.element.android.libraries.matrix.api.core.RoomId + +interface RoomAliasResolverEntryPoint : FeatureEntryPoint { + fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder + + interface NodeBuilder { + fun callback(callback: Callback): NodeBuilder + fun params(params: Params): NodeBuilder + fun build(): Node + } + + interface Callback : Plugin { + fun onAliasResolved(roomId: RoomId) + } + + data class Params( + val roomAlias: RoomAlias + ) : NodeInputs +} diff --git a/features/roomaliasresolver/impl/build.gradle.kts b/features/roomaliasresolver/impl/build.gradle.kts new file mode 100644 index 0000000000..eaf2773231 --- /dev/null +++ b/features/roomaliasresolver/impl/build.gradle.kts @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024 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. + */ + +plugins { + id("io.element.android-compose-library") + alias(libs.plugins.anvil) + alias(libs.plugins.ksp) + id("kotlin-parcelize") +} + +android { + namespace = "io.element.android.features.roomaliasresolver.impl" +} + +anvil { + generateDaggerFactories.set(true) +} + +dependencies { + implementation(projects.anvilannotations) + anvil(projects.anvilcodegen) + api(projects.features.roomaliasresolver.api) + implementation(projects.libraries.core) + implementation(projects.libraries.architecture) + implementation(projects.libraries.androidutils) + implementation(projects.libraries.matrix.api) + implementation(projects.libraries.matrixui) + implementation(projects.libraries.designsystem) + implementation(projects.libraries.uiStrings) + + testImplementation(libs.test.junit) + testImplementation(libs.coroutines.test) + testImplementation(libs.molecule.runtime) + testImplementation(libs.test.truth) + testImplementation(libs.test.turbine) + testImplementation(projects.libraries.matrix.test) + testImplementation(projects.tests.testutils) + + ksp(libs.showkase.processor) +} diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPoint.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPoint.kt new file mode 100644 index 0000000000..46bb6cf6c1 --- /dev/null +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPoint.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 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.roomaliasresolver.impl + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint +import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.di.AppScope +import javax.inject.Inject + +@ContributesBinding(AppScope::class) +class DefaultRoomAliasResolverEntryPoint @Inject constructor() : RoomAliasResolverEntryPoint { + override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): RoomAliasResolverEntryPoint.NodeBuilder { + val plugins = ArrayList() + + return object : RoomAliasResolverEntryPoint.NodeBuilder { + override fun callback(callback: RoomAliasResolverEntryPoint.Callback): RoomAliasResolverEntryPoint.NodeBuilder { + plugins += callback + return this + } + + override fun params(params: RoomAliasResolverEntryPoint.Params): RoomAliasResolverEntryPoint.NodeBuilder { + plugins += params + return this + } + + override fun build(): Node { + return parentNode.createNode(buildContext, plugins) + } + } + } +} diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverEvents.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverEvents.kt similarity index 92% rename from appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverEvents.kt rename to features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverEvents.kt index 1eb3c8ffe9..60ac90d762 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverEvents.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverEvents.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.appnav.room.resolver +package io.element.android.features.roomaliasresolver.impl sealed interface RoomAliasResolverEvents { data object Retry : RoomAliasResolverEvents diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverNode.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt similarity index 80% rename from appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverNode.kt rename to features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt index c429ec1a95..a4dbcc40b5 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverNode.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.appnav.room.resolver +package io.element.android.features.roomaliasresolver.impl import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -25,10 +25,9 @@ import com.bumble.appyx.core.plugin.plugins import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode -import io.element.android.libraries.architecture.NodeInputs +import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId @ContributesNode(SessionScope::class) @@ -37,22 +36,14 @@ class RoomAliasResolverNode @AssistedInject constructor( @Assisted plugins: List, presenterFactory: RoomAliasResolverPresenter.Factory, ) : Node(buildContext, plugins = plugins) { - data class Inputs( - val roomAlias: RoomAlias - ) : NodeInputs - - private val inputs = inputs() + private val inputs = inputs() private val presenter = presenterFactory.create( inputs.roomAlias ) - interface Callback : Plugin { - fun onAliasResolved(roomId: RoomId) - } - private fun onAliasResolved(roomId: RoomId) { - plugins().forEach { it.onAliasResolved(roomId) } + plugins().forEach { it.onAliasResolved(roomId) } } @Composable diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverPresenter.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt similarity index 97% rename from appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverPresenter.kt rename to features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt index 5e9e3382d5..775be7d6ff 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverPresenter.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenter.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.appnav.room.resolver +package io.element.android.features.roomaliasresolver.impl import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverState.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverState.kt similarity index 94% rename from appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverState.kt rename to features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverState.kt index 4f800a76cd..638214da3f 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverState.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverState.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.appnav.room.resolver +package io.element.android.features.roomaliasresolver.impl import androidx.compose.runtime.Immutable import io.element.android.libraries.architecture.AsyncData diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverStateProvider.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverStateProvider.kt similarity index 96% rename from appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverStateProvider.kt rename to features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverStateProvider.kt index 9584bb21b8..3c5599628c 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverStateProvider.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverStateProvider.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.appnav.room.resolver +package io.element.android.features.roomaliasresolver.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncData diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverView.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt similarity index 99% rename from appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverView.kt rename to features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt index e208119a16..fa61012ec0 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverView.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.appnav.room.resolver +package io.element.android.features.roomaliasresolver.impl import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/ResolveRoomModule.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/di/RoomAliasResolverModule.kt similarity index 88% rename from appnav/src/main/kotlin/io/element/android/appnav/room/resolver/ResolveRoomModule.kt rename to features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/di/RoomAliasResolverModule.kt index 687fefc5c5..538cc57f32 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/resolver/ResolveRoomModule.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/di/RoomAliasResolverModule.kt @@ -14,18 +14,19 @@ * limitations under the License. */ -package io.element.android.appnav.room.resolver +package io.element.android.features.roomaliasresolver.impl.di import com.squareup.anvil.annotations.ContributesTo import dagger.Module import dagger.Provides +import io.element.android.features.roomaliasresolver.impl.RoomAliasResolverPresenter import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomAlias @Module @ContributesTo(SessionScope::class) -object ResolveRoomModule { +object RoomAliasResolverModule { @Provides fun providesJoinRoomPresenterFactory( client: MatrixClient, diff --git a/appnav/src/test/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverPresenterTest.kt b/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenterTest.kt similarity index 98% rename from appnav/src/test/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverPresenterTest.kt rename to features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenterTest.kt index 79e44cd627..2c64690600 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/room/resolver/RoomAliasResolverPresenterTest.kt +++ b/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverPresenterTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.appnav.room.resolver +package io.element.android.features.roomaliasresolver.impl import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow From 1435f8d9f378c963416d789382ab054c02184eeb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 23:06:54 +0200 Subject: [PATCH 23/46] Rename `eventId` to `focusedEventId` for clarity. --- .../element/android/appnav/room/RoomNavigationTarget.kt | 2 +- .../appnav/room/joined/JoinedRoomLoadedFlowNode.kt | 6 +++--- .../android/features/messages/api/MessagesEntryPoint.kt | 2 +- .../features/messages/impl/DefaultMessagesEntryPoint.kt | 2 +- .../android/features/messages/impl/MessagesFlowNode.kt | 8 ++++---- .../android/features/messages/impl/MessagesNode.kt | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomNavigationTarget.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomNavigationTarget.kt index 776171d141..c8d8cf9030 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomNavigationTarget.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomNavigationTarget.kt @@ -22,7 +22,7 @@ import kotlinx.parcelize.Parcelize sealed interface RoomNavigationTarget : Parcelable { @Parcelize - data class Messages(val eventId: EventId? = null) : RoomNavigationTarget + data class Messages(val focusedEventId: EventId? = null) : RoomNavigationTarget @Parcelize data object Details : RoomNavigationTarget 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 7a6c736385..5f9a6b6eb3 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 @@ -66,7 +66,7 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor( ) : BaseFlowNode( backstack = BackStack( initialElement = when (val input = plugins.filterIsInstance(Inputs::class.java).first().initialElement) { - is RoomNavigationTarget.Messages -> NavTarget.Messages(input.eventId) + is RoomNavigationTarget.Messages -> NavTarget.Messages(input.focusedEventId) RoomNavigationTarget.Details -> NavTarget.RoomDetails RoomNavigationTarget.NotificationSettings -> NavTarget.RoomNotificationSettings }, @@ -161,7 +161,7 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor( } } messagesEntryPoint.nodeBuilder(this, buildContext) - .params(MessagesEntryPoint.Params(navTarget.eventId)) + .params(MessagesEntryPoint.Params(navTarget.focusedEventId)) .callback(callback) .build() } @@ -179,7 +179,7 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor( sealed interface NavTarget : Parcelable { @Parcelize - data class Messages(val eventId: EventId? = null) : NavTarget + data class Messages(val focusedEventId: EventId? = null) : NavTarget @Parcelize data object RoomDetails : NavTarget diff --git a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt index 9012d3a776..15d6e5fd95 100644 --- a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt +++ b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt @@ -35,7 +35,7 @@ interface MessagesEntryPoint : FeatureEntryPoint { } data class Params( - val eventId: EventId?, + val focusedEventId: EventId?, ) interface Callback : Plugin { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPoint.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPoint.kt index b45c7fc6b5..73ab29bfeb 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPoint.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPoint.kt @@ -32,7 +32,7 @@ class DefaultMessagesEntryPoint @Inject constructor() : MessagesEntryPoint { return object : MessagesEntryPoint.NodeBuilder { override fun params(params: MessagesEntryPoint.Params): MessagesEntryPoint.NodeBuilder { - plugins += MessagesNode.Inputs(eventId = params.eventId) + plugins += MessagesNode.Inputs(focusedEventId = params.focusedEventId) return this } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index c8a0a753c4..f92214b679 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -81,7 +81,7 @@ class MessagesFlowNode @AssistedInject constructor( private val createPollEntryPoint: CreatePollEntryPoint, ) : BaseFlowNode( backstack = BackStack( - initialElement = NavTarget.Messages(plugins.filterIsInstance().firstOrNull()?.eventId), + initialElement = NavTarget.Messages(plugins.filterIsInstance().firstOrNull()?.focusedEventId), savedStateMap = buildContext.savedStateMap, ), overlay = Overlay( @@ -90,7 +90,7 @@ class MessagesFlowNode @AssistedInject constructor( buildContext = buildContext, plugins = plugins ) { - data class Inputs(val eventId: EventId?) : NodeInputs + data class Inputs(val focusedEventId: EventId?) : NodeInputs sealed interface NavTarget : Parcelable { @Parcelize @@ -98,7 +98,7 @@ class MessagesFlowNode @AssistedInject constructor( @Parcelize data class Messages( - val eventId: EventId? = null, + val focusedEventId: EventId? = null, ) : NavTarget @Parcelize @@ -192,7 +192,7 @@ class MessagesFlowNode @AssistedInject constructor( } } val params = MessagesNode.Inputs( - eventId = navTarget.eventId, + focusedEventId = navTarget.focusedEventId, ) createNode(buildContext, listOf(callback, params)) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index 37292f5e08..88472956c0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -69,7 +69,7 @@ class MessagesNode @AssistedInject constructor( private val callback = plugins().firstOrNull() // TODO Handle navigation to the Event - data class Inputs(val eventId: EventId?) : NodeInputs + data class Inputs(val focusedEventId: EventId?) : NodeInputs interface Callback : Plugin { fun onRoomDetailsClicked() From 7827e88a766b8f0c5b3c2d6353abec7b9f0b03ad Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 23:04:45 +0200 Subject: [PATCH 24/46] Avoid providing default value. --- .../main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index 960b393266..b4326803ab 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -194,7 +194,7 @@ class LoggedInFlowNode @AssistedInject constructor( data class Room( val roomIdOrAlias: RoomIdOrAlias, val roomDescription: RoomDescription? = null, - val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages(null) + val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages() ) : NavTarget @Parcelize From d67a9da112afc8f2a4e741b84d8815a0b7f46bb2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 23:10:27 +0200 Subject: [PATCH 25/46] Remove extra space. --- .../android/libraries/matrix/api/room/preview/RoomPreview.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/preview/RoomPreview.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/preview/RoomPreview.kt index ad16561380..4dfec3add4 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/preview/RoomPreview.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/preview/RoomPreview.kt @@ -23,7 +23,7 @@ data class RoomPreview( /** The room id for this room. */ val roomId: RoomId, /** The canonical alias for the room. */ - val canonicalAlias : RoomAlias?, + val canonicalAlias: RoomAlias?, /** The room's name, if set. */ val name: String?, /** The room's topic, if set. */ From 70f7bf7e8de72a051255fd922869c58a71d15c3f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 23:13:51 +0200 Subject: [PATCH 26/46] Ignore too generic exceptions (temporary code). --- .../element/android/libraries/matrix/impl/RustMatrixClient.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 82619352c0..d27daaafdd 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -464,6 +464,7 @@ class RustMatrixClient( } } + @Suppress("TooGenericExceptionThrown") override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result = withContext(sessionDispatcher) { runCatching { // TODO Waiting for SDK to be released @@ -472,6 +473,7 @@ class RustMatrixClient( } } + @Suppress("TooGenericExceptionThrown") override suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias): Result = withContext(sessionDispatcher) { runCatching { // TODO Waiting for SDK to be released From 98c285ece80334c302f119b616182c7931787643 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 23:15:00 +0200 Subject: [PATCH 27/46] Rename `JoinRoomEvents.Retry` to `JoinRoomEvents.RetryFetchingContent` --- .../io/element/android/features/joinroom/impl/JoinRoomEvents.kt | 2 +- .../element/android/features/joinroom/impl/JoinRoomPresenter.kt | 2 +- .../io/element/android/features/joinroom/impl/JoinRoomView.kt | 2 +- .../android/features/joinroom/impl/JoinRoomPresenterTest.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomEvents.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomEvents.kt index 2dddb22e13..7163fc2bad 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomEvents.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomEvents.kt @@ -17,7 +17,7 @@ package io.element.android.features.joinroom.impl sealed interface JoinRoomEvents { - data object Retry : JoinRoomEvents + data object RetryFetchingContent : JoinRoomEvents data object JoinRoom : JoinRoomEvents data object AcceptInvite : JoinRoomEvents data object DeclineInvite : JoinRoomEvents 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 504294037f..f7feb43926 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 @@ -109,7 +109,7 @@ class JoinRoomPresenter @AssistedInject constructor( AcceptDeclineInviteEvents.DeclineInvite(inviteData) ) } - JoinRoomEvents.Retry -> { + JoinRoomEvents.RetryFetchingContent -> { retryCount++ } } diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt index a22b114d08..e7b43af679 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt @@ -84,7 +84,7 @@ fun JoinRoomView( state.eventSink(JoinRoomEvents.JoinRoom) }, onRetry = { - state.eventSink(JoinRoomEvents.Retry) + state.eventSink(JoinRoomEvents.RetryFetchingContent) } ) } diff --git a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt index 539600d9db..ff5a1b10b2 100644 --- a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt +++ b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt @@ -305,7 +305,7 @@ class JoinRoomPresenterTest { error = AN_EXCEPTION ) ) - state.eventSink(JoinRoomEvents.Retry) + state.eventSink(JoinRoomEvents.RetryFetchingContent) } skipItems(1) awaitItem().also { state -> From f65896932155ffaf44044ff8ffe2f0b9a04bf30d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 23:19:38 +0200 Subject: [PATCH 28/46] Remove useless usage of coroutine scope. --- .../joinroom/impl/JoinRoomPresenter.kt | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) 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 f7feb43926..93399b0446 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 @@ -23,7 +23,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.produceState import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -39,7 +38,6 @@ import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.preview.RoomPreview -import kotlinx.coroutines.launch import java.util.Optional class JoinRoomPresenter @AssistedInject constructor( @@ -59,7 +57,6 @@ class JoinRoomPresenter @AssistedInject constructor( @Composable override fun present(): JoinRoomState { - val coroutineScope = rememberCoroutineScope() var retryCount by remember { mutableIntStateOf(0) } val roomInfo by matrixClient.getRoomInfoFlow(roomId).collectAsState(initial = Optional.empty()) val contentState by produceState( @@ -67,28 +64,26 @@ class JoinRoomPresenter @AssistedInject constructor( key1 = roomInfo, key2 = retryCount, ) { - value = when { + when { roomInfo.isPresent -> { - roomInfo.get().toContentState() + value = roomInfo.get().toContentState() } roomDescription.isPresent -> { - roomDescription.get().toContentState() + value = roomDescription.get().toContentState() } else -> { - coroutineScope.launch { - val result = matrixClient.getRoomPreview(roomId.toRoomIdOrAlias()) - value = result.fold( - onSuccess = { it.toContentState() }, - onFailure = { throwable -> - if (throwable.message?.contains("403") == true) { - ContentState.UnknownRoom(roomIdOrAlias) - } else { - ContentState.Failure(roomIdOrAlias, throwable) - } + value = ContentState.Loading(roomIdOrAlias) + val result = matrixClient.getRoomPreview(roomId.toRoomIdOrAlias()) + value = result.fold( + onSuccess = { it.toContentState() }, + onFailure = { throwable -> + if (throwable.message?.contains("403") == true) { + ContentState.UnknownRoom(roomIdOrAlias) + } else { + ContentState.Failure(roomIdOrAlias, throwable) } - ) - } - ContentState.Loading(roomIdOrAlias) + } + ) } } } From 7aafec7fb92dc692b938f319844c35927211f056 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 23:21:02 +0200 Subject: [PATCH 29/46] Remove useless launch. --- .../android/appnav/LoggedInFlowNode.kt | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index b4326803ab..83cfd5f1f3 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -274,25 +274,23 @@ class LoggedInFlowNode @AssistedInject constructor( } override fun onPermalinkClicked(data: PermalinkData) { - coroutineScope.launch { - when (data) { - is PermalinkData.UserLink -> { - // FIXME Add a user profile screen. - Timber.e("User link clicked: ${data.userId}. TODO Add a user profile screen") - } - is PermalinkData.RoomLink -> { - backstack.push( - NavTarget.Room( - data.roomIdOrAlias, - initialElement = RoomNavigationTarget.Messages(data.eventId), - // TODO Use the viaParameters - ) + when (data) { + is PermalinkData.UserLink -> { + // FIXME Add a user profile screen. + Timber.e("User link clicked: ${data.userId}. TODO Add a user profile screen") + } + is PermalinkData.RoomLink -> { + backstack.push( + NavTarget.Room( + data.roomIdOrAlias, + initialElement = RoomNavigationTarget.Messages(data.eventId), + // TODO Use the viaParameters ) - } - is PermalinkData.FallbackLink, - is PermalinkData.RoomEmailInviteLink -> { - // Should not happen (handled by MessagesNode) - } + ) + } + is PermalinkData.FallbackLink, + is PermalinkData.RoomEmailInviteLink -> { + // Should not happen (handled by MessagesNode) } } } From 46d7235ac24e2a89e868f4044c27746773674ce1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 23:25:45 +0200 Subject: [PATCH 30/46] Move ContentScaffold to design system and rename to RoomPreviewOrganism --- .../features/joinroom/impl/JoinRoomView.kt | 48 +++------------- .../impl/RoomAliasResolverView.kt | 37 +----------- .../atomic/organisms/RoomPreviewOrganism.kt | 56 +++++++++++++++++++ 3 files changed, 66 insertions(+), 75 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/organisms/RoomPreviewOrganism.kt diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt index e7b43af679..fe022f0a34 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt @@ -18,12 +18,9 @@ package io.element.android.features.joinroom.impl import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.CircleShape @@ -42,6 +39,7 @@ import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom import io.element.android.libraries.designsystem.atomic.molecules.ButtonRowMolecule +import io.element.android.libraries.designsystem.atomic.organisms.RoomPreviewOrganism import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarSize @@ -154,7 +152,7 @@ private fun JoinRoomContent( ) { when (contentState) { is ContentState.Loaded -> { - ContentScaffold( + RoomPreviewOrganism( modifier = modifier, avatar = { Avatar(contentState.avatarData(AvatarSize.RoomHeader)) @@ -176,7 +174,7 @@ private fun JoinRoomContent( ) } is ContentState.UnknownRoom -> { - ContentScaffold( + RoomPreviewOrganism( modifier = modifier, avatar = { PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp) @@ -190,7 +188,7 @@ private fun JoinRoomContent( ) } is ContentState.Loading -> { - ContentScaffold( + RoomPreviewOrganism( modifier = modifier, avatar = { PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp) @@ -204,7 +202,7 @@ private fun JoinRoomContent( ) } is ContentState.Failure -> { - ContentScaffold( + RoomPreviewOrganism( modifier = modifier, avatar = { PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp) @@ -231,36 +229,6 @@ private fun JoinRoomContent( } } -@Composable -private fun ContentScaffold( - avatar: @Composable () -> Unit, - title: @Composable () -> Unit, - subtitle: @Composable () -> Unit, - modifier: Modifier = Modifier, - description: @Composable (() -> Unit)? = null, - memberCount: @Composable (() -> Unit)? = null, -) { - Column( - modifier = modifier.fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally - ) { - avatar() - Spacer(modifier = Modifier.height(16.dp)) - title() - Spacer(modifier = Modifier.height(8.dp)) - subtitle() - Spacer(modifier = Modifier.height(8.dp)) - if (memberCount != null) { - memberCount() - } - Spacer(modifier = Modifier.height(8.dp)) - if (description != null) { - description() - } - Spacer(modifier = Modifier.height(24.dp)) - } -} - @Composable private fun Title(title: String, modifier: Modifier = Modifier) { Text( @@ -300,9 +268,9 @@ private fun Description(description: String, modifier: Modifier = Modifier) { private fun MembersCount(memberCount: Long) { Row( modifier = Modifier - .background(color = ElementTheme.colors.bgSubtleSecondary, shape = CircleShape) - .widthIn(min = 48.dp) - .padding(all = 2.dp), + .background(color = ElementTheme.colors.bgSubtleSecondary, shape = CircleShape) + .widthIn(min = 48.dp) + .padding(all = 2.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp) ) { diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt index fa61012ec0..37ccffde12 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt @@ -17,19 +17,15 @@ package io.element.android.features.roomaliasresolver.impl import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect 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 import androidx.compose.ui.text.style.TextAlign @@ -39,6 +35,7 @@ import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom +import io.element.android.libraries.designsystem.atomic.organisms.RoomPreviewOrganism import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.button.BackButton @@ -116,7 +113,7 @@ private fun RoomAliasResolverContent( state: RoomAliasResolverState, modifier: Modifier = Modifier, ) { - ContentScaffold( + RoomPreviewOrganism( modifier = modifier, avatar = { PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp) @@ -140,36 +137,6 @@ private fun RoomAliasResolverContent( ) } -@Composable -private fun ContentScaffold( - avatar: @Composable () -> Unit, - title: @Composable () -> Unit, - subtitle: @Composable () -> Unit, - modifier: Modifier = Modifier, - description: @Composable (() -> Unit)? = null, - memberCount: @Composable (() -> Unit)? = null, -) { - Column( - modifier = modifier.fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally - ) { - avatar() - Spacer(modifier = Modifier.height(16.dp)) - title() - Spacer(modifier = Modifier.height(8.dp)) - subtitle() - Spacer(modifier = Modifier.height(8.dp)) - if (memberCount != null) { - memberCount() - } - Spacer(modifier = Modifier.height(8.dp)) - if (description != null) { - description() - } - Spacer(modifier = Modifier.height(24.dp)) - } -} - @Composable private fun Title(title: String, modifier: Modifier = Modifier) { Text( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/organisms/RoomPreviewOrganism.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/organisms/RoomPreviewOrganism.kt new file mode 100644 index 0000000000..ede1cff787 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/organisms/RoomPreviewOrganism.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024 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.libraries.designsystem.atomic.organisms + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun RoomPreviewOrganism( + avatar: @Composable () -> Unit, + title: @Composable () -> Unit, + subtitle: @Composable () -> Unit, + modifier: Modifier = Modifier, + description: @Composable (() -> Unit)? = null, + memberCount: @Composable (() -> Unit)? = null, +) { + Column( + modifier = modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + avatar() + Spacer(modifier = Modifier.height(16.dp)) + title() + Spacer(modifier = Modifier.height(8.dp)) + subtitle() + Spacer(modifier = Modifier.height(8.dp)) + if (memberCount != null) { + memberCount() + } + Spacer(modifier = Modifier.height(8.dp)) + if (description != null) { + description() + } + Spacer(modifier = Modifier.height(24.dp)) + } +} From 1a9f6da1bc2bc652dc695cb5ccc800e86734ae2d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 23:32:59 +0200 Subject: [PATCH 31/46] Create Atom and Molecule for RoomPreview screens --- .../features/joinroom/impl/JoinRoomView.kt | 86 +++---------------- .../impl/RoomAliasResolverView.kt | 15 +--- .../atoms/RoomPreviewDescriptionAtom.kt | 37 ++++++++ .../atomic/atoms/RoomPreviewSubtitleAtom.kt | 34 ++++++++ .../atomic/atoms/RoomPreviewTitleAtom.kt | 34 ++++++++ .../RoomPreviewMembersCountMolecule.kt | 55 ++++++++++++ 6 files changed, 173 insertions(+), 88 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoomPreviewDescriptionAtom.kt create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoomPreviewSubtitleAtom.kt create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoomPreviewTitleAtom.kt create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/RoomPreviewMembersCountMolecule.kt diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt index fe022f0a34..e944dc281f 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt @@ -16,30 +16,25 @@ package io.element.android.features.joinroom.impl -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme 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.text.style.TextAlign -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewLightDark 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.atomic.atoms.PlaceholderAtom import io.element.android.libraries.designsystem.atomic.molecules.ButtonRowMolecule +import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewDescriptionAtom +import io.element.android.libraries.designsystem.atomic.molecules.RoomPreviewMembersCountMolecule import io.element.android.libraries.designsystem.atomic.organisms.RoomPreviewOrganism +import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewSubtitleAtom +import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewTitleAtom import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarSize @@ -47,7 +42,6 @@ import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.ButtonSize -import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.OutlinedButton import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar @@ -158,17 +152,17 @@ private fun JoinRoomContent( Avatar(contentState.avatarData(AvatarSize.RoomHeader)) }, title = { - Title(contentState.computedTitle) + RoomPreviewTitleAtom(contentState.computedTitle) }, subtitle = { - Subtitle(contentState.computedSubtitle) + RoomPreviewSubtitleAtom(contentState.computedSubtitle) }, description = { - Description(contentState.topic ?: "") + RoomPreviewDescriptionAtom(contentState.topic ?: "") }, memberCount = { if (contentState.showMemberCount) { - MembersCount(memberCount = contentState.numberOfMembers ?: 0) + RoomPreviewMembersCountMolecule(memberCount = contentState.numberOfMembers ?: 0) } } ) @@ -180,10 +174,10 @@ private fun JoinRoomContent( PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp) }, title = { - Title(stringResource(R.string.screen_join_room_title_no_preview)) + RoomPreviewTitleAtom(stringResource(R.string.screen_join_room_title_no_preview)) }, subtitle = { - Subtitle(stringResource(R.string.screen_join_room_subtitle_no_preview)) + RoomPreviewSubtitleAtom(stringResource(R.string.screen_join_room_subtitle_no_preview)) }, ) } @@ -210,7 +204,7 @@ private fun JoinRoomContent( title = { when (contentState.roomIdOrAlias) { is RoomIdOrAlias.Alias -> { - Title(contentState.roomIdOrAlias.identifier) + RoomPreviewTitleAtom(contentState.roomIdOrAlias.identifier) } is RoomIdOrAlias.Id -> { PlaceholderAtom(width = 200.dp, height = 22.dp) @@ -229,64 +223,6 @@ private fun JoinRoomContent( } } -@Composable -private fun Title(title: String, modifier: Modifier = Modifier) { - Text( - modifier = modifier, - text = title, - style = ElementTheme.typography.fontHeadingMdBold, - textAlign = TextAlign.Center, - color = ElementTheme.colors.textPrimary, - ) -} - -@Composable -private fun Subtitle(subtitle: String, modifier: Modifier = Modifier) { - Text( - modifier = modifier, - text = subtitle, - style = ElementTheme.typography.fontBodyLgRegular, - textAlign = TextAlign.Center, - color = ElementTheme.colors.textSecondary, - ) -} - -@Composable -private fun Description(description: String, modifier: Modifier = Modifier) { - Text( - modifier = modifier, - text = description, - style = ElementTheme.typography.fontBodySmRegular, - textAlign = TextAlign.Center, - color = ElementTheme.colors.textSecondary, - maxLines = 3, - overflow = TextOverflow.Ellipsis, - ) -} - -@Composable -private fun MembersCount(memberCount: Long) { - Row( - modifier = Modifier - .background(color = ElementTheme.colors.bgSubtleSecondary, shape = CircleShape) - .widthIn(min = 48.dp) - .padding(all = 2.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(4.dp) - ) { - Icon( - imageVector = CompoundIcons.UserProfile(), - contentDescription = null, - tint = ElementTheme.colors.iconSecondary, - ) - Text( - text = "$memberCount", - style = ElementTheme.typography.fontBodySmMedium, - color = ElementTheme.colors.textSecondary, - ) - } -} - @OptIn(ExperimentalMaterial3Api::class) @Composable private fun JoinRoomTopBar( diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt index 37ccffde12..e032af66cb 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt @@ -32,9 +32,9 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom +import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewTitleAtom import io.element.android.libraries.designsystem.atomic.organisms.RoomPreviewOrganism import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.components.avatar.AvatarSize @@ -119,9 +119,9 @@ private fun RoomAliasResolverContent( PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp) }, title = { + RoomPreviewTitleAtom(state.roomAlias.value) }, subtitle = { - Title(state.roomAlias.value) }, description = { if (state.resolveState.isFailure()) { @@ -137,17 +137,6 @@ private fun RoomAliasResolverContent( ) } -@Composable -private fun Title(title: String, modifier: Modifier = Modifier) { - Text( - modifier = modifier, - text = title, - style = ElementTheme.typography.fontHeadingMdBold, - textAlign = TextAlign.Center, - color = ElementTheme.colors.textPrimary, - ) -} - @OptIn(ExperimentalMaterial3Api::class) @Composable private fun RoomAliasResolverTopBar( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoomPreviewDescriptionAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoomPreviewDescriptionAtom.kt new file mode 100644 index 0000000000..13bc48ea3a --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoomPreviewDescriptionAtom.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 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.libraries.designsystem.atomic.atoms + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.designsystem.theme.components.Text + +@Composable +fun RoomPreviewDescriptionAtom(description: String, modifier: Modifier = Modifier) { + Text( + modifier = modifier, + text = description, + style = ElementTheme.typography.fontBodySmRegular, + textAlign = TextAlign.Center, + color = ElementTheme.colors.textSecondary, + maxLines = 3, + overflow = TextOverflow.Ellipsis, + ) +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoomPreviewSubtitleAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoomPreviewSubtitleAtom.kt new file mode 100644 index 0000000000..0548eadd17 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoomPreviewSubtitleAtom.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 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.libraries.designsystem.atomic.atoms + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.designsystem.theme.components.Text + +@Composable +fun RoomPreviewSubtitleAtom(subtitle: String, modifier: Modifier = Modifier) { + Text( + modifier = modifier, + text = subtitle, + style = ElementTheme.typography.fontBodyLgRegular, + textAlign = TextAlign.Center, + color = ElementTheme.colors.textSecondary, + ) +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoomPreviewTitleAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoomPreviewTitleAtom.kt new file mode 100644 index 0000000000..81edd42db5 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoomPreviewTitleAtom.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 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.libraries.designsystem.atomic.atoms + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.designsystem.theme.components.Text + +@Composable +fun RoomPreviewTitleAtom(title: String, modifier: Modifier = Modifier) { + Text( + modifier = modifier, + text = title, + style = ElementTheme.typography.fontHeadingMdBold, + textAlign = TextAlign.Center, + color = ElementTheme.colors.textPrimary, + ) +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/RoomPreviewMembersCountMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/RoomPreviewMembersCountMolecule.kt new file mode 100644 index 0000000000..508d637174 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/RoomPreviewMembersCountMolecule.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 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.libraries.designsystem.atomic.molecules + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +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.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.Text + +@Composable +fun RoomPreviewMembersCountMolecule(memberCount: Long) { + Row( + modifier = Modifier + .background(color = ElementTheme.colors.bgSubtleSecondary, shape = CircleShape) + .widthIn(min = 48.dp) + .padding(all = 2.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + Icon( + imageVector = CompoundIcons.UserProfile(), + contentDescription = null, + tint = ElementTheme.colors.iconSecondary, + ) + Text( + text = "$memberCount", + style = ElementTheme.typography.fontBodySmMedium, + color = ElementTheme.colors.textSecondary, + ) + } +} From 44035905fc1278889173a84a08b0e77ef99849e8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 23:53:00 +0200 Subject: [PATCH 32/46] Subscrie to RoomInfoFlow instead of pushing the JoinRoom. The user may be a member of the room, and in this case, it will be more direct. --- .../main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 932e36811b..6d6405c0e8 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 @@ -147,7 +147,7 @@ class RoomFlowNode @AssistedInject constructor( is NavTarget.Resolving -> { val callback = object : RoomAliasResolverEntryPoint.Callback { override fun onAliasResolved(roomId: RoomId) { - backstack.newRoot(NavTarget.JoinRoom(roomId)) + subscribeToRoomInfoFlow(roomId) } } val params = RoomAliasResolverEntryPoint.Params(navTarget.roomAlias) From 8082330bc5c17d753506bdfd1e8a4cdd0ba53df1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Apr 2024 23:59:44 +0200 Subject: [PATCH 33/46] Use string from Localazy. --- .../roomaliasresolver/impl/RoomAliasResolverView.kt | 2 +- .../roomaliasresolver/impl/src/main/res/values/localazy.xml | 4 ++++ tools/localazy/config.json | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 features/roomaliasresolver/impl/src/main/res/values/localazy.xml diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt index e032af66cb..03c47602e4 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt @@ -126,7 +126,7 @@ private fun RoomAliasResolverContent( description = { if (state.resolveState.isFailure()) { Text( - text = "Failed to resolve room alias", + text = stringResource(id = R.string.screen_room_alias_resolver_resolve_alias_failure), textAlign = TextAlign.Center, color = MaterialTheme.colorScheme.error, ) diff --git a/features/roomaliasresolver/impl/src/main/res/values/localazy.xml b/features/roomaliasresolver/impl/src/main/res/values/localazy.xml new file mode 100644 index 0000000000..21d5c17135 --- /dev/null +++ b/features/roomaliasresolver/impl/src/main/res/values/localazy.xml @@ -0,0 +1,4 @@ + + + "Failed to resolve room alias." + diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 15d2f67949..7d546df8ae 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -20,6 +20,12 @@ "screen_signout_.*" ] }, + { + "name" : ":features:roomaliasresolver:impl", + "includeRegex" : [ + "screen_room_alias_resolver_.*" + ] + }, { "name" : ":features:onboarding:impl", "includeRegex" : [ From 164506ebfbe913efd7bc343c8f0801243c4654f0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 18 Apr 2024 00:29:59 +0200 Subject: [PATCH 34/46] Fix compilation issue after rebase: String -> RoomAlias --- .../features/roomlist/impl/components/RoomSummaryRow.kt | 5 +++-- .../features/roomlist/impl/model/RoomListRoomSummary.kt | 3 ++- .../roomlist/impl/model/RoomListRoomSummaryProvider.kt | 5 +++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt index ce27d3b916..92616f460e 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt @@ -65,6 +65,7 @@ import io.element.android.libraries.designsystem.theme.roomListRoomMessage import io.element.android.libraries.designsystem.theme.roomListRoomMessageDate import io.element.android.libraries.designsystem.theme.roomListRoomName import io.element.android.libraries.designsystem.theme.unreadIndicator +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.ui.strings.CommonStrings import timber.log.Timber @@ -198,13 +199,13 @@ private fun NameAndTimestampRow( private fun InviteSubtitle( isDirect: Boolean, inviteSender: InviteSender?, - canonicalAlias: String?, + canonicalAlias: RoomAlias?, modifier: Modifier = Modifier ) { val subtitle = if (isDirect) { inviteSender?.userId?.value } else { - canonicalAlias + canonicalAlias?.value } if (subtitle != null) { Text( diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt index 4f37cd0ae6..741da3adcc 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt @@ -18,6 +18,7 @@ package io.element.android.features.roomlist.impl.model import androidx.compose.runtime.Immutable import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.RoomNotificationMode @@ -27,7 +28,7 @@ data class RoomListRoomSummary( val displayType: RoomSummaryDisplayType, val roomId: RoomId, val name: String, - val canonicalAlias: String?, + val canonicalAlias: RoomAlias?, val numberOfUnreadMessages: Int, val numberOfUnreadMentions: Int, val numberOfUnreadNotifications: Int, diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt index f44a857042..7691fab188 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt @@ -19,6 +19,7 @@ package io.element.android.features.roomlist.impl.model import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.RoomNotificationMode @@ -88,7 +89,7 @@ open class RoomListRoomSummaryProvider : PreviewParameterProvider Date: Thu, 18 Apr 2024 00:43:34 +0200 Subject: [PATCH 35/46] String -> RoomAlias --- .../io/element/android/libraries/matrix/api/room/Mention.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/Mention.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/Mention.kt index 5285638713..a02fedde4b 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/Mention.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/Mention.kt @@ -23,5 +23,5 @@ sealed interface Mention { data class User(val userId: UserId) : Mention data object AtRoom : Mention data class Room(val roomId: RoomId) : Mention - data class RoomAlias(val roomAlias: String?) : Mention + data class RoomAlias(val roomAlias: RoomAlias?) : Mention } From 426cd9106cf955725887fb56eb46e0f9a65ea29a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 18 Apr 2024 00:44:34 +0200 Subject: [PATCH 36/46] Fix test. --- .../android/features/roomdetails/RoomDetailsPresenterTests.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt index 895ce759f4..8ce06e3835 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt @@ -117,7 +117,7 @@ class RoomDetailsPresenterTests { val presenter = createRoomDetailsPresenter(room) presenter.test { val initialState = awaitItem() - assertThat(initialState.roomId).isEqualTo(room.roomId.value) + assertThat(initialState.roomId).isEqualTo(room.roomId) assertThat(initialState.roomName).isEqualTo(room.name) assertThat(initialState.roomAvatarUrl).isEqualTo(room.avatarUrl) assertThat(initialState.roomTopic).isEqualTo(RoomTopicState.ExistingTopic(room.topic!!)) From 7eae4d28efeb3403cb43c4e1d06f28480d123f99 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 18 Apr 2024 00:56:46 +0200 Subject: [PATCH 37/46] Fix test. --- .../textcomposer/impl/mentions/MentionSpanProviderTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt index a3a4ee7884..2b346ceeab 100644 --- a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt +++ b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt @@ -82,10 +82,10 @@ class MentionSpanProviderTest { fun `getting mention span for @room should return a MentionSpan with normal colors`() { permalinkParser.givenResult( PermalinkData.RoomLink( - roomIdOrAlias = RoomAlias("#").toRoomIdOrAlias(), + roomIdOrAlias = RoomAlias("#room:matrix.org").toRoomIdOrAlias(), ) ) - val mentionSpan = mentionSpanProvider.getMentionSpanFor("@room", "#") + val mentionSpan = mentionSpanProvider.getMentionSpanFor("@room", "#room:matrix.org") assertThat(mentionSpan.backgroundColor).isEqualTo(otherColor) assertThat(mentionSpan.textColor).isEqualTo(otherColor) } From 8b7cc40c29caacd92d8350daae0ed71575c17a7b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 18 Apr 2024 01:27:33 +0200 Subject: [PATCH 38/46] Add Modifier and preview for RoomPreviewMembersCountMolecule. Also increase end padding to ensure that rendering is correct for big numbers. --- .../RoomPreviewMembersCountMolecule.kt | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/RoomPreviewMembersCountMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/RoomPreviewMembersCountMolecule.kt index 508d637174..c9f5bdf204 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/RoomPreviewMembersCountMolecule.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/RoomPreviewMembersCountMolecule.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.designsystem.atomic.molecules import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.widthIn @@ -28,16 +29,21 @@ import androidx.compose.ui.Modifier 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.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text @Composable -fun RoomPreviewMembersCountMolecule(memberCount: Long) { +fun RoomPreviewMembersCountMolecule( + memberCount: Long, + modifier: Modifier = Modifier, +) { Row( - modifier = Modifier + modifier = modifier .background(color = ElementTheme.colors.bgSubtleSecondary, shape = CircleShape) .widthIn(min = 48.dp) - .padding(all = 2.dp), + .padding(start = 2.dp, end = 6.dp, top = 2.dp, bottom = 2.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp) ) { @@ -53,3 +59,16 @@ fun RoomPreviewMembersCountMolecule(memberCount: Long) { ) } } + +@PreviewsDayNight +@Composable +internal fun RoomPreviewMembersCountMoleculePreview() = ElementPreview { + Column( + modifier = Modifier.padding(8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + RoomPreviewMembersCountMolecule(memberCount = 1) + RoomPreviewMembersCountMolecule(memberCount = 888) + RoomPreviewMembersCountMolecule(memberCount = 123456) + } +} From 32949d852aea8a2845208c5becdf1a19640ee9fb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 18 Apr 2024 01:35:21 +0200 Subject: [PATCH 39/46] Use `PreviewsDayNight` instead of `PreviewLightDark` in order to generate screenshots. --- .../features/invite/impl/response/AcceptDeclineInviteView.kt | 4 ++-- .../io/element/android/features/joinroom/impl/JoinRoomView.kt | 4 ++-- .../features/roomaliasresolver/impl/RoomAliasResolverView.kt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInviteView.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInviteView.kt index e0ad70ddc5..53919dbac8 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInviteView.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInviteView.kt @@ -20,7 +20,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.features.invite.api.response.AcceptDeclineInviteState import io.element.android.features.invite.api.response.AcceptDeclineInviteStateProvider @@ -29,6 +28,7 @@ import io.element.android.features.invite.impl.R import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.ui.strings.CommonStrings import kotlin.jvm.optionals.getOrNull @@ -102,7 +102,7 @@ private fun DeclineConfirmationDialog( ) } -@PreviewLightDark +@PreviewsDayNight @Composable internal fun AcceptDeclineInviteViewLightPreview(@PreviewParameter(AcceptDeclineInviteStateProvider::class) state: AcceptDeclineInviteState) = ElementPreview { diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt index e944dc281f..9c93105f9a 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt @@ -25,7 +25,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom @@ -40,6 +39,7 @@ import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.ButtonSize import io.element.android.libraries.designsystem.theme.components.OutlinedButton @@ -236,7 +236,7 @@ private fun JoinRoomTopBar( ) } -@PreviewLightDark +@PreviewsDayNight @Composable internal fun JoinRoomViewPreview(@PreviewParameter(JoinRoomStateProvider::class) state: JoinRoomState) = ElementPreview { JoinRoomView( diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt index 03c47602e4..271baad845 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt @@ -29,7 +29,6 @@ import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.libraries.architecture.AsyncData @@ -40,6 +39,7 @@ import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.ButtonSize import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator @@ -150,7 +150,7 @@ private fun RoomAliasResolverTopBar( ) } -@PreviewLightDark +@PreviewsDayNight @Composable internal fun RoomAliasResolverViewPreview(@PreviewParameter(RoomAliasResolverStateProvider::class) state: RoomAliasResolverState) = ElementPreview { RoomAliasResolverView( From efe39a1924b0a8c76c574535be48e3660edffcd2 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Wed, 17 Apr 2024 23:47:04 +0000 Subject: [PATCH 40/46] Update screenshots --- ...ptDeclineInviteViewLight-Day-0_1_null_0,NEXUS_5,1.0,en].png | 3 +++ ...ptDeclineInviteViewLight-Day-0_1_null_1,NEXUS_5,1.0,en].png | 3 +++ ...ptDeclineInviteViewLight-Day-0_1_null_2,NEXUS_5,1.0,en].png | 3 +++ ...ptDeclineInviteViewLight-Day-0_1_null_3,NEXUS_5,1.0,en].png | 3 +++ ...ptDeclineInviteViewLight-Day-0_1_null_4,NEXUS_5,1.0,en].png | 3 +++ ...DeclineInviteViewLight-Night-0_2_null_0,NEXUS_5,1.0,en].png | 3 +++ ...DeclineInviteViewLight-Night-0_2_null_1,NEXUS_5,1.0,en].png | 3 +++ ...DeclineInviteViewLight-Night-0_2_null_2,NEXUS_5,1.0,en].png | 3 +++ ...DeclineInviteViewLight-Night-0_2_null_3,NEXUS_5,1.0,en].png | 3 +++ ...DeclineInviteViewLight-Night-0_2_null_4,NEXUS_5,1.0,en].png | 3 +++ ...omView_null_JoinRoomView-Day-0_1_null_0,NEXUS_5,1.0,en].png | 3 +++ ...omView_null_JoinRoomView-Day-0_1_null_1,NEXUS_5,1.0,en].png | 3 +++ ...omView_null_JoinRoomView-Day-0_1_null_2,NEXUS_5,1.0,en].png | 3 +++ ...omView_null_JoinRoomView-Day-0_1_null_3,NEXUS_5,1.0,en].png | 3 +++ ...omView_null_JoinRoomView-Day-0_1_null_4,NEXUS_5,1.0,en].png | 3 +++ ...omView_null_JoinRoomView-Day-0_1_null_5,NEXUS_5,1.0,en].png | 3 +++ ...omView_null_JoinRoomView-Day-0_1_null_6,NEXUS_5,1.0,en].png | 3 +++ ...View_null_JoinRoomView-Night-0_2_null_0,NEXUS_5,1.0,en].png | 3 +++ ...View_null_JoinRoomView-Night-0_2_null_1,NEXUS_5,1.0,en].png | 3 +++ ...View_null_JoinRoomView-Night-0_2_null_2,NEXUS_5,1.0,en].png | 3 +++ ...View_null_JoinRoomView-Night-0_2_null_3,NEXUS_5,1.0,en].png | 3 +++ ...View_null_JoinRoomView-Night-0_2_null_4,NEXUS_5,1.0,en].png | 3 +++ ...View_null_JoinRoomView-Night-0_2_null_5,NEXUS_5,1.0,en].png | 3 +++ ...View_null_JoinRoomView-Night-0_2_null_6,NEXUS_5,1.0,en].png | 3 +++ ...ll_RoomAliasResolverView-Day-0_1_null_0,NEXUS_5,1.0,en].png | 3 +++ ...ll_RoomAliasResolverView-Day-0_1_null_1,NEXUS_5,1.0,en].png | 3 +++ ...ll_RoomAliasResolverView-Day-0_1_null_2,NEXUS_5,1.0,en].png | 3 +++ ..._RoomAliasResolverView-Night-0_2_null_0,NEXUS_5,1.0,en].png | 3 +++ ..._RoomAliasResolverView-Night-0_2_null_1,NEXUS_5,1.0,en].png | 3 +++ ..._RoomAliasResolverView-Night-0_2_null_2,NEXUS_5,1.0,en].png | 3 +++ ...mPreviewMembersCountMolecule-Day_0_null,NEXUS_5,1.0,en].png | 3 +++ ...reviewMembersCountMolecule-Night_1_null,NEXUS_5,1.0,en].png | 3 +++ 32 files changed, 96 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_4,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_4,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_4,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_5,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_6,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_4,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_5,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_6,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Day-0_1_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Day-0_1_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Day-0_1_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Night-0_2_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Night-0_2_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Night-0_2_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_RoomPreviewMembersCountMolecule_null_RoomPreviewMembersCountMolecule-Day_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_RoomPreviewMembersCountMolecule_null_RoomPreviewMembersCountMolecule-Night_1_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..665c8811ac --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb0d3bfcfd75cbd75fd9270ff1dc27090e5dbac79ca8db8a46d91a4c12bc966b +size 4457 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..707fdfc344 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a32da6aa9e7137262ea70c3901fb058c97daf24ff445eda23dac8640d188f124 +size 25585 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f18ab57edc --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c734484b6ac89c5540db52a62eb5ca17d5013c5901aea10876d769c6abb019dd +size 26263 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5c0332b884 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b2474c3058ae955f81985c02b9491cc79cd7d87001900dc449a1fb42684114f +size 11970 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5c0332b884 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b2474c3058ae955f81985c02b9491cc79cd7d87001900dc449a1fb42684114f +size 11970 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..fae8a6fca3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c89ac73df77c2bccb0c2aa80cee1420f78e7d07f0eda89a90bffef55e8cf753 +size 4464 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..340042ca14 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5f31dc397d61b8b088ece9680dd8c700a9a0d7bb6310859e82adba8c942effaf +size 21861 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4c38bc63ca --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6083235628afa1c7aee7dd584d14b4b3ac7cd76b077b040675b2de42adbb36b7 +size 22413 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7ef2eda524 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:812e571d895c87f6b8ab77638a716a28c125dc6e41310f4f5d376035d96b5dae +size 9323 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7ef2eda524 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:812e571d895c87f6b8ab77638a716a28c125dc6e41310f4f5d376035d96b5dae +size 9323 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..dc80770d93 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f7406a2a6fa8e4dd4423a5141147ecc1aa235d4dbff980b7ef72fa2e59f502fc +size 7686 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e3c12ee174 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:329ade4a3d0e1b013f03bf5d2d107ce2dacc235f7e2defc24c5cd1623330b107 +size 23944 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4ce8f03332 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0ed85e4bb1351a1af82b64628c35aff7ef165c9df9b1ac0e1bba737a9d1e15a8 +size 25797 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..9206b1001e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d0ef19d140f808746f44b1801cc618048b9216a0e8413bb4ee5535a0cb8e9d80 +size 26590 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d681433b5c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ba446e166858f6da8d79ae4af997ff142d73edebf178aadf75579b041a3d267 +size 28605 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f30069d89c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7c34154223d9eef3a66fbc9c266010e3e51b293db7f4698d550638fa8912a231 +size 16864 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..45d13a7673 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e70d55552b46b1a34848651bf8bcf8048ba2d28c7d2d356f8c1a0f71bb6e049 +size 21104 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2cec18ce5d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa320f4667087c59776d08dfb9108a577e42eb51ba1e6b3d3ea6da3d55605495 +size 7523 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..cfafbbd9c2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d4244c5d24935e2cf6df3cff5d31eb7980757a1e555aedbb97561c1827778e2b +size 22816 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..219b7b1ebe --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3725b3e21eb939bebcc44b9c62f21817f1ab227dc7a9f2367b721cdb0e750720 +size 25127 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ef833bb514 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f9d39f1a58193367bc780fe9f266ca3b0f3f6ffbcf37c14441537aa7ee5f473c +size 25839 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..77af4a413d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:89c6f6644f84fc98ee0a0ced85f141cc862b1c114b2ca29874696f655926b4f2 +size 27539 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..09bf86e31a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:49c58bd2bbc86105e547bddf47c69df0017d6a4a66be0dc3f80e8cecfacf7a86 +size 15837 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4a2e191087 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:740d856d28b44200e7148d297103f8c38e2159c75e182c84f1d3d0d32cfff1bf +size 19529 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Day-0_1_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..fc7d210706 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Day-0_1_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9651397f53e398e05c27f41f341d1ae949fbdbce05e97083bb9aef909f67fbaf +size 11783 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Day-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Day-0_1_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..774701a753 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Day-0_1_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63c9d5c445abb20a80b45100afc59b6803f5744a9a6f83ce3d91653801fdd3eb +size 13455 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Day-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Day-0_1_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c060f1dbcf --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Day-0_1_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dfc692c20e4b8998a675b72cac9b324da4ec636df68a5611ceaa368217e50243 +size 19618 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Night-0_2_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..0dcdf1c87d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Night-0_2_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15efd8dc8b74d3e5364b3f9204a5e85e0cff8689d3b632a61368e101b2e833ae +size 11099 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Night-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Night-0_2_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..08924db96b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Night-0_2_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18cc9896eadf78db99e6f8a96ab67a2af22ce1f7072f84cd97dee6e50fd474b2 +size 12736 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Night-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Night-0_2_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d0608de0b7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomaliasresolver.impl_RoomAliasResolverView_null_RoomAliasResolverView-Night-0_2_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d7db045b1e7877a6ee7b51e3996d43bcaee22ae930ade59701fd07f7a6b0eabf +size 18250 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_RoomPreviewMembersCountMolecule_null_RoomPreviewMembersCountMolecule-Day_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_RoomPreviewMembersCountMolecule_null_RoomPreviewMembersCountMolecule-Day_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d9166cd47d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_RoomPreviewMembersCountMolecule_null_RoomPreviewMembersCountMolecule-Day_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:754e5614822424e86e73a631f5303074e85dfb6bf4e6abc7f7e698e2cbc8d6ba +size 12850 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_RoomPreviewMembersCountMolecule_null_RoomPreviewMembersCountMolecule-Night_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_RoomPreviewMembersCountMolecule_null_RoomPreviewMembersCountMolecule-Night_1_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..36604c6005 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_RoomPreviewMembersCountMolecule_null_RoomPreviewMembersCountMolecule-Night_1_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d06d65a5768007020eab6b48844ea1216fcdb9b19f17f15171234f8a5dd9c517 +size 12394 From 689d1a50dedebbb5f87502301ddbe55d1ba92929 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 18 Apr 2024 08:58:18 +0200 Subject: [PATCH 41/46] Readability --- .../atomic/molecules/RoomPreviewMembersCountMolecule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/RoomPreviewMembersCountMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/RoomPreviewMembersCountMolecule.kt index c9f5bdf204..a781c429b0 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/RoomPreviewMembersCountMolecule.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/RoomPreviewMembersCountMolecule.kt @@ -69,6 +69,6 @@ internal fun RoomPreviewMembersCountMoleculePreview() = ElementPreview { ) { RoomPreviewMembersCountMolecule(memberCount = 1) RoomPreviewMembersCountMolecule(memberCount = 888) - RoomPreviewMembersCountMolecule(memberCount = 123456) + RoomPreviewMembersCountMolecule(memberCount = 123_456) } } From fc5606a5804f39b60b93c634b3a589e4d0494a5a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 18 Apr 2024 09:07:01 +0200 Subject: [PATCH 42/46] Fix Preview name --- .../features/invite/impl/response/AcceptDeclineInviteView.kt | 2 +- ..._AcceptDeclineInviteView-Day-0_1_null_0,NEXUS_5,1.0,en].png} | 0 ..._AcceptDeclineInviteView-Day-0_1_null_1,NEXUS_5,1.0,en].png} | 0 ..._AcceptDeclineInviteView-Day-0_1_null_2,NEXUS_5,1.0,en].png} | 0 ..._AcceptDeclineInviteView-Day-0_1_null_3,NEXUS_5,1.0,en].png} | 0 ..._AcceptDeclineInviteView-Day-0_1_null_4,NEXUS_5,1.0,en].png} | 0 ...cceptDeclineInviteView-Night-0_2_null_0,NEXUS_5,1.0,en].png} | 0 ...cceptDeclineInviteView-Night-0_2_null_1,NEXUS_5,1.0,en].png} | 0 ...cceptDeclineInviteView-Night-0_2_null_2,NEXUS_5,1.0,en].png} | 0 ...cceptDeclineInviteView-Night-0_2_null_3,NEXUS_5,1.0,en].png} | 0 ...cceptDeclineInviteView-Night-0_2_null_4,NEXUS_5,1.0,en].png} | 0 11 files changed, 1 insertion(+), 1 deletion(-) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_3,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_3,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_4,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_4,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_3,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_3,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_4,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_4,NEXUS_5,1.0,en].png} (100%) diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInviteView.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInviteView.kt index 53919dbac8..3f229fbe85 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInviteView.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInviteView.kt @@ -104,7 +104,7 @@ private fun DeclineConfirmationDialog( @PreviewsDayNight @Composable -internal fun AcceptDeclineInviteViewLightPreview(@PreviewParameter(AcceptDeclineInviteStateProvider::class) state: AcceptDeclineInviteState) = +internal fun AcceptDeclineInviteViewPreview(@PreviewParameter(AcceptDeclineInviteStateProvider::class) state: AcceptDeclineInviteState) = ElementPreview { AcceptDeclineInviteView( state = state, diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Day-0_1_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteViewLight-Night-0_2_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_4,NEXUS_5,1.0,en].png From 12fef22930ef7dd50b7360b78b5a1e1ea768d512 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 18 Apr 2024 09:58:58 +0200 Subject: [PATCH 43/46] Order import --- .../element/android/features/joinroom/impl/JoinRoomView.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt index 9c93105f9a..6802e7f77c 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt @@ -28,12 +28,12 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom -import io.element.android.libraries.designsystem.atomic.molecules.ButtonRowMolecule import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewDescriptionAtom -import io.element.android.libraries.designsystem.atomic.molecules.RoomPreviewMembersCountMolecule -import io.element.android.libraries.designsystem.atomic.organisms.RoomPreviewOrganism import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewSubtitleAtom import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewTitleAtom +import io.element.android.libraries.designsystem.atomic.molecules.ButtonRowMolecule +import io.element.android.libraries.designsystem.atomic.molecules.RoomPreviewMembersCountMolecule +import io.element.android.libraries.designsystem.atomic.organisms.RoomPreviewOrganism import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarSize From 1f4034b00931092ab8ef9e382c1253347f31d1d2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 18 Apr 2024 09:59:53 +0200 Subject: [PATCH 44/46] More complete preview --- .../features/joinroom/impl/JoinRoomStateProvider.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt index 7c34a05c6b..91c7ea1e37 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt @@ -37,7 +37,15 @@ open class JoinRoomStateProvider : PreviewParameterProvider { contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin) ), aJoinRoomState( - contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanKnock) + contentState = aLoadedContentState( + joinAuthorisationStatus = JoinAuthorisationStatus.CanKnock, + topic = "lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor incididunt" + + " ut labore et dolore magna aliqua ut enim ad minim veniam quis nostrud exercitation ullamco" + + " laboris nisi ut aliquip ex ea commodo consequat duis aute irure dolor in reprehenderit in" + + " voluptate velit esse cillum dolore eu fugiat nulla pariatur excepteur sint occaecat cupidatat" + + " non proident sunt in culpa qui officia deserunt mollit anim id est laborum", + numberOfMembers = 888, + ) ), aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited) From f42479d6e2ecc551a8b62e706e67764c8de0275d Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 18 Apr 2024 08:11:24 +0000 Subject: [PATCH 45/46] Update screenshots --- ...cceptDeclineInviteView-Day-0_1_null_0,NEXUS_5,1.0,en].png} | 0 ...cceptDeclineInviteView-Day-0_1_null_1,NEXUS_5,1.0,en].png} | 0 ...cceptDeclineInviteView-Day-0_1_null_2,NEXUS_5,1.0,en].png} | 0 ...cceptDeclineInviteView-Day-0_1_null_3,NEXUS_5,1.0,en].png} | 0 ...cceptDeclineInviteView-Day-0_1_null_4,NEXUS_5,1.0,en].png} | 0 ...eptDeclineInviteView-Night-0_2_null_0,NEXUS_5,1.0,en].png} | 0 ...eptDeclineInviteView-Night-0_2_null_1,NEXUS_5,1.0,en].png} | 0 ...eptDeclineInviteView-Night-0_2_null_2,NEXUS_5,1.0,en].png} | 0 ...eptDeclineInviteView-Night-0_2_null_3,NEXUS_5,1.0,en].png} | 0 ...eptDeclineInviteView-Night-0_2_null_4,NEXUS_5,1.0,en].png} | 0 ...mView_null_JoinRoomView-Day-0_1_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...iew_null_JoinRoomView-Night-0_2_null_3,NEXUS_5,1.0,en].png | 4 ++-- 12 files changed, 4 insertions(+), 4 deletions(-) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Day-0_1_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Day-0_1_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Day-0_1_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_3,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Day-0_1_null_3,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_4,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Day-0_1_null_4,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Night-0_2_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Night-0_2_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Night-0_2_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_3,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Night-0_2_null_3,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_4,NEXUS_5,1.0,en].png => ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Night-0_2_null_4,NEXUS_5,1.0,en].png} (100%) diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Day-0_1_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Day-0_1_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Day-0_1_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Day-0_1_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Day-0_1_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Day-0_1_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Day-0_1_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Day-0_1_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Day-0_1_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Day-0_1_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Day-0_1_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Night-0_2_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Night-0_2_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Night-0_2_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Night-0_2_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Night-0_2_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Night-0_2_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Night-0_2_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Night-0_2_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Night-0_2_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteViewLight_null_AcceptDeclineInviteView-Night-0_2_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invite.impl.response_AcceptDeclineInviteView_null_AcceptDeclineInviteView-Night-0_2_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_3,NEXUS_5,1.0,en].png index 9206b1001e..89c7b10b91 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Day-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d0ef19d140f808746f44b1801cc618048b9216a0e8413bb4ee5535a0cb8e9d80 -size 26590 +oid sha256:0fcfb2b5cc790707f48dbd56f2c3f4909ebe1ae93b082309e7b0b89e82fec6c8 +size 39320 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_3,NEXUS_5,1.0,en].png index ef833bb514..083428a106 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.joinroom.impl_JoinRoomView_null_JoinRoomView-Night-0_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f9d39f1a58193367bc780fe9f266ca3b0f3f6ffbcf37c14441537aa7ee5f473c -size 25839 +oid sha256:e23f1d506bcfc603192d854945059b4f03e9215f20f3f7f9150728aee0c4bf7e +size 38085 From 30e0478cc0f7bf9a6fc0aee9f55dbcec97238327 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 18 Apr 2024 10:57:18 +0200 Subject: [PATCH 46/46] Please ktlint --- .../libraries/matrix/impl/RustMatrixClient.kt | 1 - .../impl/room/preview/RoomPreviewMapper.kt | 42 ------------------- 2 files changed, 43 deletions(-) delete mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/preview/RoomPreviewMapper.kt diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index d27daaafdd..eae3095aae 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -61,7 +61,6 @@ import io.element.android.libraries.matrix.impl.room.RoomContentForwarder import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber import io.element.android.libraries.matrix.impl.room.RustMatrixRoom import io.element.android.libraries.matrix.impl.room.map -// TODO import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewMapper import io.element.android.libraries.matrix.impl.roomdirectory.RustRoomDirectoryService import io.element.android.libraries.matrix.impl.roomlist.RoomListFactory import io.element.android.libraries.matrix.impl.roomlist.RustRoomListService diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/preview/RoomPreviewMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/preview/RoomPreviewMapper.kt deleted file mode 100644 index 0497d8aeff..0000000000 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/preview/RoomPreviewMapper.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2024 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.libraries.matrix.impl.room.preview - -// TODO Restore -// import io.element.android.libraries.matrix.api.core.RoomAlias -// import io.element.android.libraries.matrix.api.core.RoomId -// import io.element.android.libraries.matrix.api.room.preview.RoomPreview -// import org.matrix.rustcomponents.sdk.RoomPreview as RustRoomPreview -// -// object RoomPreviewMapper { -// fun map(roomPreview: RustRoomPreview): RoomPreview { -// return RoomPreview( -// roomId = RoomId(roomPreview.roomId), -// canonicalAlias = roomPreview.canonicalAlias?.let(::RoomAlias), -// name = roomPreview.name, -// topic = roomPreview.topic, -// avatarUrl = roomPreview.avatarUrl, -// numberOfJoinedMembers = roomPreview.numJoinedMembers.toLong(), -// roomType = roomPreview.roomType, -// isHistoryWorldReadable = roomPreview.isHistoryWorldReadable, -// isJoined = roomPreview.isJoined, -// isInvited = roomPreview.isInvited, -// isPublic = roomPreview.isPublic, -// canKnock = roomPreview.canKnock -// ) -// } -// }