diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt index 47681dc624..5e160ed49f 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt @@ -21,13 +21,13 @@ import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.ParentNode import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.appnav.di.SessionGraphFactory import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.DependencyInjectionGraphOwner @@ -60,6 +60,8 @@ class LoggedInAppScopeFlowNode( fun navigateToAddAccount() } + private val callback: Callback = callback() + @Parcelize object NavTarget : Parcelable @@ -82,11 +84,11 @@ class LoggedInAppScopeFlowNode( override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { val callback = object : LoggedInFlowNode.Callback { override fun navigateToBugReport() { - plugins().forEach { it.navigateToBugReport() } + callback.navigateToBugReport() } override fun navigateToAddAccount() { - plugins().forEach { it.navigateToAddAccount() } + callback.navigateToAddAccount() } } return createNode(buildContext, listOf(callback)) 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 dc6ad19f4b..466abc9e36 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -24,7 +24,6 @@ import com.bumble.appyx.core.navigation.NavKey import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel 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 import com.bumble.appyx.navmodel.backstack.BackStack.State.ACTIVE import com.bumble.appyx.navmodel.backstack.BackStack.State.CREATED @@ -67,6 +66,7 @@ import io.element.android.features.userprofile.api.UserProfileEntryPoint import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.waitForChildAttached import io.element.android.libraries.architecture.waitForNavTargetAttached @@ -152,6 +152,7 @@ class LoggedInFlowNode( fun navigateToAddAccount() } + private val callback: Callback = callback() private val loggedInFlowProcessor = LoggedInEventProcessor( snackbarDispatcher = snackbarDispatcher, roomMembershipObserver = matrixClient.roomMembershipObserver, @@ -329,7 +330,7 @@ class LoggedInFlowNode( } override fun navigateToBugReport() { - plugins().forEach { it.navigateToBugReport() } + callback.navigateToBugReport() } } homeEntryPoint @@ -396,11 +397,11 @@ class LoggedInFlowNode( is NavTarget.Settings -> { val callback = object : PreferencesEntryPoint.Callback { override fun navigateToAddAccount() { - plugins().forEach { it.navigateToAddAccount() } + callback.navigateToAddAccount() } override fun navigateToBugReport() { - plugins().forEach { it.navigateToBugReport() } + callback.navigateToAddAccount() } override fun navigateToSecureBackup() { diff --git a/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt index f3c92f4407..bb0ffa82c1 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt @@ -18,7 +18,6 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted @@ -29,6 +28,7 @@ import io.element.android.features.login.api.LoginParams import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.designsystem.utils.ForceOrientationInMobileDevices import io.element.android.libraries.designsystem.utils.ScreenOrientation @@ -58,6 +58,7 @@ class NotLoggedInFlowNode( fun navigateToBugReport() } + private val callback: Callback = callback() private val inputs = inputs() override fun onBuilt() { @@ -79,7 +80,7 @@ class NotLoggedInFlowNode( NavTarget.Root -> { val callback = object : LoginEntryPoint.Callback { override fun navigateToBugReport() { - plugins().forEach { it.navigateToBugReport() } + callback.navigateToBugReport() } } loginEntryPoint diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInNode.kt index edc2be05db..9f4187cb9c 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInNode.kt @@ -12,10 +12,10 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -32,18 +32,14 @@ class LoggedInNode( fun navigateToNotificationTroubleshoot() } - private fun navigateToNotificationTroubleshoot() { - plugins().forEach { - it.navigateToNotificationTroubleshoot() - } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { val loggedInState = loggedInPresenter.present() LoggedInView( state = loggedInState, - navigateToNotificationTroubleshoot = ::navigateToNotificationTroubleshoot, + navigateToNotificationTroubleshoot = callback::navigateToNotificationTroubleshoot, modifier = modifier ) } 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 154d8c45d0..760adfab25 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 @@ -31,6 +31,7 @@ import io.element.android.features.space.api.SpaceEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.architecture.waitForChildAttached import io.element.android.libraries.di.DependencyInjectionGraphOwner @@ -87,7 +88,7 @@ class JoinedRoomLoadedFlowNode( ) : NodeInputs private val inputs: Inputs = inputs() - private val callbacks = plugins.filterIsInstance() + private val callback: Callback = callback() override val graph = roomGraphFactory.create(inputs.room) init { @@ -124,15 +125,15 @@ class JoinedRoomLoadedFlowNode( private fun createRoomDetailsNode(buildContext: BuildContext, initialTarget: RoomDetailsEntryPoint.InitialTarget): Node { val callback = object : RoomDetailsEntryPoint.Callback { override fun navigateToGlobalNotificationSettings() { - callbacks.forEach { it.navigateToGlobalNotificationSettings() } + callback.navigateToGlobalNotificationSettings() } override fun navigateToRoom(roomId: RoomId, serverNames: List) { - callbacks.forEach { it.navigateToRoom(roomId, serverNames) } + callback.navigateToRoom(roomId, serverNames) } override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) { - callbacks.forEach { it.handlePermalinkClick(data, pushToBackstack) } + callback.handlePermalinkClick(data, pushToBackstack) } override fun startForwardEventFlow(eventId: EventId) { @@ -172,7 +173,7 @@ class JoinedRoomLoadedFlowNode( override fun onDone(roomIds: List) { backstack.pop() roomIds.singleOrNull()?.let { roomId -> - callbacks.forEach { it.navigateToRoom(roomId, emptyList()) } + callback.navigateToRoom(roomId, emptyList()) } } } @@ -187,7 +188,7 @@ class JoinedRoomLoadedFlowNode( private fun createSpaceNode(buildContext: BuildContext): Node { val callback = object : SpaceEntryPoint.Callback { override fun navigateToRoom(roomId: RoomId, viaParameters: List) { - callbacks.forEach { it.navigateToRoom(roomId, viaParameters) } + callback.navigateToRoom(roomId, viaParameters) } override fun navigateToRoomDetails() { @@ -218,7 +219,7 @@ class JoinedRoomLoadedFlowNode( } override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) { - callbacks.forEach { it.handlePermalinkClick(data, pushToBackstack) } + callback.handlePermalinkClick(data, pushToBackstack) } override fun forwardEvent(eventId: EventId) { @@ -226,7 +227,7 @@ class JoinedRoomLoadedFlowNode( } override fun navigateToRoom(roomId: RoomId) { - callbacks.forEach { it.navigateToRoom(roomId, emptyList()) } + callback.navigateToRoom(roomId, emptyList()) } } val params = MessagesEntryPoint.Params( diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt index 8f46103ba5..809edc933e 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt @@ -13,7 +13,6 @@ 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 com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.replace import dev.zacsweers.metro.Assisted @@ -24,6 +23,7 @@ import io.element.android.features.createroom.impl.addpeople.AddPeopleNode import io.element.android.features.createroom.impl.configureroom.ConfigureRoomNode import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId @@ -42,9 +42,7 @@ class CreateRoomFlowNode( buildContext = buildContext, plugins = plugins ) { - private fun onRoomCreated(roomId: RoomId) { - plugins().forEach { it.onRoomCreated(roomId) } - } + private val callback: CreateRoomEntryPoint.Callback = callback() override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { @@ -60,7 +58,7 @@ class CreateRoomFlowNode( val inputs = AddPeopleNode.Inputs(navTarget.roomId) val callback: AddPeopleNode.Callback = object : AddPeopleNode.Callback { override fun onFinish() { - onRoomCreated(navTarget.roomId) + callback.onRoomCreated(navTarget.roomId) } } createNode(buildContext, plugins = listOf(inputs, callback)) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt index 9ea89912cb..5db56043d9 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt @@ -12,13 +12,13 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.invitepeople.api.InvitePeoplePresenter import io.element.android.features.invitepeople.api.InvitePeopleRenderer import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId @@ -39,10 +39,7 @@ class AddPeopleNode( fun onFinish() } - private fun onFinish() { - plugins().forEach { it.onFinish() } - } - + private val callback: Callback = callback() private val roomId = inputs().roomId private val invitePeoplePresenter = invitePeoplePresenterFactory.create( joinedRoom = null, @@ -54,7 +51,7 @@ class AddPeopleNode( val state = invitePeoplePresenter.present() AddPeopleView( state = state, - onFinish = ::onFinish, + onFinish = callback::onFinish, ) { invitePeopleRenderer.Render(state, Modifier) } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt index 9e721d28bd..3c57c0b59d 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt @@ -13,11 +13,11 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.services.analytics.api.AnalyticsService @@ -42,9 +42,7 @@ class ConfigureRoomNode( ) } - private fun onCreateRoomSuccess(roomId: RoomId) { - plugins().forEach { it.onCreateRoomSuccess(roomId) } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -53,7 +51,7 @@ class ConfigureRoomNode( state = state, modifier = modifier, onBackClick = this::navigateUp, - onCreateRoomSuccess = ::onCreateRoomSuccess, + onCreateRoomSuccess = callback::onCreateRoomSuccess, ) } } diff --git a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesNode.kt b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesNode.kt index 27e95898d2..6fb1b2dc0e 100644 --- a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesNode.kt +++ b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesNode.kt @@ -22,6 +22,7 @@ import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.forward.api.ForwardEntryPoint import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.EventId @@ -55,8 +56,8 @@ class ForwardMessagesNode( ) : NodeInputs private val inputs = inputs() + private val callback: ForwardEntryPoint.Callback = callback() private val presenter = presenterFactory.create(inputs.eventId.value, inputs.timelineProvider) - private val callbacks = plugins.filterIsInstance() override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { val callback = object : RoomSelectEntryPoint.Callback { @@ -65,7 +66,7 @@ class ForwardMessagesNode( } override fun onCancel() { - onForwardDone(emptyList()) + callback.onDone(emptyList()) } } @@ -86,12 +87,8 @@ class ForwardMessagesNode( val state = presenter.present() ForwardMessagesView( state = state, - onForwardSuccess = ::onForwardDone, + onForwardSuccess = callback::onDone, ) } } - - private fun onForwardDone(roomIds: List) { - callbacks.forEach { it.onDone(roomIds) } - } } diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/FtueSessionVerificationFlowNode.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/FtueSessionVerificationFlowNode.kt index b36c5633dc..47f2da3455 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/FtueSessionVerificationFlowNode.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/FtueSessionVerificationFlowNode.kt @@ -15,7 +15,6 @@ import androidx.lifecycle.lifecycleScope 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 com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.newRoot import com.bumble.appyx.navmodel.backstack.operation.pop @@ -29,6 +28,7 @@ import io.element.android.features.securebackup.api.SecureBackupEntryPoint import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.designsystem.utils.OpenUrlInTabView import io.element.android.libraries.di.SessionScope @@ -69,6 +69,8 @@ class FtueSessionVerificationFlowNode( fun onDone() } + private val callback: Callback = callback() + private val secureBackupEntryPointCallback = object : SecureBackupEntryPoint.Callback { override fun onDone() { lifecycleScope.launch { @@ -102,13 +104,15 @@ class FtueSessionVerificationFlowNode( } is NavTarget.UseAnotherDevice -> { outgoingVerificationEntryPoint.nodeBuilder(this, buildContext) - .params(OutgoingVerificationEntryPoint.Params( - showDeviceVerifiedScreen = true, - verificationRequest = VerificationRequest.Outgoing.CurrentSession, - )) + .params( + OutgoingVerificationEntryPoint.Params( + showDeviceVerifiedScreen = true, + verificationRequest = VerificationRequest.Outgoing.CurrentSession, + ) + ) .callback(object : OutgoingVerificationEntryPoint.Callback { override fun onDone() { - plugins().forEach { it.onDone() } + callback.onDone() } override fun onBack() { @@ -133,7 +137,7 @@ class FtueSessionVerificationFlowNode( .params(SecureBackupEntryPoint.Params(SecureBackupEntryPoint.InitialTarget.ResetIdentity)) .callback(object : SecureBackupEntryPoint.Callback { override fun onDone() { - plugins().forEach { it.onDone() } + callback.onDone() } }) .build() diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeNode.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeNode.kt index d5a0f91ed7..ac2aa09b22 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeNode.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeNode.kt @@ -12,12 +12,12 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.logout.api.direct.DirectLogoutView import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -35,7 +35,7 @@ class ChooseSelfVerificationModeNode( fun navigateToLearnMoreAboutEncryption() } - private val callback = plugins().first() + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { 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 7245db296e..eb3a8bd880 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 @@ -21,7 +21,6 @@ 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 import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push @@ -44,6 +43,7 @@ import io.element.android.features.reportroom.api.ReportRoomEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.appyx.launchMolecule +import io.element.android.libraries.architecture.callback import io.element.android.libraries.deeplink.api.usecase.InviteFriendsUseCase import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient @@ -78,6 +78,7 @@ class HomeFlowNode( buildContext = buildContext, plugins = plugins ) { + private val callback: HomeEntryPoint.Callback = callback() private val stateFlow = launchMolecule { presenter.present() } override fun onBuilt() { @@ -115,35 +116,11 @@ class HomeFlowNode( data class SelectNewOwnersWhenLeavingRoom(val roomId: RoomId) : NavTarget } - private fun onRoomClick(roomId: RoomId) { - plugins().forEach { it.navigateToRoom(roomId) } - } - - private fun onOpenSettings() { - plugins().forEach { it.navigateToSettings() } - } - - private fun onStartChatClick() { - plugins().forEach { it.navigateToCreateRoom() } - } - - private fun onSetUpRecoveryClick() { - plugins().forEach { it.navigateToSetUpRecovery() } - } - - private fun onSessionConfirmRecoveryKeyClick() { - plugins().forEach { it.navigateToEnterRecoveryKey() } - } - - private fun onRoomSettingsClick(roomId: RoomId) { - plugins().forEach { it.navigateToRoomSettings(roomId) } - } - - private fun onReportRoomClick(roomId: RoomId) { + private fun navigateToReportRoom(roomId: RoomId) { backstack.push(NavTarget.ReportRoom(roomId)) } - private fun onDeclineInviteAndBlockUserClick(roomSummary: RoomListRoomSummary) { + private fun navigateToDeclineInviteAndBlockUser(roomSummary: RoomListRoomSummary) { backstack.push(NavTarget.DeclineInviteAndBlockUser(roomSummary.toInviteData())) } @@ -153,12 +130,12 @@ class HomeFlowNode( inviteFriendsUseCase.execute(activity) } RoomListMenuAction.ReportBug -> { - plugins().forEach { it.navigateToBugReport() } + callback.navigateToBugReport() } } } - private fun onSelectNewOwnersWhenLeavingRoom(roomId: RoomId) { + private fun navigateToSelectNewOwnersWhenLeavingRoom(roomId: RoomId) { backstack.push(NavTarget.SelectNewOwnersWhenLeavingRoom(roomId)) } @@ -172,20 +149,20 @@ class HomeFlowNode( val activity = requireNotNull(LocalActivity.current) HomeView( homeState = state, - onRoomClick = this::onRoomClick, - onSettingsClick = this::onOpenSettings, - onStartChatClick = this::onStartChatClick, - onSetUpRecoveryClick = this::onSetUpRecoveryClick, - onConfirmRecoveryKeyClick = this::onSessionConfirmRecoveryKeyClick, - onRoomSettingsClick = this::onRoomSettingsClick, + onRoomClick = callback::navigateToRoom, + onSettingsClick = callback::navigateToSettings, + onStartChatClick = callback::navigateToCreateRoom, + onSetUpRecoveryClick = callback::navigateToSetUpRecovery, + onConfirmRecoveryKeyClick = callback::navigateToEnterRecoveryKey, + onRoomSettingsClick = callback::navigateToRoomSettings, onMenuActionClick = { onMenuActionClick(activity, it) }, - onReportRoomClick = this::onReportRoomClick, - onDeclineInviteAndBlockUser = this::onDeclineInviteAndBlockUserClick, + onReportRoomClick = ::navigateToReportRoom, + onDeclineInviteAndBlockUser = ::navigateToDeclineInviteAndBlockUser, modifier = modifier, acceptDeclineInviteView = { acceptDeclineInviteView.Render( state = state.roomListState.acceptDeclineInviteState, - onAcceptInviteSuccess = this::onRoomClick, + onAcceptInviteSuccess = callback::navigateToRoom, onDeclineInviteSuccess = { }, modifier = Modifier ) @@ -193,7 +170,7 @@ class HomeFlowNode( leaveRoomView = { leaveRoomRenderer.Render( state = state.roomListState.leaveRoomState, - onSelectNewOwners = this::onSelectNewOwnersWhenLeavingRoom, + onSelectNewOwners = ::navigateToSelectNewOwnersWhenLeavingRoom, modifier = Modifier ) } diff --git a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListNode.kt b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListNode.kt index 56faebe71b..00493a0489 100644 --- a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListNode.kt +++ b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListNode.kt @@ -12,12 +12,12 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.licenses.impl.model.DependencyLicenseItem +import io.element.android.libraries.architecture.callback @ContributesNode(AppScope::class) @AssistedInject @@ -33,10 +33,7 @@ class DependencyLicensesListNode( fun navigateToLicense(license: DependencyLicenseItem) } - private fun onOpenLicense(license: DependencyLicenseItem) { - plugins() - .forEach { it.navigateToLicense(license) } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -44,7 +41,7 @@ class DependencyLicensesListNode( DependencyLicensesListView( state = state, onBackClick = ::navigateUp, - onOpenLicense = ::onOpenLicense, + onOpenLicense = callback::navigateToLicense, ) } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsNode.kt index 2dcfb0524e..d7a87e4ff4 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsNode.kt @@ -12,10 +12,10 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -29,9 +29,7 @@ class LockScreenSettingsNode( fun navigateToSetupPin() } - private fun onChangePinClick() { - plugins().forEach { it.navigateToSetupPin() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -39,7 +37,7 @@ class LockScreenSettingsNode( LockScreenSettingsView( state = state, onBackClick = this::navigateUp, - onChangePinClick = this::onChangePinClick, + onChangePinClick = callback::navigateToSetupPin, modifier = modifier, ) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/LockScreenSetupFlowNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/LockScreenSetupFlowNode.kt index 263cc8ea54..92ffd94a87 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/LockScreenSetupFlowNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/LockScreenSetupFlowNode.kt @@ -14,7 +14,6 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.newRoot import dev.zacsweers.metro.Assisted @@ -27,6 +26,7 @@ import io.element.android.features.lockscreen.impl.setup.biometric.SetupBiometri import io.element.android.features.lockscreen.impl.setup.pin.SetupPinNode import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope import kotlinx.parcelize.Parcelize @@ -50,9 +50,7 @@ class LockScreenSetupFlowNode( fun onSetupDone() } - private fun onSetupDone() { - plugins().forEach { it.onSetupDone() } - } + private val callback: Callback = callback() sealed interface NavTarget : Parcelable { @Parcelize @@ -67,7 +65,7 @@ class LockScreenSetupFlowNode( if (biometricAuthenticatorManager.hasAvailableAuthenticator) { backstack.newRoot(NavTarget.Biometric) } else { - onSetupDone() + callback.onSetupDone() } } } @@ -91,7 +89,7 @@ class LockScreenSetupFlowNode( NavTarget.Biometric -> { val callback = object : SetupBiometricNode.Callback { override fun onBiometricSetupDone() { - onSetupDone() + callback.onSetupDone() } } createNode(buildContext, plugins = listOf(callback)) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricNode.kt index 64da1a321c..9ffd52e4bc 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricNode.kt @@ -13,10 +13,10 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -30,16 +30,14 @@ class SetupBiometricNode( fun onBiometricSetupDone() } - private fun onSetupDone() { - plugins().forEach { it.onBiometricSetupDone() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { val state = presenter.present() LaunchedEffect(state.isBiometricSetupDone) { if (state.isBiometricSetupDone) { - onSetupDone() + callback.onBiometricSetupDone() } } SetupBiometricView( diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt index fba460f6ee..b1d45c348c 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt @@ -13,10 +13,10 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -30,18 +30,14 @@ class PinUnlockNode( fun onUnlock() } - private fun onUnlock() { - plugins().forEach { - it.onUnlock() - } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { val state = presenter.present() LaunchedEffect(state.isUnlocked) { if (state.isUnlocked) { - onUnlock() + callback.onUnlock() } } PinUnlockView( diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt index ce85c617ef..208109d6f9 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt @@ -18,7 +18,6 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.push import com.bumble.appyx.navmodel.backstack.operation.singleTop @@ -41,6 +40,7 @@ import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTa import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs import io.element.android.libraries.matrix.api.auth.OidcDetails @@ -70,6 +70,7 @@ class LoginFlowNode( val loginHint: String?, ) : NodeInputs + private val callback: LoginEntryPoint.Callback = callback() private var activity: Activity? = null private var darkTheme: Boolean = false @@ -147,7 +148,7 @@ class LoginFlowNode( } override fun navigateToBugReport() { - plugins().forEach { it.navigateToBugReport() } + callback.navigateToBugReport() } override fun navigateToOidc(oidcDetails: OidcDetails) { diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderNode.kt index 80de8c6d71..b63b7cf011 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderNode.kt @@ -13,12 +13,12 @@ import androidx.compose.ui.platform.LocalContext import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.util.openLearnMorePage +import io.element.android.libraries.architecture.callback @ContributesNode(AppScope::class) @AssistedInject @@ -32,13 +32,7 @@ class ChangeAccountProviderNode( fun navigateToSearchAccountProvider() } - private fun onDone() { - plugins().forEach { it.onDone() } - } - - private fun onOtherClick() { - plugins().forEach { it.navigateToSearchAccountProvider() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -49,8 +43,8 @@ class ChangeAccountProviderNode( modifier = modifier, onBackClick = ::navigateUp, onLearnMoreClick = { openLearnMorePage(context) }, - onSuccess = ::onDone, - onOtherProviderClick = ::onOtherClick, + onSuccess = callback::onDone, + onOtherProviderClick = callback::navigateToSearchAccountProvider, ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt index 02203aaa72..2b65c2a70f 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt @@ -13,12 +13,12 @@ import androidx.compose.ui.platform.LocalContext import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.util.openLearnMorePage +import io.element.android.libraries.architecture.callback import io.element.android.libraries.matrix.api.auth.OidcDetails @ContributesNode(AppScope::class) @@ -34,17 +34,7 @@ class ChooseAccountProviderNode( fun navigateToCreateAccount(url: String) } - private fun onOidcDetails(oidcDetails: OidcDetails) { - plugins().forEach { it.navigateToOidc(oidcDetails) } - } - - private fun onLoginPasswordNeeded() { - plugins().forEach { it.navigateToLoginPassword() } - } - - private fun onCreateAccountContinue(url: String) { - plugins().forEach { it.navigateToCreateAccount(url) } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -54,10 +44,10 @@ class ChooseAccountProviderNode( state = state, modifier = modifier, onBackClick = ::navigateUp, - onOidcDetails = ::onOidcDetails, - onNeedLoginPassword = ::onLoginPasswordNeeded, + onOidcDetails = callback::navigateToOidc, + onNeedLoginPassword = callback::navigateToLoginPassword, onLearnMoreClick = { openLearnMorePage(context) }, - onCreateAccountContinue = ::onCreateAccountContinue, + onCreateAccountContinue = callback::navigateToCreateAccount, ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt index 52df30b4fb..e849d0d404 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt @@ -13,13 +13,13 @@ import androidx.compose.ui.platform.LocalContext import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.util.openLearnMorePage import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.matrix.api.auth.OidcDetails @@ -48,21 +48,7 @@ class ConfirmAccountProviderNode( fun navigateToChangeAccountProvider() } - private fun onOidcDetails(data: OidcDetails) { - plugins().forEach { it.navigateToOidc(data) } - } - - private fun onLoginPasswordNeeded() { - plugins().forEach { it.navigateToLoginPassword() } - } - - private fun onCreateAccountContinue(url: String) { - plugins().forEach { it.navigateToCreateAccount(url) } - } - - private fun onChangeAccountProvider() { - plugins().forEach { it.navigateToChangeAccountProvider() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -71,10 +57,10 @@ class ConfirmAccountProviderNode( ConfirmAccountProviderView( state = state, modifier = modifier, - onOidcDetails = ::onOidcDetails, - onNeedLoginPassword = ::onLoginPasswordNeeded, - onCreateAccountContinue = ::onCreateAccountContinue, - onChange = ::onChangeAccountProvider, + onOidcDetails = callback::navigateToOidc, + onNeedLoginPassword = callback::navigateToLoginPassword, + onCreateAccountContinue = callback::navigateToCreateAccount, + onChange = callback::navigateToChangeAccountProvider, onLearnMoreClick = { openLearnMorePage(context) }, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt index d9968af936..f90483778a 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt @@ -13,13 +13,13 @@ import androidx.compose.ui.platform.LocalContext import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.util.openLearnMorePage import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.matrix.api.auth.OidcDetails @@ -48,40 +48,13 @@ class OnBoardingNode( val loginHint: String?, ) : NodeInputs + private val callback: Callback = callback() private val params = inputs() private val presenter = presenterFactory.create( params = params, ) - private fun onSignIn(mustChooseAccountProvider: Boolean) { - plugins().forEach { it.navigateToSignInFlow(mustChooseAccountProvider) } - } - - private fun onSignUp() { - plugins().forEach { it.navigateToSignUpFlow() } - } - - private fun onSignInWithQrCode() { - plugins().forEach { it.navigateToQrCode() } - } - - private fun onReportProblem() { - plugins().forEach { it.navigateToBugReport() } - } - - private fun onOidcDetails(data: OidcDetails) { - plugins().forEach { it.navigateToOidc(data) } - } - - private fun onLoginPasswordNeeded() { - plugins().forEach { it.navigateToLoginPassword() } - } - - private fun onCreateAccountContinue(url: String) { - plugins().forEach { it.navigateToCreateAccount(url) } - } - @Composable override fun View(modifier: Modifier) { val state = presenter.present() @@ -89,14 +62,14 @@ class OnBoardingNode( OnBoardingView( state = state, modifier = modifier, - onSignIn = ::onSignIn, - onCreateAccount = ::onSignUp, - onSignInWithQrCode = ::onSignInWithQrCode, - onReportProblem = ::onReportProblem, - onOidcDetails = ::onOidcDetails, - onNeedLoginPassword = ::onLoginPasswordNeeded, + onSignIn = callback::navigateToSignInFlow, + onCreateAccount = callback::navigateToSignUpFlow, + onSignInWithQrCode = callback::navigateToQrCode, + onReportProblem = callback::navigateToBugReport, + onOidcDetails = callback::navigateToOidc, + onNeedLoginPassword = callback::navigateToLoginPassword, onLearnMoreClick = { openLearnMorePage(context) }, - onCreateAccountContinue = ::onCreateAccountContinue, + onCreateAccountContinue = callback::navigateToCreateAccount, onBackClick = ::navigateUp, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationNode.kt index 8b8d5b2dd5..7a3689dd3c 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationNode.kt @@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.di.QrCodeLoginScope +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs @ContributesNode(QrCodeLoginScope::class) @@ -29,17 +29,14 @@ class QrCodeConfirmationNode( fun onCancel() } + private val callback: Callback = callback() private val step = inputs() - private fun onCancel() { - plugins().forEach { it.onCancel() } - } - @Composable override fun View(modifier: Modifier) { QrCodeConfirmationView( step = step, - onCancel = ::onCancel, + onCancel = callback::onCancel, ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorNode.kt index 7b46c2e45c..294037a1f7 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorNode.kt @@ -12,12 +12,12 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.di.QrCodeLoginScope import io.element.android.features.login.impl.qrcode.QrCodeErrorScreenType +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.core.meta.BuildMeta @@ -32,10 +32,7 @@ class QrCodeErrorNode( fun onRetry() } - private fun onRetry() { - plugins().forEach { it.onRetry() } - } - + private val callback: Callback = callback() private val qrCodeErrorScreenType = inputs() @Composable @@ -44,7 +41,7 @@ class QrCodeErrorNode( modifier = modifier, errorScreenType = qrCodeErrorScreenType, appName = buildMeta.productionApplicationName, - onRetry = ::onRetry, + onRetry = callback::onRetry, ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroNode.kt index 951550fc22..719df8f423 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroNode.kt @@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.di.QrCodeLoginScope +import io.element.android.libraries.architecture.callback @ContributesNode(QrCodeLoginScope::class) @AssistedInject @@ -30,21 +30,15 @@ class QrCodeIntroNode( fun navigateToQrCodeScan() } - private fun onCancelClicked() { - plugins().forEach { it.cancel() } - } - - private fun onContinue() { - plugins().forEach { it.navigateToQrCodeScan() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { val state = presenter.present() QrCodeIntroView( state = state, - onBackClick = ::onCancelClicked, - onContinue = ::onContinue, + onBackClick = callback::cancel, + onContinue = callback::navigateToQrCodeScan, modifier = modifier ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanNode.kt index 554639c72a..44eab816c1 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanNode.kt @@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.di.QrCodeLoginScope +import io.element.android.libraries.architecture.callback import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData @ContributesNode(QrCodeLoginScope::class) @@ -31,21 +31,15 @@ class QrCodeScanNode( fun cancel() } - private fun onQrCodeDataReady(qrCodeLoginData: MatrixQrCodeLoginData) { - plugins().forEach { it.handleScannedCode(qrCodeLoginData) } - } - - private fun onCancelClicked() { - plugins().forEach { it.cancel() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { val state = presenter.present() QrCodeScanView( state = state, - onQrCodeDataReady = ::onQrCodeDataReady, - onBackClick = ::onCancelClicked, + onQrCodeDataReady = callback::handleScannedCode, + onBackClick = callback::cancel, modifier = modifier ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderNode.kt index dc6084032e..5def2bc830 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderNode.kt @@ -13,12 +13,12 @@ import androidx.compose.ui.platform.LocalContext import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.util.openLearnMorePage +import io.element.android.libraries.architecture.callback @ContributesNode(AppScope::class) @AssistedInject @@ -31,9 +31,7 @@ class SearchAccountProviderNode( fun onDone() } - private fun onDone() { - plugins().forEach { it.onDone() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -44,7 +42,7 @@ class SearchAccountProviderNode( modifier = modifier, onBackClick = ::navigateUp, onLearnMoreClick = { openLearnMorePage(context) }, - onSuccess = ::onDone, + onSuccess = callback::onDone, ) } } diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt index 3860850fcf..7cf717df68 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt @@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.logout.api.LogoutEntryPoint +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -26,16 +26,14 @@ class LogoutNode( @Assisted plugins: List, private val presenter: LogoutPresenter, ) : Node(buildContext, plugins = plugins) { - private fun onChangeRecoveryKeyClick() { - plugins().forEach { it.navigateToSecureBackup() } - } + private val callback: LogoutEntryPoint.Callback = callback() @Composable override fun View(modifier: Modifier) { val state = presenter.present() LogoutView( state = state, - onChangeRecoveryKeyClick = ::onChangeRecoveryKeyClick, + onChangeRecoveryKeyClick = callback::navigateToSecureBackup, onBackClick = ::navigateUp, modifier = modifier, ) 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 c9fe66191b..a65595788a 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 @@ -16,7 +16,6 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push @@ -55,6 +54,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.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.overlay.Overlay import io.element.android.libraries.architecture.overlay.operation.hide @@ -182,7 +182,7 @@ class MessagesFlowNode( data class Thread(val threadRootId: ThreadId, val focusedEventId: EventId?) : NavTarget } - private val callbacks = plugins() + private val callback: MessagesEntryPoint.Callback = callback() override fun onBuilt() { super.onBuilt() @@ -221,7 +221,7 @@ class MessagesFlowNode( is NavTarget.Messages -> { val callback = object : MessagesNode.Callback { override fun navigateToRoomDetails() { - callbacks.forEach { it.navigateToRoomDetails() } + callback.navigateToRoomDetails() } override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean { @@ -242,11 +242,11 @@ class MessagesFlowNode( } override fun navigateToRoomMemberDetails(userId: UserId) { - callbacks.forEach { it.navigateToRoomMemberDetails(userId) } + callback.navigateToRoomMemberDetails(userId) } override fun handlePermalinkClick(data: PermalinkData) { - callbacks.forEach { it.handlePermalinkClick(data, pushToBackstack = true) } + callback.handlePermalinkClick(data, pushToBackstack = true) } override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { @@ -317,7 +317,7 @@ class MessagesFlowNode( override fun forwardEvent(eventId: EventId) { // Need to go to the parent because of the overlay - callbacks.forEach { it.forwardEvent(eventId) } + callback.forwardEvent(eventId) } } mediaViewerEntryPoint.nodeBuilder(this, buildContext) @@ -352,7 +352,7 @@ class MessagesFlowNode( override fun onDone(roomIds: List) { backstack.pop() roomIds.singleOrNull()?.let { roomId -> - callbacks.forEach { it.navigateToRoom(roomId) } + callback.navigateToRoom(roomId) } } } @@ -400,7 +400,7 @@ class MessagesFlowNode( } override fun navigateToRoomMemberDetails(userId: UserId) { - callbacks.forEach { it.navigateToRoomMemberDetails(userId) } + callback.navigateToRoomMemberDetails(userId) } override fun viewInTimeline(eventId: EventId) { @@ -408,7 +408,7 @@ class MessagesFlowNode( } override fun handlePermalinkClick(data: PermalinkData.RoomLink) { - callbacks.forEach { it.handlePermalinkClick(data, pushToBackstack = !room.matches(data.roomIdOrAlias)) } + callback.handlePermalinkClick(data, pushToBackstack = !room.matches(data.roomIdOrAlias)) } override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { @@ -448,11 +448,11 @@ class MessagesFlowNode( } override fun navigateToRoomMemberDetails(userId: UserId) { - callbacks.forEach { it.navigateToRoomMemberDetails(userId) } + callback.navigateToRoomMemberDetails(userId) } override fun handlePermalinkClick(data: PermalinkData) { - callbacks.forEach { it.handlePermalinkClick(data, pushToBackstack = true) } + callback.handlePermalinkClick(data, pushToBackstack = true) } override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { @@ -502,7 +502,7 @@ class MessagesFlowNode( roomIdOrAlias = room.roomId.toRoomIdOrAlias(), eventId = eventId, ) - callbacks.forEach { it.handlePermalinkClick(permalinkData, pushToBackstack = false) } + callback.handlePermalinkClick(permalinkData, pushToBackstack = false) } private fun processEventClick( 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 bb2870b6af..9647ffca17 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 @@ -24,7 +24,6 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode @@ -48,8 +47,8 @@ import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTa 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.architecture.callback import io.element.android.libraries.architecture.inputs -import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.annotations.ApplicationContext @@ -93,13 +92,12 @@ class MessagesNode( private val knockRequestsBannerRenderer: KnockRequestsBannerRenderer, private val roomMemberModerationRenderer: RoomMemberModerationRenderer, ) : Node(buildContext, plugins = plugins), MessagesNavigator { - private val callbacks = plugins() - data class Inputs( val focusedEventId: EventId?, ) : NodeInputs private val inputs = inputs() + private val callback: Callback = callback() private val timelineController = TimelineController(room, room.liveTimeline) private val presenter = presenterFactory.create( @@ -143,32 +141,6 @@ class MessagesNode( ) } - private fun navigateToRoomDetails() { - callbacks.forEach { it.navigateToRoomDetails() } - } - - private fun navigateToPinnedMessagesList() { - callbacks.forEach { it.navigateToPinnedMessagesList() } - } - - private fun navigateToKnockRequestsList() { - callbacks.forEach { it.navigateToKnockRequestsList() } - } - - private fun onEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean { - // Note: cannot use `callbacks.all { it.onEventClick(event) }` because: - // - if callbacks is empty, it will return true and we want to return false. - // - if a callback returns false, the other callback will not be invoked. - return callbacks.takeIf { it.isNotEmpty() } - ?.map { it.handleEventClick(timelineMode, event) } - ?.all { it } - .orFalse() - } - - private fun navigateToRoomMemberDetails(userId: UserId) { - callbacks.forEach { it.navigateToRoomMemberDetails(userId) } - } - private fun onLinkClick( activity: Activity, darkTheme: Boolean, @@ -180,7 +152,7 @@ class MessagesNode( is PermalinkData.UserLink -> { // Open the room member profile, it will fallback to // the user profile if the user is not in the room - callbacks.forEach { it.navigateToRoomMemberDetails(permalink.userId) } + callback.navigateToRoomMemberDetails(permalink.userId) } is PermalinkData.RoomLink -> { handleRoomLinkClick(permalink, eventSink) @@ -211,28 +183,28 @@ class MessagesNode( displaySameRoomToast() } } else { - callbacks.forEach { it.handlePermalinkClick(roomLink) } + callback.handlePermalinkClick(roomLink) } } override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { - callbacks.forEach { it.navigateToEventDebugInfo(eventId, debugInfo) } + callback.navigateToEventDebugInfo(eventId, debugInfo) } override fun forwardEvent(eventId: EventId) { - callbacks.forEach { it.forwardEvent(eventId) } + callback.forwardEvent(eventId) } override fun navigateToReportMessage(eventId: EventId, senderId: UserId) { - callbacks.forEach { it.navigateToReportMessage(eventId, senderId) } + callback.navigateToReportMessage(eventId, senderId) } override fun navigateToEditPoll(eventId: EventId) { - callbacks.forEach { it.navigateToEditPoll(eventId) } + callback.navigateToEditPoll(eventId) } override fun navigateToPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) { - callbacks.forEach { it.navigateToPreviewAttachments(attachments, inReplyToEventId) } + callback.navigateToPreviewAttachments(attachments, inReplyToEventId) } override fun navigateToRoom(roomId: RoomId, eventId: EventId?, serverNames: List) { @@ -240,24 +212,12 @@ class MessagesNode( displaySameRoomToast() } else { val permalinkData = PermalinkData.RoomLink(roomId.toRoomIdOrAlias(), eventId, viaParameters = serverNames.toImmutableList()) - callbacks.forEach { it.handlePermalinkClick(permalinkData) } + callback.handlePermalinkClick(permalinkData) } } override fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) { - callbacks.forEach { it.navigateToThread(threadRootId, focusedEventId) } - } - - private fun navigateToSendLocation() { - callbacks.forEach { it.navigateToSendLocation() } - } - - private fun navigateToCreatePoll() { - callbacks.forEach { it.navigateToCreatePoll() } - } - - private fun navigateToRoomCall() { - callbacks.forEach { it.navigateToRoomCall(room.roomId) } + callback.navigateToThread(threadRootId, focusedEventId) } private fun displaySameRoomToast() { @@ -288,20 +248,20 @@ class MessagesNode( MessagesView( state = state, onBackClick = { state.eventSink(MessagesEvents.MarkAsFullyReadAndExit) }, - onRoomDetailsClick = ::navigateToRoomDetails, + onRoomDetailsClick = callback::navigateToRoomDetails, onEventContentClick = { isLive, event -> if (isLive) { - onEventClick(timelineController.mainTimelineMode(), event) + callback.handleEventClick(timelineController.mainTimelineMode(), event) } else { val detachedTimelineMode = timelineController.detachedTimelineMode() if (detachedTimelineMode != null) { - onEventClick(detachedTimelineMode, event) + callback.handleEventClick(detachedTimelineMode, event) } else { false } } }, - onUserDataClick = ::navigateToRoomMemberDetails, + onUserDataClick = callback::navigateToRoomMemberDetails, onLinkClick = { url, customTab -> onLinkClick( activity = activity, @@ -311,15 +271,15 @@ class MessagesNode( customTab = customTab, ) }, - onSendLocationClick = ::navigateToSendLocation, - onCreatePollClick = ::navigateToCreatePoll, - onJoinCallClick = ::navigateToRoomCall, - onViewAllPinnedMessagesClick = ::navigateToPinnedMessagesList, + onSendLocationClick = callback::navigateToSendLocation, + onCreatePollClick = callback::navigateToCreatePoll, + onJoinCallClick = { callback.navigateToRoomCall(room.roomId) }, + onViewAllPinnedMessagesClick = callback::navigateToPinnedMessagesList, modifier = modifier, knockRequestsBannerView = { knockRequestsBannerRenderer.View( modifier = Modifier, - onViewRequestsClick = ::navigateToKnockRequestsList, + onViewRequestsClick = callback::navigateToKnockRequestsList, ) }, ) @@ -327,7 +287,7 @@ class MessagesNode( state = state.roomMemberModerationState, onSelectAction = { action, target -> when (action) { - is ModerationAction.DisplayProfile -> navigateToRoomMemberDetails(target.userId) + is ModerationAction.DisplayProfile -> callback.navigateToRoomMemberDetails(target.userId) else -> state.roomMemberModerationState.eventSink(RoomMemberModerationEvents.ProcessAction(action, target)) } }, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt index 6d1c7b6027..130d9fe678 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt @@ -17,7 +17,6 @@ import androidx.compose.ui.platform.LocalView import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode @@ -27,6 +26,7 @@ import io.element.android.features.messages.impl.timeline.di.TimelineItemPresent import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.androidutils.system.copyToClipboard import io.element.android.libraries.androidutils.system.openUrlInExternalApp +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId @@ -34,7 +34,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.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo -import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.ui.strings.CommonStrings @ContributesNode(RoomScope::class) @@ -56,6 +55,7 @@ class PinnedMessagesListNode( fun handleForwardEventClick(eventId: EventId) } + private val callback: Callback = callback() private val presenter = presenterFactory.create( navigator = this, actionListPresenter = actionListPresenterFactory.create( @@ -63,25 +63,16 @@ class PinnedMessagesListNode( timelineMode = Timeline.Mode.PinnedEvents, ) ) - private val callbacks = plugins() - - private fun handleEventClick(event: TimelineItem.Event) { - return callbacks.forEach { it.handleEventClick(event) } - } - - private fun navigateToRoomMemberDetails(user: MatrixUser) { - callbacks.forEach { it.navigateToRoomMemberDetails(user.userId) } - } private fun onLinkClick(context: Context, url: String) { when (val permalink = permalinkParser.parse(url)) { is PermalinkData.UserLink -> { // Open the room member profile, it will fallback to // the user profile if the user is not in the room - callbacks.forEach { it.navigateToRoomMemberDetails(permalink.userId) } + callback.navigateToRoomMemberDetails(permalink.userId) } is PermalinkData.RoomLink -> { - callbacks.forEach { it.handlePermalinkClick(permalink) } + callback.handlePermalinkClick(permalink) } is PermalinkData.FallbackLink, is PermalinkData.RoomEmailInviteLink -> { @@ -91,15 +82,15 @@ class PinnedMessagesListNode( } override fun viewInTimeline(eventId: EventId) { - callbacks.forEach { it.viewInTimeline(eventId) } + callback.viewInTimeline(eventId) } override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { - callbacks.forEach { it.navigateToEventDebugInfo(eventId, debugInfo) } + callback.navigateToEventDebugInfo(eventId, debugInfo) } override fun forwardEvent(eventId: EventId) { - callbacks.forEach { it.handleForwardEventClick(eventId) } + callback.handleForwardEventClick(eventId) } @Composable @@ -113,8 +104,8 @@ class PinnedMessagesListNode( PinnedMessagesListView( state = state, onBackClick = ::navigateUp, - onEventClick = ::handleEventClick, - onUserDataClick = ::navigateToRoomMemberDetails, + onEventClick = callback::handleEventClick, + onUserDataClick = { callback.navigateToRoomMemberDetails(it.userId) }, onLinkClick = { link -> onLinkClick(context, link.url) }, onLinkLongClick = { view.performHapticFeedback( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt index 62138e99e0..5ce1b76956 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt @@ -22,7 +22,6 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode @@ -44,8 +43,8 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab import io.element.android.libraries.androidutils.system.openUrlInExternalApp import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs -import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.annotations.SessionCoroutineScope @@ -88,14 +87,13 @@ class ThreadedMessagesNode( private val permalinkParser: PermalinkParser, private val appNavigationStateService: AppNavigationStateService, ) : Node(buildContext, plugins = plugins), MessagesNavigator { - private val callbacks = plugins() - data class Inputs( val threadRootEventId: ThreadId, val focusedEventId: EventId?, ) : NodeInputs private val inputs = inputs() + private val callback: Callback = callback() // TODO use a loading state node to preload this instead of using `runBlocking` private val threadedTimeline = runBlocking { room.createTimeline(CreateTimelineParams.Threaded(threadRootEventId = inputs.threadRootEventId)).getOrThrow() } @@ -145,20 +143,6 @@ class ThreadedMessagesNode( ) } - private fun onEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean { - // Note: cannot use `callbacks.all { it.onEventClick(event) }` because: - // - if callbacks is empty, it will return true and we want to return false. - // - if a callback returns false, the other callback will not be invoked. - return callbacks.takeIf { it.isNotEmpty() } - ?.map { it.handleEventClick(timelineMode, event) } - ?.all { it } - .orFalse() - } - - private fun navigateToRoomMemberDetails(userId: UserId) { - callbacks.forEach { it.navigateToRoomMemberDetails(userId) } - } - private fun onLinkClick( activity: Activity, darkTheme: Boolean, @@ -170,7 +154,7 @@ class ThreadedMessagesNode( is PermalinkData.UserLink -> { // Open the room member profile, it will fallback to // the user profile if the user is not in the room - callbacks.forEach { it.navigateToRoomMemberDetails(permalink.userId) } + callback.navigateToRoomMemberDetails(permalink.userId) } is PermalinkData.RoomLink -> { handleRoomLinkClick(permalink, eventSink) @@ -204,53 +188,41 @@ class ThreadedMessagesNode( navigateUp() } } else { - callbacks.forEach { it.handlePermalinkClick(roomLink) } + callback.handlePermalinkClick(roomLink) } } override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { - callbacks.forEach { it.navigateToEventDebugInfo(eventId, debugInfo) } + callback.navigateToEventDebugInfo(eventId, debugInfo) } override fun forwardEvent(eventId: EventId) { - callbacks.forEach { it.handleForwardEventClick(eventId) } + callback.handleForwardEventClick(eventId) } override fun navigateToReportMessage(eventId: EventId, senderId: UserId) { - callbacks.forEach { it.navigateToReportMessage(eventId, senderId) } + callback.navigateToReportMessage(eventId, senderId) } override fun navigateToEditPoll(eventId: EventId) { - callbacks.forEach { it.navigateToEditPoll(eventId) } + callback.navigateToEditPoll(eventId) } override fun navigateToPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) { - callbacks.forEach { it.navigateToPreviewAttachments(attachments, inReplyToEventId) } + callback.navigateToPreviewAttachments(attachments, inReplyToEventId) } override fun navigateToRoom(roomId: RoomId, eventId: EventId?, serverNames: List) { val permalinkData = PermalinkData.RoomLink(roomId.toRoomIdOrAlias(), eventId, viaParameters = serverNames.toImmutableList()) - callbacks.forEach { it.handlePermalinkClick(permalinkData) } + callback.handlePermalinkClick(permalinkData) } override fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) { - callbacks.forEach { it.navigateToThread(threadRootId, focusedEventId) } + callback.navigateToThread(threadRootId, focusedEventId) } override fun onNavigateUp() = navigateUp() - private fun navigateToSendLocation() { - callbacks.forEach { it.navigateToSendLocation() } - } - - private fun navigateToCreatePoll() { - callbacks.forEach { it.navigateToCreatePoll() } - } - - private fun navigateToRoomCall() { - callbacks.forEach { it.navigateToRoomCall(room.roomId) } - } - @Composable override fun View(modifier: Modifier) { val activity = requireNotNull(LocalActivity.current) @@ -271,17 +243,17 @@ class ThreadedMessagesNode( onRoomDetailsClick = {}, onEventContentClick = { isLive, event -> if (isLive) { - onEventClick(timelineController.mainTimelineMode(), event) + callback.handleEventClick(timelineController.mainTimelineMode(), event) } else { val detachedTimelineMode = timelineController.detachedTimelineMode() if (detachedTimelineMode != null) { - onEventClick(detachedTimelineMode, event) + callback.handleEventClick(detachedTimelineMode, event) } else { false } } }, - onUserDataClick = this::navigateToRoomMemberDetails, + onUserDataClick = callback::navigateToRoomMemberDetails, onLinkClick = { url, customTab -> onLinkClick( activity = activity, @@ -291,9 +263,9 @@ class ThreadedMessagesNode( customTab = customTab, ) }, - onSendLocationClick = this::navigateToSendLocation, - onCreatePollClick = this::navigateToCreatePoll, - onJoinCallClick = this::navigateToRoomCall, + onSendLocationClick = callback::navigateToSendLocation, + onCreatePollClick = callback::navigateToCreatePoll, + onJoinCallClick = { callback.navigateToRoomCall(room.roomId) }, onViewAllPinnedMessagesClick = {}, modifier = modifier, knockRequestsBannerView = {}, diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryNode.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryNode.kt index 3ebabe5a2f..714e7763e6 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryNode.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryNode.kt @@ -12,10 +12,10 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.EventId @@ -33,16 +33,14 @@ class PollHistoryNode( fun navigateToEditPoll(pollStartEventId: EventId) } - private fun onEditPoll(pollStartEventId: EventId) { - plugins().forEach { it.navigateToEditPoll(pollStartEventId) } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { PollHistoryView( state = presenter.present(), modifier = modifier, - onEditPoll = ::onEditPoll, + onEditPoll = callback::navigateToEditPoll, goBack = this::navigateUp, ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt index 4903f9d2cf..8f3bc6eb5c 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt @@ -13,7 +13,6 @@ 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 com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push @@ -38,6 +37,7 @@ import io.element.android.features.preferences.impl.user.editprofile.EditUserPro import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.appyx.canPop +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.EventId @@ -116,20 +116,22 @@ class PreferencesFlowNode( data object OssLicenses : NavTarget } + private val callback: PreferencesEntryPoint.Callback = callback() + override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { NavTarget.Root -> { val callback = object : PreferencesRootNode.Callback { override fun navigateToAddAccount() { - plugins().forEach { it.navigateToAddAccount() } + callback.navigateToAddAccount() } override fun navigateToBugReport() { - plugins().forEach { it.navigateToBugReport() } + callback.navigateToBugReport() } override fun navigateToSecureBackup() { - plugins().forEach { it.navigateToSecureBackup() } + callback.navigateToSecureBackup() } override fun navigateToAnalyticsSettings() { @@ -241,7 +243,7 @@ class PreferencesFlowNode( } override fun navigateToEvent(roomId: RoomId, eventId: EventId) { - plugins().forEach { it.navigateToEvent(roomId, eventId) } + callback.navigateToEvent(roomId, eventId) } }) .build() @@ -249,7 +251,7 @@ class PreferencesFlowNode( is NavTarget.EditDefaultNotificationSetting -> { val callback = object : EditDefaultNotificationSettingNode.Callback { override fun navigateToRoomNotificationSettings(roomId: RoomId) { - plugins().forEach { it.navigateToRoomNotificationSettings(roomId) } + callback.navigateToRoomNotificationSettings(roomId) } } val input = EditDefaultNotificationSettingNode.Inputs(navTarget.isOneToOne) @@ -271,7 +273,7 @@ class PreferencesFlowNode( NavTarget.SignOut -> { val callBack: LogoutEntryPoint.Callback = object : LogoutEntryPoint.Callback { override fun navigateToSecureBackup() { - plugins().forEach { it.navigateToSecureBackup() } + callback.navigateToSecureBackup() } } logoutEntryPoint.nodeBuilder(this, buildContext) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutNode.kt index 564189442c..a4e67f0ef3 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutNode.kt @@ -19,6 +19,7 @@ import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -32,6 +33,8 @@ class AboutNode( fun navigateToOssLicenses() } + private val callback: Callback = callback() + private fun onElementLegalClick( activity: Activity, darkTheme: Boolean, @@ -51,9 +54,7 @@ class AboutNode( onElementLegalClick = { elementLegal -> onElementLegalClick(activity, isDark, elementLegal) }, - onOpenSourceLicensesClick = { - plugins.filterIsInstance().forEach { it.navigateToOssLicenses() } - }, + onOpenSourceLicensesClick = callback::navigateToOssLicenses, modifier = modifier ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt index 6f18fd44dc..385e99c3e3 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt @@ -14,10 +14,10 @@ import com.airbnb.android.showkase.models.Showkase import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.designsystem.showkase.getBrowserIntent import io.element.android.libraries.di.SessionScope @@ -32,11 +32,7 @@ class DeveloperSettingsNode( fun navigateToPushHistory() } - private val callbacks = plugins() - - private fun navigateToPushHistory() { - callbacks.forEach { it.navigateToPushHistory() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -51,7 +47,7 @@ class DeveloperSettingsNode( state = state, modifier = modifier, onOpenShowkase = ::openShowkase, - onPushHistoryClick = ::navigateToPushHistory, + onPushHistoryClick = callback::navigateToPushHistory, onBackClick = ::navigateUp ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt index 2b2879a4da..b2338c7cc2 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt @@ -12,10 +12,10 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -30,24 +30,16 @@ class NotificationSettingsNode( fun navigateToTroubleshootNotifications() } - private val callbacks = plugins() - - private fun navigateToEditDefaultNotificationSetting(isOneToOne: Boolean) { - callbacks.forEach { it.navigateToEditDefaultNotificationSetting(isOneToOne) } - } - - private fun navigateToTroubleshootNotifications() { - callbacks.forEach { it.navigateToTroubleshootNotifications() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { val state = presenter.present() NotificationSettingsView( state = state, - onOpenEditDefault = ::navigateToEditDefaultNotificationSetting, + onOpenEditDefault = callback::navigateToEditDefaultNotificationSetting, onBackClick = ::navigateUp, - onTroubleshootNotificationsClick = ::navigateToTroubleshootNotifications, + onTroubleshootNotificationsClick = callback::navigateToTroubleshootNotifications, modifier = modifier, ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt index e697089ad1..823b3657e0 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt @@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId @@ -36,20 +36,16 @@ class EditDefaultNotificationSettingNode( val isOneToOne: Boolean ) : NodeInputs + private val callback: Callback = callback() private val inputs = inputs() - private val callbacks = plugins() private val presenter = presenterFactory.create(inputs.isOneToOne) - private fun navigateToRoomNotificationSettings(roomId: RoomId) { - callbacks.forEach { it.navigateToRoomNotificationSettings(roomId) } - } - @Composable override fun View(modifier: Modifier) { val state = presenter.present() EditDefaultNotificationSettingView( state = state, - openRoomNotificationSettings = { navigateToRoomNotificationSettings(it) }, + openRoomNotificationSettings = callback::navigateToRoomNotificationSettings, onBackClick = ::navigateUp, modifier = modifier, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt index ba6edb97e0..4d3f482e7e 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt @@ -14,7 +14,6 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode @@ -22,6 +21,7 @@ import io.element.android.compound.theme.ElementTheme import io.element.android.features.logout.api.direct.DirectLogoutEvents import io.element.android.features.logout.api.direct.DirectLogoutView import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.user.MatrixUser @@ -50,37 +50,7 @@ class PreferencesRootNode( fun startAccountDeactivationFlow() } - private fun onAddAccount() { - plugins().forEach { it.navigateToAddAccount() } - } - - private fun onOpenBugReport() { - plugins().forEach { it.navigateToBugReport() } - } - - private fun onSecureBackupClick() { - plugins().forEach { it.navigateToSecureBackup() } - } - - private fun onOpenDeveloperSettings() { - plugins().forEach { it.navigateToDeveloperSettings() } - } - - private fun onOpenAdvancedSettings() { - plugins().forEach { it.navigateToAdvancedSettings() } - } - - private fun onOpenLabs() { - plugins().forEach { it.navigateToLabs() } - } - - private fun onOpenAnalytics() { - plugins().forEach { it.navigateToAnalyticsSettings() } - } - - private fun onOpenAbout() { - plugins().forEach { it.navigateToAbout() } - } + private val callback: Callback = callback() private fun onManageAccountClick( activity: Activity, @@ -96,30 +66,6 @@ class PreferencesRootNode( } } - private fun onOpenNotificationSettings() { - plugins().forEach { it.navigateToNotificationSettings() } - } - - private fun onOpenLockScreenSettings() { - plugins().forEach { it.navigateToLockScreenSettings() } - } - - private fun onOpenUserProfile(matrixUser: MatrixUser) { - plugins().forEach { it.navigateToUserProfile(matrixUser) } - } - - private fun onOpenBlockedUsers() { - plugins().forEach { it.navigateToBlockedUsers() } - } - - private fun onSignOutClick() { - plugins().forEach { it.startSignOutFlow() } - } - - private fun onOpenAccountDeactivation() { - plugins().forEach { it.startAccountDeactivationFlow() } - } - @Composable override fun View(modifier: Modifier) { val state = presenter.present() @@ -129,27 +75,27 @@ class PreferencesRootNode( state = state, modifier = modifier, onBackClick = this::navigateUp, - onAddAccountClick = this::onAddAccount, - onOpenRageShake = this::onOpenBugReport, - onOpenAnalytics = this::onOpenAnalytics, - onOpenAbout = this::onOpenAbout, - onSecureBackupClick = this::onSecureBackupClick, - onOpenDeveloperSettings = this::onOpenDeveloperSettings, - onOpenAdvancedSettings = this::onOpenAdvancedSettings, - onOpenLabs = this::onOpenLabs, + onAddAccountClick = callback::navigateToAddAccount, + onOpenRageShake = callback::navigateToBugReport, + onOpenAnalytics = callback::navigateToAnalyticsSettings, + onOpenAbout = callback::navigateToAbout, + onSecureBackupClick = callback::navigateToSecureBackup, + onOpenDeveloperSettings = callback::navigateToDeveloperSettings, + onOpenAdvancedSettings = callback::navigateToAdvancedSettings, + onOpenLabs = callback::navigateToLabs, onManageAccountClick = { onManageAccountClick(activity, it, isDark) }, - onOpenNotificationSettings = this::onOpenNotificationSettings, - onOpenLockScreenSettings = this::onOpenLockScreenSettings, - onOpenUserProfile = this::onOpenUserProfile, - onOpenBlockedUsers = this::onOpenBlockedUsers, + onOpenNotificationSettings = callback::navigateToNotificationSettings, + onOpenLockScreenSettings = callback::navigateToLockScreenSettings, + onOpenUserProfile = callback::navigateToUserProfile, + onOpenBlockedUsers = callback::navigateToBlockedUsers, onSignOutClick = { if (state.directLogoutState.canDoDirectSignOut) { state.directLogoutState.eventSink(DirectLogoutEvents.Logout(ignoreSdkError = false)) } else { - onSignOutClick() + callback.startSignOutFlow() } }, - onDeactivateClick = this::onOpenAccountDeactivation + onDeactivateClick = callback::startAccountDeactivationFlow ) directLogoutView.Render(state = state.directLogoutState) diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFlowNode.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFlowNode.kt index 4e1f599237..2893fa4d09 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFlowNode.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFlowNode.kt @@ -13,7 +13,6 @@ 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 com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push @@ -25,6 +24,7 @@ import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint import io.element.android.features.viewfolder.api.ViewFolderEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import kotlinx.parcelize.Parcelize @@ -42,9 +42,7 @@ class BugReportFlowNode( buildContext = buildContext, plugins = plugins ) { - private fun onDone() { - plugins().forEach { it.onDone() } - } + private val callback: BugReportEntryPoint.Callback = callback() sealed interface NavTarget : Parcelable { @Parcelize @@ -61,7 +59,7 @@ class BugReportFlowNode( NavTarget.Root -> { val callback = object : BugReportNode.Callback { override fun onDone() { - this@BugReportFlowNode.onDone() + callback.onDone() } override fun navigateToViewLogs(basePath: String) { diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportNode.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportNode.kt index 1d3974d166..b270f0cf3b 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportNode.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportNode.kt @@ -13,13 +13,13 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.rageshake.api.reporter.BugReporter import io.element.android.libraries.androidutils.system.toast +import io.element.android.libraries.architecture.callback import io.element.android.libraries.ui.strings.CommonStrings @ContributesNode(AppScope::class) @@ -35,13 +35,7 @@ class BugReportNode( fun navigateToViewLogs(basePath: String) } - private fun onViewLogs(basePath: String) { - plugins().forEach { it.navigateToViewLogs(basePath) } - } - - private fun onDone() { - plugins().forEach { it.onDone() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -53,12 +47,12 @@ class BugReportNode( onBackClick = { navigateUp() }, onSuccess = { activity?.toast(CommonStrings.common_report_submitted) - onDone() + callback.onDone() }, onViewLogs = { // Force a logcat dump bugReporter.saveLogCat() - onViewLogs(bugReporter.logDirectory().absolutePath) + callback.navigateToViewLogs(bugReporter.logDirectory().absolutePath) } ) } diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt index d7b3242def..656017b484 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt @@ -12,14 +12,13 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias @ContributesNode(SessionScope::class) @AssistedInject @@ -28,22 +27,19 @@ class RoomAliasResolverNode( @Assisted plugins: List, presenterFactory: RoomAliasResolverPresenter.Factory, ) : Node(buildContext, plugins = plugins) { + private val callback: RoomAliasResolverEntryPoint.Callback = callback() private val inputs = inputs() private val presenter = presenterFactory.create( inputs.roomAlias ) - private fun onAliasResolved(data: ResolvedRoomAlias) { - plugins().forEach { it.onAliasResolved(data) } - } - @Composable override fun View(modifier: Modifier) { val state = presenter.present() RoomAliasResolverView( state = state, - onSuccess = ::onAliasResolved, + onSuccess = callback::onAliasResolved, onBackClick = ::navigateUp, modifier = modifier ) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index 895fd9dd59..8ca5745ad1 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -16,7 +16,6 @@ import androidx.lifecycle.coroutineScope 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 com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push @@ -45,6 +44,7 @@ import io.element.android.features.userprofile.shared.UserProfileNodeHelper import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint import io.element.android.libraries.architecture.BackstackWithOverlayBox import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.overlay.operation.hide import io.element.android.libraries.architecture.overlay.operation.show @@ -146,6 +146,8 @@ class RoomDetailsFlowNode( data object SelectNewOwnersWhenLeaving : NavTarget } + private val callback: RoomDetailsEntryPoint.Callback = callback() + override fun onBuilt() { super.onBuilt() whenChildrenAttached { @@ -260,7 +262,7 @@ class RoomDetailsFlowNode( val input = RoomNotificationSettingsNode.RoomNotificationSettingInput(navTarget.showUserDefinedSettingStyle) val callback = object : RoomNotificationSettingsNode.Callback { override fun navigateToGlobalNotificationSettings() { - plugins().forEach { it.navigateToGlobalNotificationSettings() } + callback.navigateToGlobalNotificationSettings() } } createNode(buildContext, listOf(input, callback)) @@ -273,7 +275,7 @@ class RoomDetailsFlowNode( } override fun navigateToRoom(roomId: RoomId) { - plugins().forEach { it.navigateToRoom(roomId, emptyList()) } + callback.navigateToRoom(roomId, emptyList()) } override fun startCall(dmRoomId: RoomId) { @@ -323,13 +325,11 @@ class RoomDetailsFlowNode( roomIdOrAlias = room.roomId.toRoomIdOrAlias(), eventId = eventId, ) - plugins().forEach { - it.handlePermalinkClick(permalinkData, pushToBackstack = false) - } + callback.handlePermalinkClick(permalinkData, pushToBackstack = false) } override fun forward(eventId: EventId) { - plugins().forEach { it.startForwardEventFlow(eventId) } + callback.startForwardEventFlow(eventId) } } mediaGalleryEntryPoint.nodeBuilder(this, buildContext) @@ -350,15 +350,15 @@ class RoomDetailsFlowNode( override fun navigateToRoomMemberDetails(userId: UserId) = Unit override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) { - plugins().forEach { it.handlePermalinkClick(data, pushToBackstack) } + callback.handlePermalinkClick(data, pushToBackstack) } override fun forwardEvent(eventId: EventId) { - plugins().forEach { it.startForwardEventFlow(eventId) } + callback.startForwardEventFlow(eventId) } override fun navigateToRoom(roomId: RoomId) { - plugins().forEach { it.navigateToRoom(roomId, emptyList()) } + callback.navigateToRoom(roomId, emptyList()) } } return messagesEntryPoint.nodeBuilder(this, buildContext) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt index c439c2ce49..4af3d454d9 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt @@ -18,7 +18,6 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen @@ -26,6 +25,7 @@ import io.element.android.annotations.ContributesNode import io.element.android.features.leaveroom.api.LeaveRoomRenderer import io.element.android.libraries.androidutils.system.startSharePlainTextIntent import io.element.android.libraries.architecture.appyx.launchMolecule +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.BaseRoom @@ -63,7 +63,7 @@ class RoomDetailsNode( fun navigateToSelectNewOwnersWhenLeaving() } - private val callback = plugins().single() + private val callback: Callback = callback() init { lifecycle.subscribe( diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListNode.kt index 27a0bceb66..092326928a 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListNode.kt @@ -13,7 +13,6 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen @@ -21,6 +20,7 @@ import io.element.android.annotations.ContributesNode import io.element.android.features.roommembermoderation.api.ModerationAction import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents import io.element.android.features.roommembermoderation.api.RoomMemberModerationRenderer +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.UserId import io.element.android.services.analytics.api.AnalyticsService @@ -39,7 +39,7 @@ class RoomMemberListNode( fun navigateToInviteMembers() } - private val callbacks = plugins() + private val callback: Callback = callback() init { lifecycle.subscribe( @@ -50,15 +50,11 @@ class RoomMemberListNode( } override fun openRoomMemberDetails(roomMemberId: UserId) { - callbacks.forEach { - it.navigateToRoomMemberDetails(roomMemberId) - } + callback.navigateToRoomMemberDetails(roomMemberId) } override fun openInviteMembers() { - callbacks.forEach { - it.navigateToInviteMembers() - } + callback.navigateToInviteMembers() } override fun exitRoomMemberList() { diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsNode.kt index ec78bf56af..ab4b288803 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsNode.kt @@ -13,12 +13,12 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.RoomScope import io.element.android.services.analytics.api.AnalyticsService @@ -34,17 +34,16 @@ class RoomNotificationSettingsNode( data class RoomNotificationSettingInput( val showUserDefinedSettingStyle: Boolean ) : NodeInputs + interface Callback : Plugin { fun navigateToGlobalNotificationSettings() } - private val inputs = inputs() - private val callbacks = plugins() - private fun navigateToGlobalNotificationSettings() { - callbacks.forEach { it.navigateToGlobalNotificationSettings() } - } + private val callback: Callback = callback() + private val inputs = inputs() private val presenter = presenterFactory.create(inputs.showUserDefinedSettingStyle) + init { lifecycle.subscribe( onResume = { @@ -59,8 +58,8 @@ class RoomNotificationSettingsNode( RoomNotificationSettingsView( state = state, modifier = modifier, - onShowGlobalNotifications = this::navigateToGlobalNotificationSettings, - onBackClick = this::navigateUp, + onShowGlobalNotifications = callback::navigateToGlobalNotificationSettings, + onBackClick = ::navigateUp, ) } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt index a430b0f6a5..29394398d3 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt @@ -14,10 +14,10 @@ import androidx.lifecycle.lifecycleScope import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.room.RoomMember @@ -45,7 +45,7 @@ class RolesAndPermissionsNode( override fun onBackClick() {} } - private val callback = plugins().first() + private val callback: Callback = callback() @Stable private val navigator = object : RolesAndPermissionsNavigator by callback { diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryNode.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryNode.kt index 5bc492b7a8..ec01ea3ba6 100644 --- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryNode.kt +++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryNode.kt @@ -12,12 +12,11 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode -import io.element.android.features.roomdirectory.api.RoomDescription import io.element.android.features.roomdirectory.api.RoomDirectoryEntryPoint +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -27,18 +26,14 @@ class RoomDirectoryNode( @Assisted plugins: List, private val presenter: RoomDirectoryPresenter, ) : Node(buildContext, plugins = plugins) { - private fun onResultClick(roomDescription: RoomDescription) { - plugins().forEach { - it.navigateToRoom(roomDescription) - } - } + private val callback: RoomDirectoryEntryPoint.Callback = callback() @Composable override fun View(modifier: Modifier) { val state = presenter.present() RoomDirectoryView( state = state, - onResultClick = ::onResultClick, + onResultClick = callback::navigateToRoom, onBackClick = ::navigateUp, modifier = modifier ) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt index 63f6282dc8..586f37b664 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt @@ -13,7 +13,6 @@ 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 com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push @@ -29,6 +28,7 @@ import io.element.android.features.securebackup.impl.setup.SecureBackupSetupNode import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.appyx.canPop +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope import kotlinx.parcelize.Parcelize @@ -71,7 +71,7 @@ class SecureBackupFlowNode( data object ResetIdentity : NavTarget } - private val callbacks = plugins() + private val callback: SecureBackupEntryPoint.Callback = callback() override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { @@ -116,7 +116,7 @@ class SecureBackupFlowNode( if (backstack.canPop()) { backstack.pop() } else { - callbacks.forEach { it.onDone() } + callback.onDone() } } } @@ -125,7 +125,7 @@ class SecureBackupFlowNode( is NavTarget.ResetIdentity -> { val callback = object : ResetIdentityFlowNode.Callback { override fun onDone() { - callbacks.forEach { it.onDone() } + callback.onDone() } } createNode(buildContext, listOf(callback)) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt index 77d1fe8f32..ba4848ca82 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt @@ -12,10 +12,10 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -29,7 +29,7 @@ class SecureBackupEnterRecoveryKeyNode( fun onEnterRecoveryKeySuccess() } - private val callback = plugins().first() + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt index dfc9425ebe..5f94d556c7 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt @@ -20,7 +20,6 @@ import androidx.lifecycle.LifecycleOwner 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 com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted @@ -33,6 +32,7 @@ import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTa import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.di.SessionScope @@ -63,6 +63,8 @@ class ResetIdentityFlowNode( fun onDone() } + private val callback: Callback = callback() + sealed interface NavTarget : Parcelable { @Parcelize data object Root : NavTarget @@ -86,7 +88,7 @@ class ResetIdentityFlowNode( cancelResetJob() resetIdentityFlowManager.whenResetIsDone { - plugins().forEach { it.onDone() } + callback.onDone() } } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootNode.kt index 8267242f97..aee266b249 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootNode.kt @@ -15,6 +15,7 @@ import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -27,8 +28,8 @@ class ResetIdentityRootNode( fun onContinue() } + private val callback: Callback = callback() private val presenter = ResetIdentityRootPresenter() - private val callback: Callback = plugins.filterIsInstance().first() @Composable override fun View(modifier: Modifier) { diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootNode.kt index e8d13a3d38..6b29bb6928 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootNode.kt @@ -14,11 +14,11 @@ import androidx.compose.ui.platform.UriHandler import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.appconfig.LearnMoreConfig +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -38,21 +38,7 @@ class SecureBackupRootNode( fun navigateToEnterRecoveryKey() } - private fun onSetupClick() { - plugins().forEach { it.navigateToSetup() } - } - - private fun onChangeClick() { - plugins().forEach { it.navigateToChange() } - } - - private fun onDisableClick() { - plugins().forEach { it.navigateToDisable() } - } - - private fun onConfirmRecoveryKeyClick() { - plugins().forEach { it.navigateToEnterRecoveryKey() } - } + private val callback: Callback = callback() private fun onLearnMoreClick(uriHandler: UriHandler) { uriHandler.openUri(LearnMoreConfig.SECURE_BACKUP_URL) @@ -65,10 +51,10 @@ class SecureBackupRootNode( SecureBackupRootView( state = state, onBackClick = ::navigateUp, - onSetupClick = ::onSetupClick, - onChangeClick = ::onChangeClick, - onDisableClick = ::onDisableClick, - onConfirmRecoveryKeyClick = ::onConfirmRecoveryKeyClick, + onSetupClick = callback::navigateToSetup, + onChangeClick = callback::navigateToChange, + onDisableClick = callback::navigateToDisable, + onConfirmRecoveryKeyClick = callback::navigateToEnterRecoveryKey, onLearnMoreClick = { onLearnMoreClick(uriHandler) }, modifier = modifier, ) diff --git a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareNode.kt b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareNode.kt index 12d2f5bff5..c36f5cc97d 100644 --- a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareNode.kt +++ b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareNode.kt @@ -23,6 +23,7 @@ import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.share.api.ShareEntryPoint import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId @@ -52,7 +53,7 @@ class ShareNode( private val inputs = inputs() private val presenter = presenterFactory.create(inputs.intent) - private val callbacks = plugins.filterIsInstance() + private val callback: ShareEntryPoint.Callback = callback() override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { val callback = object : RoomSelectEntryPoint.Callback { @@ -61,7 +62,7 @@ class ShareNode( } override fun onCancel() { - onShareDone(emptyList()) + callback.onDone(emptyList()) } } @@ -82,12 +83,8 @@ class ShareNode( val state = presenter.present() ShareView( state = state, - onShareSuccess = ::onShareDone, + onShareSuccess = callback::onDone, ) } } - - private fun onShareDone(roomIds: List) { - callbacks.forEach { it.onDone(roomIds) } - } } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt index fb3de4a9be..1ef496d319 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt @@ -28,6 +28,7 @@ import io.element.android.features.space.impl.leave.LeaveSpaceNode import io.element.android.features.space.impl.root.SpaceNode import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.DependencyInjectionGraphOwner @@ -52,7 +53,7 @@ class SpaceFlowNode( plugins = plugins, ), DependencyInjectionGraphOwner { private val inputs: SpaceEntryPoint.Inputs = inputs() - private val callback = plugins.filterIsInstance().single() + private val callback: SpaceEntryPoint.Callback = callback() private val spaceRoomList = spaceService.spaceRoomList(inputs.roomId) override val graph = graphFactory.create(spaceRoomList) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceNode.kt index 28481651eb..174fa71ee8 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceNode.kt @@ -22,6 +22,7 @@ import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteV import io.element.android.features.space.impl.di.SpaceFlowScope import io.element.android.libraries.androidutils.R import io.element.android.libraries.androidutils.system.startSharePlainTextIntent +import io.element.android.libraries.architecture.callback import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.spaces.SpaceRoomList @@ -46,7 +47,7 @@ class SpaceNode( fun startLeaveSpaceFlow() } - private val callback = plugins.filterIsInstance().single() + private val callback: Callback = callback() private fun onShareRoom(context: Context) = lifecycleScope.launch { matrixClient.getRoom(spaceRoomList.roomId)?.use { room -> diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatFlowNode.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatFlowNode.kt index e875dcc748..55ff1d46f9 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatFlowNode.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatFlowNode.kt @@ -16,7 +16,6 @@ import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.navigation.transition.JumpToEndTransitionHandler 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 import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject @@ -29,6 +28,7 @@ import io.element.android.features.startchat.impl.root.StartChatNode import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.OverlayView +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId @@ -60,15 +60,12 @@ class StartChatFlowNode( data object JoinByAddress : NavTarget } + private val callback: StartChatEntryPoint.Callback = callback() private val navigator = DefaultStartChatNavigator( backstack = backstack, overlay = overlay, - openRoom = { roomIdOrAlias, viaServers -> - plugins().forEach { it.onRoomCreated(roomIdOrAlias, viaServers) } - }, - openRoomDirectory = { - plugins().forEach { it.navigateToRoomDirectory() } - } + openRoom = callback::onRoomCreated, + openRoomDirectory = callback::navigateToRoomDirectory, ) override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt index c9df5024be..45b1e95f14 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt @@ -13,7 +13,6 @@ 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 com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push @@ -28,6 +27,7 @@ import io.element.android.features.userprofile.shared.UserProfileNodeHelper import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope @@ -67,6 +67,7 @@ class UserProfileFlowNode( data class VerifyUser(val userId: UserId) : NavTarget } + private val callback: UserProfileEntryPoint.Callback = callback() private val inputs = inputs() override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { @@ -78,7 +79,7 @@ class UserProfileFlowNode( } override fun navigateToRoom(roomId: RoomId) { - plugins().forEach { it.navigateToRoom(roomId) } + callback.navigateToRoom(roomId) } override fun startCall(dmRoomId: RoomId) { diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationNode.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationNode.kt index a17054b9e6..fed3014925 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationNode.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationNode.kt @@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope @@ -28,13 +28,14 @@ class IncomingVerificationNode( presenterFactory: IncomingVerificationPresenter.Factory, ) : Node(buildContext, plugins = plugins), IncomingVerificationNavigator { + private val callback: IncomingVerificationEntryPoint.Callback = callback() private val presenter = presenterFactory.create( verificationRequest = inputs().verificationRequest, navigator = this, ) override fun onFinish() { - plugins().forEach { it.onDone() } + callback.onDone() } @Composable diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationNode.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationNode.kt index c5a04f7834..fa5ba8d3f8 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationNode.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationNode.kt @@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope @@ -27,8 +27,7 @@ class OutgoingVerificationNode( @Assisted plugins: List, presenterFactory: OutgoingVerificationPresenter.Factory, ) : Node(buildContext, plugins = plugins) { - private val callback = plugins().first() - + private val callback: OutgoingVerificationEntryPoint.Callback = callback() private val inputs = inputs() private val presenter = presenterFactory.create( diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileNode.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileNode.kt index 41369dda07..e474073e6f 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileNode.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileNode.kt @@ -12,12 +12,12 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs @ContributesNode(AppScope::class) @@ -36,6 +36,7 @@ class ViewFileNode( fun onBackClick() } + private val callback: Callback = callback() private val inputs: Inputs = inputs() private val presenter = presenterFactory.create( @@ -43,17 +44,13 @@ class ViewFileNode( name = inputs.name, ) - private fun onBackClick() { - plugins().forEach { it.onBackClick() } - } - @Composable override fun View(modifier: Modifier) { val state = presenter.present() ViewFileView( state = state, modifier = modifier, - onBackClick = ::onBackClick, + onBackClick = callback::onBackClick, ) } } diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderNode.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderNode.kt index b7c6fe1bd1..faea81c084 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderNode.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderNode.kt @@ -12,13 +12,13 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.viewfolder.impl.model.Item import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs @ContributesNode(AppScope::class) @@ -38,6 +38,7 @@ class ViewFolderNode( fun navigateToItem(item: Item) } + private val callback: Callback = callback() private val inputs: Inputs = inputs() private val presenter = presenterFactory.create( @@ -45,22 +46,14 @@ class ViewFolderNode( path = inputs.path, ) - private fun onBackClick() { - plugins().forEach { it.onBackClick() } - } - - private fun onNavigateTo(item: Item) { - plugins().forEach { it.navigateToItem(item) } - } - @Composable override fun View(modifier: Modifier) { val state = presenter.present() ViewFolderView( state = state, modifier = modifier, - onNavigateTo = ::onNavigateTo, - onBackClick = ::onBackClick, + onNavigateTo = callback::navigateToItem, + onBackClick = callback::onBackClick, ) } } diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/ViewFolderFlowNode.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/ViewFolderFlowNode.kt index 3c7935464b..4695576f9d 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/ViewFolderFlowNode.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/ViewFolderFlowNode.kt @@ -13,7 +13,6 @@ 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 com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push @@ -28,6 +27,7 @@ import io.element.android.features.viewfolder.impl.model.Item import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs import kotlinx.parcelize.Parcelize @@ -65,6 +65,7 @@ class ViewFolderFlowNode( val rootPath: String, ) : NodeInputs + private val callback: ViewFolderEntryPoint.Callback = callback() private val inputs: Inputs = inputs() override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { @@ -108,7 +109,7 @@ class ViewFolderFlowNode( ): Node { val callback: ViewFolderNode.Callback = object : ViewFolderNode.Callback { override fun onBackClick() { - onDone() + callback.onDone() } override fun navigateToItem(item: Item) { @@ -133,8 +134,4 @@ class ViewFolderFlowNode( override fun View(modifier: Modifier) { BackstackView() } - - private fun onDone() { - plugins().forEach { it.onDone() } - } } diff --git a/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectNode.kt b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectNode.kt index f0a543340c..c1a0fbff65 100644 --- a/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectNode.kt +++ b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectNode.kt @@ -17,7 +17,7 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.accountselect.api.AccountSelectEntryPoint -import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.architecture.callback @ContributesNode(AppScope::class) @AssistedInject @@ -26,23 +26,15 @@ class AccountSelectNode( @Assisted plugins: List, private val presenter: AccountSelectPresenter, ) : Node(buildContext, plugins = plugins) { - private val callbacks = plugins.filterIsInstance() - - private fun onDismiss() { - callbacks.forEach { it.onCancel() } - } - - private fun onAccountSelected(sessionId: SessionId) { - callbacks.forEach { it.onAccountSelected(sessionId) } - } + private val callback: AccountSelectEntryPoint.Callback = callback() @Composable override fun View(modifier: Modifier) { val state = presenter.present() AccountSelectView( state = state, - onDismiss = ::onDismiss, - onSelectAccount = ::onAccountSelected, + onDismiss = callback::onCancel, + onSelectAccount = callback::onAccountSelected, modifier = modifier, ) } diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeCallback.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeCallback.kt new file mode 100644 index 0000000000..5054f62290 --- /dev/null +++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeCallback.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2023, 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.architecture + +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import com.bumble.appyx.core.plugin.plugins + +inline fun Node.callback(): I { + return requireNotNull(plugins().singleOrNull()) { "Make sure to actually pass a Callback plugin to your node" } +} diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryNode.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryNode.kt index ff1359b672..d784f972d0 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryNode.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryNode.kt @@ -13,10 +13,10 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.mediaviewer.impl.gallery.di.LocalMediaItemPresenterFactories @@ -43,28 +43,14 @@ class MediaGalleryNode( fun forward(eventId: EventId) } - private fun onBackClick() { - plugins().forEach { - it.onBackClick() - } - } + private val callback: Callback = callback() override fun onViewInTimelineClick(eventId: EventId) { - plugins().forEach { - it.viewInTimeline(eventId) - } + callback.viewInTimeline(eventId) } override fun onForwardClick(eventId: EventId) { - plugins().forEach { - it.forward(eventId) - } - } - - private fun onItemClick(item: MediaItem.Event) { - plugins().forEach { - it.showItem(item) - } + callback.forward(eventId) } @Composable @@ -75,8 +61,8 @@ class MediaGalleryNode( val state = presenter.present() MediaGalleryView( state = state, - onBackClick = ::onBackClick, - onItemClick = ::onItemClick, + onBackClick = callback::onBackClick, + onItemClick = callback::showItem, modifier = modifier, ) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryFlowNode.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryFlowNode.kt index b72a3fbd46..a57b9203f4 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryFlowNode.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryFlowNode.kt @@ -13,13 +13,13 @@ 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 com.bumble.appyx.navmodel.backstack.BackStack import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.BackstackWithOverlayBox import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.overlay.Overlay import io.element.android.libraries.architecture.overlay.operation.hide @@ -70,38 +70,22 @@ class MediaGalleryFlowNode( ) : NavTarget } - private fun onBackClick() { - plugins().forEach { - it.onBackClick() - } - } - - private fun onViewInTimeline(eventId: EventId) { - plugins().forEach { - it.viewInTimeline(eventId) - } - } - - private fun forwardEvent(eventId: EventId) { - plugins().forEach { - it.forward(eventId) - } - } + private val callback: MediaGalleryEntryPoint.Callback = callback() override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { NavTarget.Root -> { val callback = object : MediaGalleryNode.Callback { override fun onBackClick() { - this@MediaGalleryFlowNode.onBackClick() + callback.onBackClick() } override fun viewInTimeline(eventId: EventId) { - this@MediaGalleryFlowNode.onViewInTimeline(eventId) + callback.viewInTimeline(eventId) } override fun forward(eventId: EventId) { - forwardEvent(eventId) + callback.forward(eventId) } override fun showItem(item: MediaItem.Event) { @@ -132,12 +116,12 @@ class MediaGalleryFlowNode( } override fun viewInTimeline(eventId: EventId) { - this@MediaGalleryFlowNode.onViewInTimeline(eventId) + callback.viewInTimeline(eventId) } override fun forwardEvent(eventId: EventId) { // Need to go to the parent because of the overlay - this@MediaGalleryFlowNode.forwardEvent(eventId) + callback.forward(eventId) } } mediaViewerEntryPoint.nodeBuilder(this, buildContext) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNode.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNode.kt index a62784555a..79a95c4648 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNode.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNode.kt @@ -15,7 +15,6 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode @@ -23,6 +22,7 @@ import io.element.android.compound.colors.SemanticColorsLightDark import io.element.android.compound.theme.ForcedDarkElementTheme import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.viewfolder.api.TextFileViewer +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.audio.api.AudioFocus import io.element.android.libraries.core.coroutine.CoroutineDispatchers @@ -57,28 +57,19 @@ class MediaViewerNode( private val enterpriseService: EnterpriseService, ) : Node(buildContext, plugins = plugins), MediaViewerNavigator { + private val callback: MediaViewerEntryPoint.Callback = callback() private val inputs = inputs() - private fun onDone() { - plugins().forEach { - it.onDone() - } - } - override fun onViewInTimelineClick(eventId: EventId) { - plugins().forEach { - it.viewInTimeline(eventId) - } + callback.viewInTimeline(eventId) } override fun onForwardClick(eventId: EventId) { - plugins().forEach { - it.forwardEvent(eventId) - } + callback.forwardEvent(eventId) } override fun onItemDeleted() { - onDone() + callback.onDone() } private val mediaGallerySource = if (inputs.mode == MediaViewerEntryPoint.MediaViewerMode.SingleMedia) { @@ -153,7 +144,7 @@ class MediaViewerNode( textFileViewer = textFileViewer, modifier = modifier, audioFocus = audioFocus, - onBackClick = ::onDone, + onBackClick = callback::onDone, ) } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/IgnoredUsersTest.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/IgnoredUsersTest.kt index 93e855c94d..e753e4d9e0 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/IgnoredUsersTest.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/IgnoredUsersTest.kt @@ -62,7 +62,7 @@ class IgnoredUsersTest( coroutineScope: CoroutineScope, navigator: NotificationTroubleshootNavigator, ) { - navigator.openIgnoredUsers() + navigator.navigateToBlockedUsers() } override suspend fun reset() = delegate.reset() diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/IgnoredUsersTestTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/IgnoredUsersTestTest.kt index e38baa7321..eebfd9d15b 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/IgnoredUsersTestTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/IgnoredUsersTestTest.kt @@ -39,7 +39,7 @@ class IgnoredUsersTestTest { ) val openIgnoredUsersResult = lambdaRecorder {} val navigator = object : NotificationTroubleshootNavigator { - override fun openIgnoredUsers() = openIgnoredUsersResult() + override fun navigateToBlockedUsers() = openIgnoredUsersResult() } sut.quickFix( coroutineScope = backgroundScope, diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt index e72c45ed22..5c05dcf6ec 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt @@ -16,9 +16,9 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.roomselect.api.RoomSelectEntryPoint import io.element.android.libraries.roomselect.api.RoomSelectMode @@ -35,24 +35,15 @@ class RoomSelectNode( private val inputs: Inputs = inputs() private val presenter = presenterFactory.create(inputs.mode) - - private val callbacks = plugins.filterIsInstance() - - private fun onDismiss() { - callbacks.forEach { it.onCancel() } - } - - private fun onRoomSelected(roomIds: List) { - callbacks.forEach { it.onRoomSelected(roomIds) } - } + private val callback: RoomSelectEntryPoint.Callback = callback() @Composable override fun View(modifier: Modifier) { val state = presenter.present() RoomSelectView( state = state, - onDismiss = ::onDismiss, - onSubmit = ::onRoomSelected, + onDismiss = callback::onCancel, + onSubmit = callback::onRoomSelected, modifier = modifier ) } diff --git a/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/test/NotificationTroubleshootNavigator.kt b/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/test/NotificationTroubleshootNavigator.kt index 0cce358072..75c8a83ae7 100644 --- a/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/test/NotificationTroubleshootNavigator.kt +++ b/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/test/NotificationTroubleshootNavigator.kt @@ -8,5 +8,5 @@ package io.element.android.libraries.troubleshoot.api.test interface NotificationTroubleshootNavigator { - fun openIgnoredUsers() + fun navigateToBlockedUsers() } diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsNode.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsNode.kt index 9ab1156d49..22ccda67ce 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsNode.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsNode.kt @@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope import io.element.android.libraries.troubleshoot.api.NotificationTroubleShootEntryPoint import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator @@ -31,20 +31,13 @@ class TroubleshootNotificationsNode( factory: TroubleshootNotificationsPresenter.Factory, ) : Node(buildContext, plugins = plugins), NotificationTroubleshootNavigator { + private val callback: NotificationTroubleShootEntryPoint.Callback = callback() private val presenter = factory.create( navigator = this, ) - private fun onDone() { - plugins().forEach { - it.onDone() - } - } - - override fun openIgnoredUsers() { - plugins().forEach { - it.navigateToBlockedUsers() - } + override fun navigateToBlockedUsers() { + callback.navigateToBlockedUsers() } @Composable @@ -53,7 +46,7 @@ class TroubleshootNotificationsNode( val state = presenter.present() TroubleshootNotificationsView( state = state, - onBackClick = ::onDone, + onBackClick = callback::onDone, modifier = modifier, ) } diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryNode.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryNode.kt index e8b17bf2ca..532c2230ee 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryNode.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryNode.kt @@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId @@ -31,16 +31,10 @@ class PushHistoryNode( presenterFactory: PushHistoryPresenter.Factory, private val screenTracker: ScreenTracker, ) : Node(buildContext, plugins = plugins), PushHistoryNavigator { - private fun onDone() { - plugins().forEach { - it.onDone() - } - } + private val callback: PushHistoryEntryPoint.Callback = callback() override fun navigateTo(roomId: RoomId, eventId: EventId) { - plugins().forEach { - it.navigateToEvent(roomId, eventId) - } + callback.navigateToEvent(roomId, eventId) } private val presenter = presenterFactory.create(this) @@ -51,7 +45,7 @@ class PushHistoryNode( val state = presenter.present() PushHistoryView( state = state, - onBackClick = ::onDone, + onBackClick = callback::onDone, modifier = modifier, ) } diff --git a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenterTest.kt b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenterTest.kt index f8be5a5696..751328578d 100644 --- a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenterTest.kt +++ b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenterTest.kt @@ -180,7 +180,7 @@ private fun createTroubleshootTestSuite( internal fun createTroubleshootNotificationsPresenter( navigator: NotificationTroubleshootNavigator = object : NotificationTroubleshootNavigator { - override fun openIgnoredUsers() = lambdaError() + override fun navigateToBlockedUsers() = lambdaError() }, troubleshootTestSuite: TroubleshootTestSuite = createTroubleshootTestSuite(), ): TroubleshootNotificationsPresenter { diff --git a/libraries/troubleshoot/test/src/main/kotlin/io/element/android/libraries/troubleshoot/test/FakeNotificationTroubleshootNavigator.kt b/libraries/troubleshoot/test/src/main/kotlin/io/element/android/libraries/troubleshoot/test/FakeNotificationTroubleshootNavigator.kt index 63445e5a3e..ea736b6a7b 100644 --- a/libraries/troubleshoot/test/src/main/kotlin/io/element/android/libraries/troubleshoot/test/FakeNotificationTroubleshootNavigator.kt +++ b/libraries/troubleshoot/test/src/main/kotlin/io/element/android/libraries/troubleshoot/test/FakeNotificationTroubleshootNavigator.kt @@ -13,5 +13,5 @@ import io.element.android.tests.testutils.lambda.lambdaError class FakeNotificationTroubleshootNavigator( private val openIgnoredUsersResult: () -> Unit = { lambdaError() }, ) : NotificationTroubleshootNavigator { - override fun openIgnoredUsers() = openIgnoredUsersResult() + override fun navigateToBlockedUsers() = openIgnoredUsersResult() }