From 043aa932761794d672aaaeaf8cb763763832d2a4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Sep 2025 09:57:21 +0200 Subject: [PATCH 01/10] Remove unused NavTarget.Empty --- .../android/features/messages/impl/MessagesFlowNode.kt | 7 ------- 1 file changed, 7 deletions(-) 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 2f563395f7..5cbce99701 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 @@ -15,7 +15,6 @@ import androidx.lifecycle.lifecycleScope import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.node.node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack @@ -126,9 +125,6 @@ class MessagesFlowNode( plugins = plugins ) { sealed interface NavTarget : Parcelable { - @Parcelize - data object Empty : NavTarget - @Parcelize data class Messages(val focusedEventId: EventId?) : NavTarget @@ -399,9 +395,6 @@ class MessagesFlowNode( } createNode(buildContext, plugins = listOf(callback)) } - NavTarget.Empty -> { - node(buildContext) {} - } NavTarget.KnockRequestsList -> { knockRequestsListEntryPoint.createNode(this, buildContext) } From d3502c44123f7abbc9e625f351f41b4f1fd23078 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Sep 2025 09:58:42 +0200 Subject: [PATCH 02/10] Let SpaceEntryPoint.Inputs implement NodeInputs. --- .../io/element/android/features/space/api/SpaceEntryPoint.kt | 3 ++- .../kotlin/io/element/android/features/space/impl/SpaceNode.kt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/features/space/api/src/main/kotlin/io/element/android/features/space/api/SpaceEntryPoint.kt b/features/space/api/src/main/kotlin/io/element/android/features/space/api/SpaceEntryPoint.kt index 88cbbad5dd..5f7be8dba0 100644 --- a/features/space/api/src/main/kotlin/io/element/android/features/space/api/SpaceEntryPoint.kt +++ b/features/space/api/src/main/kotlin/io/element/android/features/space/api/SpaceEntryPoint.kt @@ -11,6 +11,7 @@ import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import io.element.android.libraries.architecture.FeatureEntryPoint +import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.matrix.api.core.RoomId interface SpaceEntryPoint : FeatureEntryPoint { @@ -27,7 +28,7 @@ interface SpaceEntryPoint : FeatureEntryPoint { data class Inputs( val roomId: RoomId - ) : Plugin + ) : NodeInputs interface Callback : Plugin { fun onOpenRoom(roomId: RoomId) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceNode.kt index d37be4c76c..a4828fe9fe 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceNode.kt @@ -16,6 +16,7 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Inject import io.element.android.annotations.ContributesNode import io.element.android.features.space.api.SpaceEntryPoint +import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -25,7 +26,7 @@ class SpaceNode( @Assisted plugins: List, presenterFactory: SpacePresenter.Factory, ) : Node(buildContext, plugins = plugins) { - private val inputs = plugins.filterIsInstance().single() + private val inputs: SpaceEntryPoint.Inputs = inputs() private val callback = plugins.filterIsInstance().single() private val presenter = presenterFactory.create(inputs) From e77c0ab0c2847778b6f2792724cf0e7f5cee8661 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Sep 2025 10:06:16 +0200 Subject: [PATCH 03/10] Small cleanup --- .../permissions/ChangeRoomPermissionsNode.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsNode.kt index 2a3e5a2bad..906c97fb2e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsNode.kt @@ -34,10 +34,7 @@ class ChangeRoomPermissionsNode( ) : NodeInputs, Parcelable private val inputs: Inputs = inputs() - - private val presenter = presenterFactory.run { - create(inputs.section) - } + private val presenter = presenterFactory.create(inputs.section) @Composable override fun View(modifier: Modifier) { From f7108b0f676beb382731a499f0af0ea4307b91b8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Sep 2025 10:08:50 +0200 Subject: [PATCH 04/10] Small cleanup --- .../impl/ChangeRolesNode.kt | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNode.kt b/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNode.kt index 342fba632f..26b1af6bbb 100644 --- a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNode.kt +++ b/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNode.kt @@ -37,16 +37,7 @@ class ChangeRolesNode( ) : NodeInputs private val inputs: Inputs = inputs() - - private val presenter = presenterFactory.run { - val role = when (inputs.listType) { - ChangeRoomMemberRolesListType.Admins -> RoomMember.Role.Admin - ChangeRoomMemberRolesListType.Moderators -> RoomMember.Role.Moderator - ChangeRoomMemberRolesListType.SelectNewOwnersWhenLeaving -> RoomMember.Role.Owner(isCreator = false) - } - create(role) - } - + private val presenter = presenterFactory.create(inputs.listType.toRoomMemberRole()) private val stateFlow = launchMolecule { presenter.present() } suspend fun waitForRoleChanged() { @@ -63,3 +54,9 @@ class ChangeRolesNode( ) } } + +private fun ChangeRoomMemberRolesListType.toRoomMemberRole() = when (this) { + ChangeRoomMemberRolesListType.Admins -> RoomMember.Role.Admin + ChangeRoomMemberRolesListType.Moderators -> RoomMember.Role.Moderator + ChangeRoomMemberRolesListType.SelectNewOwnersWhenLeaving -> RoomMember.Role.Owner(isCreator = false) +} From b3135285442368f912a532e9f9c0edde77c5fb70 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Sep 2025 10:24:16 +0200 Subject: [PATCH 05/10] Method can be private. --- .../io/element/android/features/home/impl/HomeFlowNode.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt index a8eeb6eee8..d8ecf7016f 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt @@ -164,7 +164,7 @@ class HomeFlowNode( stateFlow.value.roomListState.eventSink(RoomListEvents.LeaveRoom(roomId, needsConfirmation = false)) } - fun rootNode(buildContext: BuildContext): Node { + private fun rootNode(buildContext: BuildContext): Node { return node(buildContext) { modifier -> val state by stateFlow.collectAsState() val activity = requireNotNull(LocalActivity.current) From 071665905b4e742db48c224d9f6a0daec27ffd22 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Sep 2025 10:33:18 +0200 Subject: [PATCH 06/10] Simplify code --- .../impl/ChangeRoomMemberRolesRootNode.kt | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRoomMemberRolesRootNode.kt b/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRoomMemberRolesRootNode.kt index 0ce019aa16..5055761e6e 100644 --- a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRoomMemberRolesRootNode.kt +++ b/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRoomMemberRolesRootNode.kt @@ -39,16 +39,13 @@ class ChangeRoomMemberRolesRootNode( roomComponentFactory: RoomComponentFactory, ) : ParentNode( navModel = PermanentNavModel( - navTargets = setOf(NavTarget.Root), + navTargets = setOf(NavTarget), savedStateMap = buildContext.savedStateMap, ), buildContext = buildContext, plugins = plugins, ), DependencyInjectionGraphOwner, ChangeRoomMemberRolesEntryPoint.NodeProxy { - sealed interface NavTarget : Parcelable { - @Parcelize - object Root : NavTarget - } + @Parcelize object NavTarget : Parcelable data class Inputs( val joinedRoom: JoinedRoom, @@ -60,14 +57,10 @@ class ChangeRoomMemberRolesRootNode( override val graph = roomComponentFactory.create(inputs.joinedRoom) override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { - return when (navTarget) { - NavTarget.Root -> { - createNode( - buildContext = buildContext, - plugins = listOf(ChangeRolesNode.Inputs(listType = inputs.listType)), - ) - } - } + return createNode( + buildContext = buildContext, + plugins = listOf(ChangeRolesNode.Inputs(listType = inputs.listType)), + ) } @Composable From 11887a39a27eae2122648b1da319386bc766c7b5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Sep 2025 10:36:35 +0200 Subject: [PATCH 07/10] Rename to follow naming convention --- .../features/viewfolder/impl/DefaultViewFolderEntryPoint.kt | 6 +++--- .../root/{ViewFolderRootNode.kt => ViewFolderFlowNode.kt} | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) rename features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/{ViewFolderRootNode.kt => ViewFolderFlowNode.kt} (98%) diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/DefaultViewFolderEntryPoint.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/DefaultViewFolderEntryPoint.kt index 64d469c34f..330ca68a8c 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/DefaultViewFolderEntryPoint.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/DefaultViewFolderEntryPoint.kt @@ -14,7 +14,7 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.Inject import io.element.android.features.viewfolder.api.ViewFolderEntryPoint -import io.element.android.features.viewfolder.impl.root.ViewFolderRootNode +import io.element.android.features.viewfolder.impl.root.ViewFolderFlowNode import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) @@ -25,7 +25,7 @@ class DefaultViewFolderEntryPoint : ViewFolderEntryPoint { return object : ViewFolderEntryPoint.NodeBuilder { override fun params(params: ViewFolderEntryPoint.Params): ViewFolderEntryPoint.NodeBuilder { - plugins += ViewFolderRootNode.Inputs(params.rootPath) + plugins += ViewFolderFlowNode.Inputs(params.rootPath) return this } @@ -35,7 +35,7 @@ class DefaultViewFolderEntryPoint : ViewFolderEntryPoint { } override fun build(): Node { - return parentNode.createNode(buildContext, plugins) + return parentNode.createNode(buildContext, plugins) } } } diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/ViewFolderRootNode.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/ViewFolderFlowNode.kt similarity index 98% rename from features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/ViewFolderRootNode.kt rename to features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/ViewFolderFlowNode.kt index 984c2e1f10..2eb6712364 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/ViewFolderRootNode.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/ViewFolderFlowNode.kt @@ -34,10 +34,10 @@ import kotlinx.parcelize.Parcelize @ContributesNode(AppScope::class) @Inject -class ViewFolderRootNode( +class ViewFolderFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, -) : BaseFlowNode( +) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Root, savedStateMap = buildContext.savedStateMap, From a45e9d23c275a7ca355a7e4f637bbcea6d181dbe Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Sep 2025 10:38:16 +0200 Subject: [PATCH 08/10] Rename Node to follow naming convention --- .../mediaviewer/impl/DefaultMediaGalleryEntryPoint.kt | 4 ++-- ...MediaGalleryRootNode.kt => MediaGalleryFlowNode.kt} | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) rename libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/{MediaGalleryRootNode.kt => MediaGalleryFlowNode.kt} (95%) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaGalleryEntryPoint.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaGalleryEntryPoint.kt index e9e62131aa..0b6c2cc6cb 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaGalleryEntryPoint.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaGalleryEntryPoint.kt @@ -15,7 +15,7 @@ import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.Inject import io.element.android.libraries.architecture.createNode import io.element.android.libraries.mediaviewer.api.MediaGalleryEntryPoint -import io.element.android.libraries.mediaviewer.impl.gallery.root.MediaGalleryRootNode +import io.element.android.libraries.mediaviewer.impl.gallery.root.MediaGalleryFlowNode @ContributesBinding(AppScope::class) @Inject @@ -30,7 +30,7 @@ class DefaultMediaGalleryEntryPoint : MediaGalleryEntryPoint { } override fun build(): Node { - return parentNode.createNode(buildContext, plugins) + return parentNode.createNode(buildContext, plugins) } } } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryRootNode.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryFlowNode.kt similarity index 95% rename from libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryRootNode.kt rename to libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryFlowNode.kt index 66cd9e14e1..4f899ac0f5 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryRootNode.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryFlowNode.kt @@ -41,11 +41,11 @@ import kotlinx.parcelize.Parcelize @ContributesNode(RoomScope::class) @Inject -class MediaGalleryRootNode( +class MediaGalleryFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, private val mediaViewerEntryPoint: MediaViewerEntryPoint -) : BaseFlowNode( +) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Root, savedStateMap = buildContext.savedStateMap, @@ -87,11 +87,11 @@ class MediaGalleryRootNode( NavTarget.Root -> { val callback = object : MediaGalleryNode.Callback { override fun onBackClick() { - this@MediaGalleryRootNode.onBackClick() + this@MediaGalleryFlowNode.onBackClick() } override fun onViewInTimeline(eventId: EventId) { - this@MediaGalleryRootNode.onViewInTimeline(eventId) + this@MediaGalleryFlowNode.onViewInTimeline(eventId) } override fun onItemClick(item: MediaItem.Event) { @@ -122,7 +122,7 @@ class MediaGalleryRootNode( } override fun onViewInTimeline(eventId: EventId) { - this@MediaGalleryRootNode.onViewInTimeline(eventId) + this@MediaGalleryFlowNode.onViewInTimeline(eventId) } } mediaViewerEntryPoint.nodeBuilder(this, buildContext) From 17c1f21927857f219a3de1dbe737dd2db2f14636 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Sep 2025 10:50:34 +0200 Subject: [PATCH 09/10] Add Konsist test. --- .../android/tests/konsist/KonsistClassNameTest.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt index d24a489c6a..8a0e6d0966 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt @@ -20,6 +20,7 @@ import com.lemonappdev.konsist.api.ext.list.withoutName import com.lemonappdev.konsist.api.ext.list.withoutNameStartingWith import com.lemonappdev.konsist.api.verify.assertEmpty import com.lemonappdev.konsist.api.verify.assertTrue +import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.Presenter import org.junit.Test @@ -44,6 +45,16 @@ class KonsistClassNameTest { } } + @Test + fun `Classes extending 'BaseFlowNode' should have 'FlowNode' suffix`() { + Konsist.scopeFromProject() + .classes() + .withAllParentsOf(BaseFlowNode::class) + .assertTrue { + it.name.endsWith("FlowNode") + } + } + @Test fun `Classes extending 'PreviewParameterProvider' name MUST end with 'Provider' and MUST contain provided class name`() { Konsist.scopeFromProduction() From c286c60c3049073353274fe227233dfbb3600725 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Sep 2025 12:27:27 +0200 Subject: [PATCH 10/10] Remove useless line of code. --- .../io/element/android/libraries/matrix/impl/RustMatrixClient.kt | 1 - 1 file changed, 1 deletion(-) 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 e193d8f37d..24407ab6f9 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 @@ -287,7 +287,6 @@ class RustMatrixClient( } override suspend fun getRoom(roomId: RoomId): BaseRoom? = withContext(sessionDispatcher) { - innerClient.rooms() roomFactory.getBaseRoom(roomId) }