Ensure a Callback and only one is provided in the Plugin. Also reduce boilerplate code in Nodes.

This commit is contained in:
Benoit Marty
2025-10-30 09:14:41 +01:00
committed by Benoit Marty
parent 596b7f00fc
commit 5197154f54
76 changed files with 374 additions and 741 deletions

View File

@@ -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.Node
import com.bumble.appyx.core.node.ParentNode import com.bumble.appyx.core.node.ParentNode
import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.appnav.di.SessionGraphFactory import io.element.android.appnav.di.SessionGraphFactory
import io.element.android.libraries.architecture.NodeInputs 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.createNode
import io.element.android.libraries.architecture.inputs import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.DependencyInjectionGraphOwner import io.element.android.libraries.di.DependencyInjectionGraphOwner
@@ -60,6 +60,8 @@ class LoggedInAppScopeFlowNode(
fun navigateToAddAccount() fun navigateToAddAccount()
} }
private val callback: Callback = callback()
@Parcelize @Parcelize
object NavTarget : Parcelable object NavTarget : Parcelable
@@ -82,11 +84,11 @@ class LoggedInAppScopeFlowNode(
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
val callback = object : LoggedInFlowNode.Callback { val callback = object : LoggedInFlowNode.Callback {
override fun navigateToBugReport() { override fun navigateToBugReport() {
plugins<Callback>().forEach { it.navigateToBugReport() } callback.navigateToBugReport()
} }
override fun navigateToAddAccount() { override fun navigateToAddAccount() {
plugins<Callback>().forEach { it.navigateToAddAccount() } callback.navigateToAddAccount()
} }
} }
return createNode<LoggedInFlowNode>(buildContext, listOf(callback)) return createNode<LoggedInFlowNode>(buildContext, listOf(callback))

View File

@@ -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.navigation.model.permanent.PermanentNavModel
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.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.BackStack.State.ACTIVE import com.bumble.appyx.navmodel.backstack.BackStack.State.ACTIVE
import com.bumble.appyx.navmodel.backstack.BackStack.State.CREATED 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.features.verifysession.api.IncomingVerificationEntryPoint
import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode 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.createNode
import io.element.android.libraries.architecture.waitForChildAttached import io.element.android.libraries.architecture.waitForChildAttached
import io.element.android.libraries.architecture.waitForNavTargetAttached import io.element.android.libraries.architecture.waitForNavTargetAttached
@@ -152,6 +152,7 @@ class LoggedInFlowNode(
fun navigateToAddAccount() fun navigateToAddAccount()
} }
private val callback: Callback = callback()
private val loggedInFlowProcessor = LoggedInEventProcessor( private val loggedInFlowProcessor = LoggedInEventProcessor(
snackbarDispatcher = snackbarDispatcher, snackbarDispatcher = snackbarDispatcher,
roomMembershipObserver = matrixClient.roomMembershipObserver, roomMembershipObserver = matrixClient.roomMembershipObserver,
@@ -329,7 +330,7 @@ class LoggedInFlowNode(
} }
override fun navigateToBugReport() { override fun navigateToBugReport() {
plugins<Callback>().forEach { it.navigateToBugReport() } callback.navigateToBugReport()
} }
} }
homeEntryPoint homeEntryPoint
@@ -396,11 +397,11 @@ class LoggedInFlowNode(
is NavTarget.Settings -> { is NavTarget.Settings -> {
val callback = object : PreferencesEntryPoint.Callback { val callback = object : PreferencesEntryPoint.Callback {
override fun navigateToAddAccount() { override fun navigateToAddAccount() {
plugins<Callback>().forEach { it.navigateToAddAccount() } callback.navigateToAddAccount()
} }
override fun navigateToBugReport() { override fun navigateToBugReport() {
plugins<Callback>().forEach { it.navigateToBugReport() } callback.navigateToAddAccount()
} }
override fun navigateToSecureBackup() { override fun navigateToSecureBackup() {

View File

@@ -18,7 +18,6 @@ import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Assisted 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.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.NodeInputs 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.inputs
import io.element.android.libraries.designsystem.utils.ForceOrientationInMobileDevices import io.element.android.libraries.designsystem.utils.ForceOrientationInMobileDevices
import io.element.android.libraries.designsystem.utils.ScreenOrientation import io.element.android.libraries.designsystem.utils.ScreenOrientation
@@ -58,6 +58,7 @@ class NotLoggedInFlowNode(
fun navigateToBugReport() fun navigateToBugReport()
} }
private val callback: Callback = callback()
private val inputs = inputs<Params>() private val inputs = inputs<Params>()
override fun onBuilt() { override fun onBuilt() {
@@ -79,7 +80,7 @@ class NotLoggedInFlowNode(
NavTarget.Root -> { NavTarget.Root -> {
val callback = object : LoginEntryPoint.Callback { val callback = object : LoginEntryPoint.Callback {
override fun navigateToBugReport() { override fun navigateToBugReport() {
plugins<Callback>().forEach { it.navigateToBugReport() } callback.navigateToBugReport()
} }
} }
loginEntryPoint loginEntryPoint

View File

@@ -12,10 +12,10 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode 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.di.SessionScope
@ContributesNode(SessionScope::class) @ContributesNode(SessionScope::class)
@@ -32,18 +32,14 @@ class LoggedInNode(
fun navigateToNotificationTroubleshoot() fun navigateToNotificationTroubleshoot()
} }
private fun navigateToNotificationTroubleshoot() { private val callback: Callback = callback()
plugins<Callback>().forEach {
it.navigateToNotificationTroubleshoot()
}
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val loggedInState = loggedInPresenter.present() val loggedInState = loggedInPresenter.present()
LoggedInView( LoggedInView(
state = loggedInState, state = loggedInState,
navigateToNotificationTroubleshoot = ::navigateToNotificationTroubleshoot, navigateToNotificationTroubleshoot = callback::navigateToNotificationTroubleshoot,
modifier = modifier modifier = modifier
) )
} }

View File

@@ -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.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.NodeInputs 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.inputs
import io.element.android.libraries.architecture.waitForChildAttached import io.element.android.libraries.architecture.waitForChildAttached
import io.element.android.libraries.di.DependencyInjectionGraphOwner import io.element.android.libraries.di.DependencyInjectionGraphOwner
@@ -87,7 +88,7 @@ class JoinedRoomLoadedFlowNode(
) : NodeInputs ) : NodeInputs
private val inputs: Inputs = inputs() private val inputs: Inputs = inputs()
private val callbacks = plugins.filterIsInstance<Callback>() private val callback: Callback = callback()
override val graph = roomGraphFactory.create(inputs.room) override val graph = roomGraphFactory.create(inputs.room)
init { init {
@@ -124,15 +125,15 @@ class JoinedRoomLoadedFlowNode(
private fun createRoomDetailsNode(buildContext: BuildContext, initialTarget: RoomDetailsEntryPoint.InitialTarget): Node { private fun createRoomDetailsNode(buildContext: BuildContext, initialTarget: RoomDetailsEntryPoint.InitialTarget): Node {
val callback = object : RoomDetailsEntryPoint.Callback { val callback = object : RoomDetailsEntryPoint.Callback {
override fun navigateToGlobalNotificationSettings() { override fun navigateToGlobalNotificationSettings() {
callbacks.forEach { it.navigateToGlobalNotificationSettings() } callback.navigateToGlobalNotificationSettings()
} }
override fun navigateToRoom(roomId: RoomId, serverNames: List<String>) { override fun navigateToRoom(roomId: RoomId, serverNames: List<String>) {
callbacks.forEach { it.navigateToRoom(roomId, serverNames) } callback.navigateToRoom(roomId, serverNames)
} }
override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) { override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) {
callbacks.forEach { it.handlePermalinkClick(data, pushToBackstack) } callback.handlePermalinkClick(data, pushToBackstack)
} }
override fun startForwardEventFlow(eventId: EventId) { override fun startForwardEventFlow(eventId: EventId) {
@@ -172,7 +173,7 @@ class JoinedRoomLoadedFlowNode(
override fun onDone(roomIds: List<RoomId>) { override fun onDone(roomIds: List<RoomId>) {
backstack.pop() backstack.pop()
roomIds.singleOrNull()?.let { roomId -> 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 { private fun createSpaceNode(buildContext: BuildContext): Node {
val callback = object : SpaceEntryPoint.Callback { val callback = object : SpaceEntryPoint.Callback {
override fun navigateToRoom(roomId: RoomId, viaParameters: List<String>) { override fun navigateToRoom(roomId: RoomId, viaParameters: List<String>) {
callbacks.forEach { it.navigateToRoom(roomId, viaParameters) } callback.navigateToRoom(roomId, viaParameters)
} }
override fun navigateToRoomDetails() { override fun navigateToRoomDetails() {
@@ -218,7 +219,7 @@ class JoinedRoomLoadedFlowNode(
} }
override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) { override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) {
callbacks.forEach { it.handlePermalinkClick(data, pushToBackstack) } callback.handlePermalinkClick(data, pushToBackstack)
} }
override fun forwardEvent(eventId: EventId) { override fun forwardEvent(eventId: EventId) {
@@ -226,7 +227,7 @@ class JoinedRoomLoadedFlowNode(
} }
override fun navigateToRoom(roomId: RoomId) { override fun navigateToRoom(roomId: RoomId) {
callbacks.forEach { it.navigateToRoom(roomId, emptyList()) } callback.navigateToRoom(roomId, emptyList())
} }
} }
val params = MessagesEntryPoint.Params( val params = MessagesEntryPoint.Params(

View File

@@ -13,7 +13,6 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.replace import com.bumble.appyx.navmodel.backstack.operation.replace
import dev.zacsweers.metro.Assisted 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.features.createroom.impl.configureroom.ConfigureRoomNode
import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode 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.createNode
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
@@ -42,9 +42,7 @@ class CreateRoomFlowNode(
buildContext = buildContext, buildContext = buildContext,
plugins = plugins plugins = plugins
) { ) {
private fun onRoomCreated(roomId: RoomId) { private val callback: CreateRoomEntryPoint.Callback = callback()
plugins<CreateRoomEntryPoint.Callback>().forEach { it.onRoomCreated(roomId) }
}
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
return when (navTarget) { return when (navTarget) {
@@ -60,7 +58,7 @@ class CreateRoomFlowNode(
val inputs = AddPeopleNode.Inputs(navTarget.roomId) val inputs = AddPeopleNode.Inputs(navTarget.roomId)
val callback: AddPeopleNode.Callback = object : AddPeopleNode.Callback { val callback: AddPeopleNode.Callback = object : AddPeopleNode.Callback {
override fun onFinish() { override fun onFinish() {
onRoomCreated(navTarget.roomId) callback.onRoomCreated(navTarget.roomId)
} }
} }
createNode<AddPeopleNode>(buildContext, plugins = listOf(inputs, callback)) createNode<AddPeopleNode>(buildContext, plugins = listOf(inputs, callback))

View File

@@ -12,13 +12,13 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.invitepeople.api.InvitePeoplePresenter import io.element.android.features.invitepeople.api.InvitePeoplePresenter
import io.element.android.features.invitepeople.api.InvitePeopleRenderer import io.element.android.features.invitepeople.api.InvitePeopleRenderer
import io.element.android.libraries.architecture.NodeInputs 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.inputs
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
@@ -39,10 +39,7 @@ class AddPeopleNode(
fun onFinish() fun onFinish()
} }
private fun onFinish() { private val callback: Callback = callback()
plugins<Callback>().forEach { it.onFinish() }
}
private val roomId = inputs<Inputs>().roomId private val roomId = inputs<Inputs>().roomId
private val invitePeoplePresenter = invitePeoplePresenterFactory.create( private val invitePeoplePresenter = invitePeoplePresenterFactory.create(
joinedRoom = null, joinedRoom = null,
@@ -54,7 +51,7 @@ class AddPeopleNode(
val state = invitePeoplePresenter.present() val state = invitePeoplePresenter.present()
AddPeopleView( AddPeopleView(
state = state, state = state,
onFinish = ::onFinish, onFinish = callback::onFinish,
) { ) {
invitePeopleRenderer.Render(state, Modifier) invitePeopleRenderer.Render(state, Modifier)
} }

View File

@@ -13,11 +13,11 @@ import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
import io.element.android.annotations.ContributesNode 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.di.SessionScope
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.api.AnalyticsService
@@ -42,9 +42,7 @@ class ConfigureRoomNode(
) )
} }
private fun onCreateRoomSuccess(roomId: RoomId) { private val callback: Callback = callback()
plugins<Callback>().forEach { it.onCreateRoomSuccess(roomId) }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
@@ -53,7 +51,7 @@ class ConfigureRoomNode(
state = state, state = state,
modifier = modifier, modifier = modifier,
onBackClick = this::navigateUp, onBackClick = this::navigateUp,
onCreateRoomSuccess = ::onCreateRoomSuccess, onCreateRoomSuccess = callback::onCreateRoomSuccess,
) )
} }
} }

View File

@@ -22,6 +22,7 @@ import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.forward.api.ForwardEntryPoint import io.element.android.features.forward.api.ForwardEntryPoint
import io.element.android.libraries.architecture.NodeInputs 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.inputs
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
@@ -55,8 +56,8 @@ class ForwardMessagesNode(
) : NodeInputs ) : NodeInputs
private val inputs = inputs<Inputs>() private val inputs = inputs<Inputs>()
private val callback: ForwardEntryPoint.Callback = callback()
private val presenter = presenterFactory.create(inputs.eventId.value, inputs.timelineProvider) private val presenter = presenterFactory.create(inputs.eventId.value, inputs.timelineProvider)
private val callbacks = plugins.filterIsInstance<ForwardEntryPoint.Callback>()
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
val callback = object : RoomSelectEntryPoint.Callback { val callback = object : RoomSelectEntryPoint.Callback {
@@ -65,7 +66,7 @@ class ForwardMessagesNode(
} }
override fun onCancel() { override fun onCancel() {
onForwardDone(emptyList()) callback.onDone(emptyList())
} }
} }
@@ -86,12 +87,8 @@ class ForwardMessagesNode(
val state = presenter.present() val state = presenter.present()
ForwardMessagesView( ForwardMessagesView(
state = state, state = state,
onForwardSuccess = ::onForwardDone, onForwardSuccess = callback::onDone,
) )
} }
} }
private fun onForwardDone(roomIds: List<RoomId>) {
callbacks.forEach { it.onDone(roomIds) }
}
} }

View File

@@ -15,7 +15,6 @@ import androidx.lifecycle.lifecycleScope
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.newRoot import com.bumble.appyx.navmodel.backstack.operation.newRoot
import com.bumble.appyx.navmodel.backstack.operation.pop 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.features.verifysession.api.OutgoingVerificationEntryPoint
import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode 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.createNode
import io.element.android.libraries.designsystem.utils.OpenUrlInTabView import io.element.android.libraries.designsystem.utils.OpenUrlInTabView
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
@@ -69,6 +69,8 @@ class FtueSessionVerificationFlowNode(
fun onDone() fun onDone()
} }
private val callback: Callback = callback()
private val secureBackupEntryPointCallback = object : SecureBackupEntryPoint.Callback { private val secureBackupEntryPointCallback = object : SecureBackupEntryPoint.Callback {
override fun onDone() { override fun onDone() {
lifecycleScope.launch { lifecycleScope.launch {
@@ -102,13 +104,15 @@ class FtueSessionVerificationFlowNode(
} }
is NavTarget.UseAnotherDevice -> { is NavTarget.UseAnotherDevice -> {
outgoingVerificationEntryPoint.nodeBuilder(this, buildContext) outgoingVerificationEntryPoint.nodeBuilder(this, buildContext)
.params(OutgoingVerificationEntryPoint.Params( .params(
showDeviceVerifiedScreen = true, OutgoingVerificationEntryPoint.Params(
verificationRequest = VerificationRequest.Outgoing.CurrentSession, showDeviceVerifiedScreen = true,
)) verificationRequest = VerificationRequest.Outgoing.CurrentSession,
)
)
.callback(object : OutgoingVerificationEntryPoint.Callback { .callback(object : OutgoingVerificationEntryPoint.Callback {
override fun onDone() { override fun onDone() {
plugins<Callback>().forEach { it.onDone() } callback.onDone()
} }
override fun onBack() { override fun onBack() {
@@ -133,7 +137,7 @@ class FtueSessionVerificationFlowNode(
.params(SecureBackupEntryPoint.Params(SecureBackupEntryPoint.InitialTarget.ResetIdentity)) .params(SecureBackupEntryPoint.Params(SecureBackupEntryPoint.InitialTarget.ResetIdentity))
.callback(object : SecureBackupEntryPoint.Callback { .callback(object : SecureBackupEntryPoint.Callback {
override fun onDone() { override fun onDone() {
plugins<Callback>().forEach { it.onDone() } callback.onDone()
} }
}) })
.build() .build()

View File

@@ -12,12 +12,12 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.logout.api.direct.DirectLogoutView import io.element.android.features.logout.api.direct.DirectLogoutView
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.callback
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
@ContributesNode(SessionScope::class) @ContributesNode(SessionScope::class)
@@ -35,7 +35,7 @@ class ChooseSelfVerificationModeNode(
fun navigateToLearnMoreAboutEncryption() fun navigateToLearnMoreAboutEncryption()
} }
private val callback = plugins<Callback>().first() private val callback: Callback = callback()
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {

View File

@@ -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.node.node import com.bumble.appyx.core.node.node
import com.bumble.appyx.core.plugin.Plugin 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
import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.pop
import com.bumble.appyx.navmodel.backstack.operation.push 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.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.appyx.launchMolecule 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.deeplink.api.usecase.InviteFriendsUseCase
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
@@ -78,6 +78,7 @@ class HomeFlowNode(
buildContext = buildContext, buildContext = buildContext,
plugins = plugins plugins = plugins
) { ) {
private val callback: HomeEntryPoint.Callback = callback()
private val stateFlow = launchMolecule { presenter.present() } private val stateFlow = launchMolecule { presenter.present() }
override fun onBuilt() { override fun onBuilt() {
@@ -115,35 +116,11 @@ class HomeFlowNode(
data class SelectNewOwnersWhenLeavingRoom(val roomId: RoomId) : NavTarget data class SelectNewOwnersWhenLeavingRoom(val roomId: RoomId) : NavTarget
} }
private fun onRoomClick(roomId: RoomId) { private fun navigateToReportRoom(roomId: RoomId) {
plugins<HomeEntryPoint.Callback>().forEach { it.navigateToRoom(roomId) }
}
private fun onOpenSettings() {
plugins<HomeEntryPoint.Callback>().forEach { it.navigateToSettings() }
}
private fun onStartChatClick() {
plugins<HomeEntryPoint.Callback>().forEach { it.navigateToCreateRoom() }
}
private fun onSetUpRecoveryClick() {
plugins<HomeEntryPoint.Callback>().forEach { it.navigateToSetUpRecovery() }
}
private fun onSessionConfirmRecoveryKeyClick() {
plugins<HomeEntryPoint.Callback>().forEach { it.navigateToEnterRecoveryKey() }
}
private fun onRoomSettingsClick(roomId: RoomId) {
plugins<HomeEntryPoint.Callback>().forEach { it.navigateToRoomSettings(roomId) }
}
private fun onReportRoomClick(roomId: RoomId) {
backstack.push(NavTarget.ReportRoom(roomId)) backstack.push(NavTarget.ReportRoom(roomId))
} }
private fun onDeclineInviteAndBlockUserClick(roomSummary: RoomListRoomSummary) { private fun navigateToDeclineInviteAndBlockUser(roomSummary: RoomListRoomSummary) {
backstack.push(NavTarget.DeclineInviteAndBlockUser(roomSummary.toInviteData())) backstack.push(NavTarget.DeclineInviteAndBlockUser(roomSummary.toInviteData()))
} }
@@ -153,12 +130,12 @@ class HomeFlowNode(
inviteFriendsUseCase.execute(activity) inviteFriendsUseCase.execute(activity)
} }
RoomListMenuAction.ReportBug -> { RoomListMenuAction.ReportBug -> {
plugins<HomeEntryPoint.Callback>().forEach { it.navigateToBugReport() } callback.navigateToBugReport()
} }
} }
} }
private fun onSelectNewOwnersWhenLeavingRoom(roomId: RoomId) { private fun navigateToSelectNewOwnersWhenLeavingRoom(roomId: RoomId) {
backstack.push(NavTarget.SelectNewOwnersWhenLeavingRoom(roomId)) backstack.push(NavTarget.SelectNewOwnersWhenLeavingRoom(roomId))
} }
@@ -172,20 +149,20 @@ class HomeFlowNode(
val activity = requireNotNull(LocalActivity.current) val activity = requireNotNull(LocalActivity.current)
HomeView( HomeView(
homeState = state, homeState = state,
onRoomClick = this::onRoomClick, onRoomClick = callback::navigateToRoom,
onSettingsClick = this::onOpenSettings, onSettingsClick = callback::navigateToSettings,
onStartChatClick = this::onStartChatClick, onStartChatClick = callback::navigateToCreateRoom,
onSetUpRecoveryClick = this::onSetUpRecoveryClick, onSetUpRecoveryClick = callback::navigateToSetUpRecovery,
onConfirmRecoveryKeyClick = this::onSessionConfirmRecoveryKeyClick, onConfirmRecoveryKeyClick = callback::navigateToEnterRecoveryKey,
onRoomSettingsClick = this::onRoomSettingsClick, onRoomSettingsClick = callback::navigateToRoomSettings,
onMenuActionClick = { onMenuActionClick(activity, it) }, onMenuActionClick = { onMenuActionClick(activity, it) },
onReportRoomClick = this::onReportRoomClick, onReportRoomClick = ::navigateToReportRoom,
onDeclineInviteAndBlockUser = this::onDeclineInviteAndBlockUserClick, onDeclineInviteAndBlockUser = ::navigateToDeclineInviteAndBlockUser,
modifier = modifier, modifier = modifier,
acceptDeclineInviteView = { acceptDeclineInviteView = {
acceptDeclineInviteView.Render( acceptDeclineInviteView.Render(
state = state.roomListState.acceptDeclineInviteState, state = state.roomListState.acceptDeclineInviteState,
onAcceptInviteSuccess = this::onRoomClick, onAcceptInviteSuccess = callback::navigateToRoom,
onDeclineInviteSuccess = { }, onDeclineInviteSuccess = { },
modifier = Modifier modifier = Modifier
) )
@@ -193,7 +170,7 @@ class HomeFlowNode(
leaveRoomView = { leaveRoomView = {
leaveRoomRenderer.Render( leaveRoomRenderer.Render(
state = state.roomListState.leaveRoomState, state = state.roomListState.leaveRoomState,
onSelectNewOwners = this::onSelectNewOwnersWhenLeavingRoom, onSelectNewOwners = ::navigateToSelectNewOwnersWhenLeavingRoom,
modifier = Modifier modifier = Modifier
) )
} }

View File

@@ -12,12 +12,12 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.licenses.impl.model.DependencyLicenseItem import io.element.android.features.licenses.impl.model.DependencyLicenseItem
import io.element.android.libraries.architecture.callback
@ContributesNode(AppScope::class) @ContributesNode(AppScope::class)
@AssistedInject @AssistedInject
@@ -33,10 +33,7 @@ class DependencyLicensesListNode(
fun navigateToLicense(license: DependencyLicenseItem) fun navigateToLicense(license: DependencyLicenseItem)
} }
private fun onOpenLicense(license: DependencyLicenseItem) { private val callback: Callback = callback()
plugins<Callback>()
.forEach { it.navigateToLicense(license) }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
@@ -44,7 +41,7 @@ class DependencyLicensesListNode(
DependencyLicensesListView( DependencyLicensesListView(
state = state, state = state,
onBackClick = ::navigateUp, onBackClick = ::navigateUp,
onOpenLicense = ::onOpenLicense, onOpenLicense = callback::navigateToLicense,
) )
} }
} }

View File

@@ -12,10 +12,10 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode 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.di.SessionScope
@ContributesNode(SessionScope::class) @ContributesNode(SessionScope::class)
@@ -29,9 +29,7 @@ class LockScreenSettingsNode(
fun navigateToSetupPin() fun navigateToSetupPin()
} }
private fun onChangePinClick() { private val callback: Callback = callback()
plugins<Callback>().forEach { it.navigateToSetupPin() }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
@@ -39,7 +37,7 @@ class LockScreenSettingsNode(
LockScreenSettingsView( LockScreenSettingsView(
state = state, state = state,
onBackClick = this::navigateUp, onBackClick = this::navigateUp,
onChangePinClick = this::onChangePinClick, onChangePinClick = callback::navigateToSetupPin,
modifier = modifier, modifier = modifier,
) )
} }

View File

@@ -14,7 +14,6 @@ import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.newRoot import com.bumble.appyx.navmodel.backstack.operation.newRoot
import dev.zacsweers.metro.Assisted 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.features.lockscreen.impl.setup.pin.SetupPinNode
import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode 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.createNode
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@@ -50,9 +50,7 @@ class LockScreenSetupFlowNode(
fun onSetupDone() fun onSetupDone()
} }
private fun onSetupDone() { private val callback: Callback = callback()
plugins<Callback>().forEach { it.onSetupDone() }
}
sealed interface NavTarget : Parcelable { sealed interface NavTarget : Parcelable {
@Parcelize @Parcelize
@@ -67,7 +65,7 @@ class LockScreenSetupFlowNode(
if (biometricAuthenticatorManager.hasAvailableAuthenticator) { if (biometricAuthenticatorManager.hasAvailableAuthenticator) {
backstack.newRoot(NavTarget.Biometric) backstack.newRoot(NavTarget.Biometric)
} else { } else {
onSetupDone() callback.onSetupDone()
} }
} }
} }
@@ -91,7 +89,7 @@ class LockScreenSetupFlowNode(
NavTarget.Biometric -> { NavTarget.Biometric -> {
val callback = object : SetupBiometricNode.Callback { val callback = object : SetupBiometricNode.Callback {
override fun onBiometricSetupDone() { override fun onBiometricSetupDone() {
onSetupDone() callback.onSetupDone()
} }
} }
createNode<SetupBiometricNode>(buildContext, plugins = listOf(callback)) createNode<SetupBiometricNode>(buildContext, plugins = listOf(callback))

View File

@@ -13,10 +13,10 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode 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.di.SessionScope
@ContributesNode(SessionScope::class) @ContributesNode(SessionScope::class)
@@ -30,16 +30,14 @@ class SetupBiometricNode(
fun onBiometricSetupDone() fun onBiometricSetupDone()
} }
private fun onSetupDone() { private val callback: Callback = callback()
plugins<Callback>().forEach { it.onBiometricSetupDone() }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
LaunchedEffect(state.isBiometricSetupDone) { LaunchedEffect(state.isBiometricSetupDone) {
if (state.isBiometricSetupDone) { if (state.isBiometricSetupDone) {
onSetupDone() callback.onBiometricSetupDone()
} }
} }
SetupBiometricView( SetupBiometricView(

View File

@@ -13,10 +13,10 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode 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.di.SessionScope
@ContributesNode(SessionScope::class) @ContributesNode(SessionScope::class)
@@ -30,18 +30,14 @@ class PinUnlockNode(
fun onUnlock() fun onUnlock()
} }
private fun onUnlock() { private val callback: Callback = callback()
plugins<Callback>().forEach {
it.onUnlock()
}
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
LaunchedEffect(state.isUnlocked) { LaunchedEffect(state.isUnlocked) {
if (state.isUnlocked) { if (state.isUnlocked) {
onUnlock() callback.onUnlock()
} }
} }
PinUnlockView( PinUnlockView(

View File

@@ -18,7 +18,6 @@ import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.push import com.bumble.appyx.navmodel.backstack.operation.push
import com.bumble.appyx.navmodel.backstack.operation.singleTop 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.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.NodeInputs 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.createNode
import io.element.android.libraries.architecture.inputs import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.matrix.api.auth.OidcDetails import io.element.android.libraries.matrix.api.auth.OidcDetails
@@ -70,6 +70,7 @@ class LoginFlowNode(
val loginHint: String?, val loginHint: String?,
) : NodeInputs ) : NodeInputs
private val callback: LoginEntryPoint.Callback = callback()
private var activity: Activity? = null private var activity: Activity? = null
private var darkTheme: Boolean = false private var darkTheme: Boolean = false
@@ -147,7 +148,7 @@ class LoginFlowNode(
} }
override fun navigateToBugReport() { override fun navigateToBugReport() {
plugins<LoginEntryPoint.Callback>().forEach { it.navigateToBugReport() } callback.navigateToBugReport()
} }
override fun navigateToOidc(oidcDetails: OidcDetails) { override fun navigateToOidc(oidcDetails: OidcDetails) {

View File

@@ -13,12 +13,12 @@ import androidx.compose.ui.platform.LocalContext
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.login.impl.util.openLearnMorePage import io.element.android.features.login.impl.util.openLearnMorePage
import io.element.android.libraries.architecture.callback
@ContributesNode(AppScope::class) @ContributesNode(AppScope::class)
@AssistedInject @AssistedInject
@@ -32,13 +32,7 @@ class ChangeAccountProviderNode(
fun navigateToSearchAccountProvider() fun navigateToSearchAccountProvider()
} }
private fun onDone() { private val callback: Callback = callback()
plugins<Callback>().forEach { it.onDone() }
}
private fun onOtherClick() {
plugins<Callback>().forEach { it.navigateToSearchAccountProvider() }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
@@ -49,8 +43,8 @@ class ChangeAccountProviderNode(
modifier = modifier, modifier = modifier,
onBackClick = ::navigateUp, onBackClick = ::navigateUp,
onLearnMoreClick = { openLearnMorePage(context) }, onLearnMoreClick = { openLearnMorePage(context) },
onSuccess = ::onDone, onSuccess = callback::onDone,
onOtherProviderClick = ::onOtherClick, onOtherProviderClick = callback::navigateToSearchAccountProvider,
) )
} }
} }

View File

@@ -13,12 +13,12 @@ import androidx.compose.ui.platform.LocalContext
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.login.impl.util.openLearnMorePage 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 import io.element.android.libraries.matrix.api.auth.OidcDetails
@ContributesNode(AppScope::class) @ContributesNode(AppScope::class)
@@ -34,17 +34,7 @@ class ChooseAccountProviderNode(
fun navigateToCreateAccount(url: String) fun navigateToCreateAccount(url: String)
} }
private fun onOidcDetails(oidcDetails: OidcDetails) { private val callback: Callback = callback()
plugins<Callback>().forEach { it.navigateToOidc(oidcDetails) }
}
private fun onLoginPasswordNeeded() {
plugins<Callback>().forEach { it.navigateToLoginPassword() }
}
private fun onCreateAccountContinue(url: String) {
plugins<Callback>().forEach { it.navigateToCreateAccount(url) }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
@@ -54,10 +44,10 @@ class ChooseAccountProviderNode(
state = state, state = state,
modifier = modifier, modifier = modifier,
onBackClick = ::navigateUp, onBackClick = ::navigateUp,
onOidcDetails = ::onOidcDetails, onOidcDetails = callback::navigateToOidc,
onNeedLoginPassword = ::onLoginPasswordNeeded, onNeedLoginPassword = callback::navigateToLoginPassword,
onLearnMoreClick = { openLearnMorePage(context) }, onLearnMoreClick = { openLearnMorePage(context) },
onCreateAccountContinue = ::onCreateAccountContinue, onCreateAccountContinue = callback::navigateToCreateAccount,
) )
} }
} }

View File

@@ -13,13 +13,13 @@ import androidx.compose.ui.platform.LocalContext
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.login.impl.util.openLearnMorePage import io.element.android.features.login.impl.util.openLearnMorePage
import io.element.android.libraries.architecture.NodeInputs 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.inputs
import io.element.android.libraries.matrix.api.auth.OidcDetails import io.element.android.libraries.matrix.api.auth.OidcDetails
@@ -48,21 +48,7 @@ class ConfirmAccountProviderNode(
fun navigateToChangeAccountProvider() fun navigateToChangeAccountProvider()
} }
private fun onOidcDetails(data: OidcDetails) { private val callback: Callback = callback()
plugins<Callback>().forEach { it.navigateToOidc(data) }
}
private fun onLoginPasswordNeeded() {
plugins<Callback>().forEach { it.navigateToLoginPassword() }
}
private fun onCreateAccountContinue(url: String) {
plugins<Callback>().forEach { it.navigateToCreateAccount(url) }
}
private fun onChangeAccountProvider() {
plugins<Callback>().forEach { it.navigateToChangeAccountProvider() }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
@@ -71,10 +57,10 @@ class ConfirmAccountProviderNode(
ConfirmAccountProviderView( ConfirmAccountProviderView(
state = state, state = state,
modifier = modifier, modifier = modifier,
onOidcDetails = ::onOidcDetails, onOidcDetails = callback::navigateToOidc,
onNeedLoginPassword = ::onLoginPasswordNeeded, onNeedLoginPassword = callback::navigateToLoginPassword,
onCreateAccountContinue = ::onCreateAccountContinue, onCreateAccountContinue = callback::navigateToCreateAccount,
onChange = ::onChangeAccountProvider, onChange = callback::navigateToChangeAccountProvider,
onLearnMoreClick = { openLearnMorePage(context) }, onLearnMoreClick = { openLearnMorePage(context) },
) )
} }

View File

@@ -13,13 +13,13 @@ import androidx.compose.ui.platform.LocalContext
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.login.impl.util.openLearnMorePage import io.element.android.features.login.impl.util.openLearnMorePage
import io.element.android.libraries.architecture.NodeInputs 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.inputs
import io.element.android.libraries.matrix.api.auth.OidcDetails import io.element.android.libraries.matrix.api.auth.OidcDetails
@@ -48,40 +48,13 @@ class OnBoardingNode(
val loginHint: String?, val loginHint: String?,
) : NodeInputs ) : NodeInputs
private val callback: Callback = callback()
private val params = inputs<Params>() private val params = inputs<Params>()
private val presenter = presenterFactory.create( private val presenter = presenterFactory.create(
params = params, params = params,
) )
private fun onSignIn(mustChooseAccountProvider: Boolean) {
plugins<Callback>().forEach { it.navigateToSignInFlow(mustChooseAccountProvider) }
}
private fun onSignUp() {
plugins<Callback>().forEach { it.navigateToSignUpFlow() }
}
private fun onSignInWithQrCode() {
plugins<Callback>().forEach { it.navigateToQrCode() }
}
private fun onReportProblem() {
plugins<Callback>().forEach { it.navigateToBugReport() }
}
private fun onOidcDetails(data: OidcDetails) {
plugins<Callback>().forEach { it.navigateToOidc(data) }
}
private fun onLoginPasswordNeeded() {
plugins<Callback>().forEach { it.navigateToLoginPassword() }
}
private fun onCreateAccountContinue(url: String) {
plugins<Callback>().forEach { it.navigateToCreateAccount(url) }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
@@ -89,14 +62,14 @@ class OnBoardingNode(
OnBoardingView( OnBoardingView(
state = state, state = state,
modifier = modifier, modifier = modifier,
onSignIn = ::onSignIn, onSignIn = callback::navigateToSignInFlow,
onCreateAccount = ::onSignUp, onCreateAccount = callback::navigateToSignUpFlow,
onSignInWithQrCode = ::onSignInWithQrCode, onSignInWithQrCode = callback::navigateToQrCode,
onReportProblem = ::onReportProblem, onReportProblem = callback::navigateToBugReport,
onOidcDetails = ::onOidcDetails, onOidcDetails = callback::navigateToOidc,
onNeedLoginPassword = ::onLoginPasswordNeeded, onNeedLoginPassword = callback::navigateToLoginPassword,
onLearnMoreClick = { openLearnMorePage(context) }, onLearnMoreClick = { openLearnMorePage(context) },
onCreateAccountContinue = ::onCreateAccountContinue, onCreateAccountContinue = callback::navigateToCreateAccount,
onBackClick = ::navigateUp, onBackClick = ::navigateUp,
) )
} }

View File

@@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.login.impl.di.QrCodeLoginScope import io.element.android.features.login.impl.di.QrCodeLoginScope
import io.element.android.libraries.architecture.callback
import io.element.android.libraries.architecture.inputs import io.element.android.libraries.architecture.inputs
@ContributesNode(QrCodeLoginScope::class) @ContributesNode(QrCodeLoginScope::class)
@@ -29,17 +29,14 @@ class QrCodeConfirmationNode(
fun onCancel() fun onCancel()
} }
private val callback: Callback = callback()
private val step = inputs<QrCodeConfirmationStep>() private val step = inputs<QrCodeConfirmationStep>()
private fun onCancel() {
plugins<Callback>().forEach { it.onCancel() }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
QrCodeConfirmationView( QrCodeConfirmationView(
step = step, step = step,
onCancel = ::onCancel, onCancel = callback::onCancel,
) )
} }
} }

View File

@@ -12,12 +12,12 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.login.impl.di.QrCodeLoginScope import io.element.android.features.login.impl.di.QrCodeLoginScope
import io.element.android.features.login.impl.qrcode.QrCodeErrorScreenType 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.architecture.inputs
import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildMeta
@@ -32,10 +32,7 @@ class QrCodeErrorNode(
fun onRetry() fun onRetry()
} }
private fun onRetry() { private val callback: Callback = callback()
plugins<Callback>().forEach { it.onRetry() }
}
private val qrCodeErrorScreenType = inputs<QrCodeErrorScreenType>() private val qrCodeErrorScreenType = inputs<QrCodeErrorScreenType>()
@Composable @Composable
@@ -44,7 +41,7 @@ class QrCodeErrorNode(
modifier = modifier, modifier = modifier,
errorScreenType = qrCodeErrorScreenType, errorScreenType = qrCodeErrorScreenType,
appName = buildMeta.productionApplicationName, appName = buildMeta.productionApplicationName,
onRetry = ::onRetry, onRetry = callback::onRetry,
) )
} }
} }

View File

@@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.login.impl.di.QrCodeLoginScope import io.element.android.features.login.impl.di.QrCodeLoginScope
import io.element.android.libraries.architecture.callback
@ContributesNode(QrCodeLoginScope::class) @ContributesNode(QrCodeLoginScope::class)
@AssistedInject @AssistedInject
@@ -30,21 +30,15 @@ class QrCodeIntroNode(
fun navigateToQrCodeScan() fun navigateToQrCodeScan()
} }
private fun onCancelClicked() { private val callback: Callback = callback()
plugins<Callback>().forEach { it.cancel() }
}
private fun onContinue() {
plugins<Callback>().forEach { it.navigateToQrCodeScan() }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
QrCodeIntroView( QrCodeIntroView(
state = state, state = state,
onBackClick = ::onCancelClicked, onBackClick = callback::cancel,
onContinue = ::onContinue, onContinue = callback::navigateToQrCodeScan,
modifier = modifier modifier = modifier
) )
} }

View File

@@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.login.impl.di.QrCodeLoginScope 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 import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData
@ContributesNode(QrCodeLoginScope::class) @ContributesNode(QrCodeLoginScope::class)
@@ -31,21 +31,15 @@ class QrCodeScanNode(
fun cancel() fun cancel()
} }
private fun onQrCodeDataReady(qrCodeLoginData: MatrixQrCodeLoginData) { private val callback: Callback = callback()
plugins<Callback>().forEach { it.handleScannedCode(qrCodeLoginData) }
}
private fun onCancelClicked() {
plugins<Callback>().forEach { it.cancel() }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
QrCodeScanView( QrCodeScanView(
state = state, state = state,
onQrCodeDataReady = ::onQrCodeDataReady, onQrCodeDataReady = callback::handleScannedCode,
onBackClick = ::onCancelClicked, onBackClick = callback::cancel,
modifier = modifier modifier = modifier
) )
} }

View File

@@ -13,12 +13,12 @@ import androidx.compose.ui.platform.LocalContext
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.login.impl.util.openLearnMorePage import io.element.android.features.login.impl.util.openLearnMorePage
import io.element.android.libraries.architecture.callback
@ContributesNode(AppScope::class) @ContributesNode(AppScope::class)
@AssistedInject @AssistedInject
@@ -31,9 +31,7 @@ class SearchAccountProviderNode(
fun onDone() fun onDone()
} }
private fun onDone() { private val callback: Callback = callback()
plugins<Callback>().forEach { it.onDone() }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
@@ -44,7 +42,7 @@ class SearchAccountProviderNode(
modifier = modifier, modifier = modifier,
onBackClick = ::navigateUp, onBackClick = ::navigateUp,
onLearnMoreClick = { openLearnMorePage(context) }, onLearnMoreClick = { openLearnMorePage(context) },
onSuccess = ::onDone, onSuccess = callback::onDone,
) )
} }
} }

View File

@@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.logout.api.LogoutEntryPoint import io.element.android.features.logout.api.LogoutEntryPoint
import io.element.android.libraries.architecture.callback
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
@ContributesNode(SessionScope::class) @ContributesNode(SessionScope::class)
@@ -26,16 +26,14 @@ class LogoutNode(
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
private val presenter: LogoutPresenter, private val presenter: LogoutPresenter,
) : Node(buildContext, plugins = plugins) { ) : Node(buildContext, plugins = plugins) {
private fun onChangeRecoveryKeyClick() { private val callback: LogoutEntryPoint.Callback = callback()
plugins<LogoutEntryPoint.Callback>().forEach { it.navigateToSecureBackup() }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
LogoutView( LogoutView(
state = state, state = state,
onChangeRecoveryKeyClick = ::onChangeRecoveryKeyClick, onChangeRecoveryKeyClick = callback::navigateToSecureBackup,
onBackClick = ::navigateUp, onBackClick = ::navigateUp,
modifier = modifier, modifier = modifier,
) )

View File

@@ -16,7 +16,6 @@ import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.pop
import com.bumble.appyx.navmodel.backstack.operation.push 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.features.poll.api.create.CreatePollMode
import io.element.android.libraries.architecture.BackstackWithOverlayBox import io.element.android.libraries.architecture.BackstackWithOverlayBox
import io.element.android.libraries.architecture.BaseFlowNode 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.createNode
import io.element.android.libraries.architecture.overlay.Overlay import io.element.android.libraries.architecture.overlay.Overlay
import io.element.android.libraries.architecture.overlay.operation.hide 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 data class Thread(val threadRootId: ThreadId, val focusedEventId: EventId?) : NavTarget
} }
private val callbacks = plugins<MessagesEntryPoint.Callback>() private val callback: MessagesEntryPoint.Callback = callback()
override fun onBuilt() { override fun onBuilt() {
super.onBuilt() super.onBuilt()
@@ -221,7 +221,7 @@ class MessagesFlowNode(
is NavTarget.Messages -> { is NavTarget.Messages -> {
val callback = object : MessagesNode.Callback { val callback = object : MessagesNode.Callback {
override fun navigateToRoomDetails() { override fun navigateToRoomDetails() {
callbacks.forEach { it.navigateToRoomDetails() } callback.navigateToRoomDetails()
} }
override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean { override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean {
@@ -242,11 +242,11 @@ class MessagesFlowNode(
} }
override fun navigateToRoomMemberDetails(userId: UserId) { override fun navigateToRoomMemberDetails(userId: UserId) {
callbacks.forEach { it.navigateToRoomMemberDetails(userId) } callback.navigateToRoomMemberDetails(userId)
} }
override fun handlePermalinkClick(data: PermalinkData) { override fun handlePermalinkClick(data: PermalinkData) {
callbacks.forEach { it.handlePermalinkClick(data, pushToBackstack = true) } callback.handlePermalinkClick(data, pushToBackstack = true)
} }
override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) {
@@ -317,7 +317,7 @@ class MessagesFlowNode(
override fun forwardEvent(eventId: EventId) { override fun forwardEvent(eventId: EventId) {
// Need to go to the parent because of the overlay // Need to go to the parent because of the overlay
callbacks.forEach { it.forwardEvent(eventId) } callback.forwardEvent(eventId)
} }
} }
mediaViewerEntryPoint.nodeBuilder(this, buildContext) mediaViewerEntryPoint.nodeBuilder(this, buildContext)
@@ -352,7 +352,7 @@ class MessagesFlowNode(
override fun onDone(roomIds: List<RoomId>) { override fun onDone(roomIds: List<RoomId>) {
backstack.pop() backstack.pop()
roomIds.singleOrNull()?.let { roomId -> roomIds.singleOrNull()?.let { roomId ->
callbacks.forEach { it.navigateToRoom(roomId) } callback.navigateToRoom(roomId)
} }
} }
} }
@@ -400,7 +400,7 @@ class MessagesFlowNode(
} }
override fun navigateToRoomMemberDetails(userId: UserId) { override fun navigateToRoomMemberDetails(userId: UserId) {
callbacks.forEach { it.navigateToRoomMemberDetails(userId) } callback.navigateToRoomMemberDetails(userId)
} }
override fun viewInTimeline(eventId: EventId) { override fun viewInTimeline(eventId: EventId) {
@@ -408,7 +408,7 @@ class MessagesFlowNode(
} }
override fun handlePermalinkClick(data: PermalinkData.RoomLink) { 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) { override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) {
@@ -448,11 +448,11 @@ class MessagesFlowNode(
} }
override fun navigateToRoomMemberDetails(userId: UserId) { override fun navigateToRoomMemberDetails(userId: UserId) {
callbacks.forEach { it.navigateToRoomMemberDetails(userId) } callback.navigateToRoomMemberDetails(userId)
} }
override fun handlePermalinkClick(data: PermalinkData) { override fun handlePermalinkClick(data: PermalinkData) {
callbacks.forEach { it.handlePermalinkClick(data, pushToBackstack = true) } callback.handlePermalinkClick(data, pushToBackstack = true)
} }
override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) {
@@ -502,7 +502,7 @@ class MessagesFlowNode(
roomIdOrAlias = room.roomId.toRoomIdOrAlias(), roomIdOrAlias = room.roomId.toRoomIdOrAlias(),
eventId = eventId, eventId = eventId,
) )
callbacks.forEach { it.handlePermalinkClick(permalinkData, pushToBackstack = false) } callback.handlePermalinkClick(permalinkData, pushToBackstack = false)
} }
private fun processEventClick( private fun processEventClick(

View File

@@ -24,7 +24,6 @@ import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode 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.openUrlInExternalApp
import io.element.android.libraries.androidutils.system.toast import io.element.android.libraries.androidutils.system.toast
import io.element.android.libraries.architecture.NodeInputs 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.inputs
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.annotations.ApplicationContext import io.element.android.libraries.di.annotations.ApplicationContext
@@ -93,13 +92,12 @@ class MessagesNode(
private val knockRequestsBannerRenderer: KnockRequestsBannerRenderer, private val knockRequestsBannerRenderer: KnockRequestsBannerRenderer,
private val roomMemberModerationRenderer: RoomMemberModerationRenderer, private val roomMemberModerationRenderer: RoomMemberModerationRenderer,
) : Node(buildContext, plugins = plugins), MessagesNavigator { ) : Node(buildContext, plugins = plugins), MessagesNavigator {
private val callbacks = plugins<Callback>()
data class Inputs( data class Inputs(
val focusedEventId: EventId?, val focusedEventId: EventId?,
) : NodeInputs ) : NodeInputs
private val inputs = inputs<Inputs>() private val inputs = inputs<Inputs>()
private val callback: Callback = callback()
private val timelineController = TimelineController(room, room.liveTimeline) private val timelineController = TimelineController(room, room.liveTimeline)
private val presenter = presenterFactory.create( 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( private fun onLinkClick(
activity: Activity, activity: Activity,
darkTheme: Boolean, darkTheme: Boolean,
@@ -180,7 +152,7 @@ class MessagesNode(
is PermalinkData.UserLink -> { is PermalinkData.UserLink -> {
// Open the room member profile, it will fallback to // Open the room member profile, it will fallback to
// the user profile if the user is not in the room // the user profile if the user is not in the room
callbacks.forEach { it.navigateToRoomMemberDetails(permalink.userId) } callback.navigateToRoomMemberDetails(permalink.userId)
} }
is PermalinkData.RoomLink -> { is PermalinkData.RoomLink -> {
handleRoomLinkClick(permalink, eventSink) handleRoomLinkClick(permalink, eventSink)
@@ -211,28 +183,28 @@ class MessagesNode(
displaySameRoomToast() displaySameRoomToast()
} }
} else { } else {
callbacks.forEach { it.handlePermalinkClick(roomLink) } callback.handlePermalinkClick(roomLink)
} }
} }
override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) {
callbacks.forEach { it.navigateToEventDebugInfo(eventId, debugInfo) } callback.navigateToEventDebugInfo(eventId, debugInfo)
} }
override fun forwardEvent(eventId: EventId) { override fun forwardEvent(eventId: EventId) {
callbacks.forEach { it.forwardEvent(eventId) } callback.forwardEvent(eventId)
} }
override fun navigateToReportMessage(eventId: EventId, senderId: UserId) { override fun navigateToReportMessage(eventId: EventId, senderId: UserId) {
callbacks.forEach { it.navigateToReportMessage(eventId, senderId) } callback.navigateToReportMessage(eventId, senderId)
} }
override fun navigateToEditPoll(eventId: EventId) { override fun navigateToEditPoll(eventId: EventId) {
callbacks.forEach { it.navigateToEditPoll(eventId) } callback.navigateToEditPoll(eventId)
} }
override fun navigateToPreviewAttachments(attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?) { override fun navigateToPreviewAttachments(attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?) {
callbacks.forEach { it.navigateToPreviewAttachments(attachments, inReplyToEventId) } callback.navigateToPreviewAttachments(attachments, inReplyToEventId)
} }
override fun navigateToRoom(roomId: RoomId, eventId: EventId?, serverNames: List<String>) { override fun navigateToRoom(roomId: RoomId, eventId: EventId?, serverNames: List<String>) {
@@ -240,24 +212,12 @@ class MessagesNode(
displaySameRoomToast() displaySameRoomToast()
} else { } else {
val permalinkData = PermalinkData.RoomLink(roomId.toRoomIdOrAlias(), eventId, viaParameters = serverNames.toImmutableList()) 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?) { override fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) {
callbacks.forEach { it.navigateToThread(threadRootId, focusedEventId) } callback.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) }
} }
private fun displaySameRoomToast() { private fun displaySameRoomToast() {
@@ -288,20 +248,20 @@ class MessagesNode(
MessagesView( MessagesView(
state = state, state = state,
onBackClick = { state.eventSink(MessagesEvents.MarkAsFullyReadAndExit) }, onBackClick = { state.eventSink(MessagesEvents.MarkAsFullyReadAndExit) },
onRoomDetailsClick = ::navigateToRoomDetails, onRoomDetailsClick = callback::navigateToRoomDetails,
onEventContentClick = { isLive, event -> onEventContentClick = { isLive, event ->
if (isLive) { if (isLive) {
onEventClick(timelineController.mainTimelineMode(), event) callback.handleEventClick(timelineController.mainTimelineMode(), event)
} else { } else {
val detachedTimelineMode = timelineController.detachedTimelineMode() val detachedTimelineMode = timelineController.detachedTimelineMode()
if (detachedTimelineMode != null) { if (detachedTimelineMode != null) {
onEventClick(detachedTimelineMode, event) callback.handleEventClick(detachedTimelineMode, event)
} else { } else {
false false
} }
} }
}, },
onUserDataClick = ::navigateToRoomMemberDetails, onUserDataClick = callback::navigateToRoomMemberDetails,
onLinkClick = { url, customTab -> onLinkClick = { url, customTab ->
onLinkClick( onLinkClick(
activity = activity, activity = activity,
@@ -311,15 +271,15 @@ class MessagesNode(
customTab = customTab, customTab = customTab,
) )
}, },
onSendLocationClick = ::navigateToSendLocation, onSendLocationClick = callback::navigateToSendLocation,
onCreatePollClick = ::navigateToCreatePoll, onCreatePollClick = callback::navigateToCreatePoll,
onJoinCallClick = ::navigateToRoomCall, onJoinCallClick = { callback.navigateToRoomCall(room.roomId) },
onViewAllPinnedMessagesClick = ::navigateToPinnedMessagesList, onViewAllPinnedMessagesClick = callback::navigateToPinnedMessagesList,
modifier = modifier, modifier = modifier,
knockRequestsBannerView = { knockRequestsBannerView = {
knockRequestsBannerRenderer.View( knockRequestsBannerRenderer.View(
modifier = Modifier, modifier = Modifier,
onViewRequestsClick = ::navigateToKnockRequestsList, onViewRequestsClick = callback::navigateToKnockRequestsList,
) )
}, },
) )
@@ -327,7 +287,7 @@ class MessagesNode(
state = state.roomMemberModerationState, state = state.roomMemberModerationState,
onSelectAction = { action, target -> onSelectAction = { action, target ->
when (action) { when (action) {
is ModerationAction.DisplayProfile -> navigateToRoomMemberDetails(target.userId) is ModerationAction.DisplayProfile -> callback.navigateToRoomMemberDetails(target.userId)
else -> state.roomMemberModerationState.eventSink(RoomMemberModerationEvents.ProcessAction(action, target)) else -> state.roomMemberModerationState.eventSink(RoomMemberModerationEvents.ProcessAction(action, target))
} }
}, },

View File

@@ -17,7 +17,6 @@ import androidx.compose.ui.platform.LocalView
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode 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.features.messages.impl.timeline.model.TimelineItem
import io.element.android.libraries.androidutils.system.copyToClipboard import io.element.android.libraries.androidutils.system.copyToClipboard
import io.element.android.libraries.androidutils.system.openUrlInExternalApp 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.di.RoomScope
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId 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.permalink.PermalinkParser
import io.element.android.libraries.matrix.api.timeline.Timeline 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.timeline.item.TimelineItemDebugInfo
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@ContributesNode(RoomScope::class) @ContributesNode(RoomScope::class)
@@ -56,6 +55,7 @@ class PinnedMessagesListNode(
fun handleForwardEventClick(eventId: EventId) fun handleForwardEventClick(eventId: EventId)
} }
private val callback: Callback = callback()
private val presenter = presenterFactory.create( private val presenter = presenterFactory.create(
navigator = this, navigator = this,
actionListPresenter = actionListPresenterFactory.create( actionListPresenter = actionListPresenterFactory.create(
@@ -63,25 +63,16 @@ class PinnedMessagesListNode(
timelineMode = Timeline.Mode.PinnedEvents, timelineMode = Timeline.Mode.PinnedEvents,
) )
) )
private val callbacks = plugins<Callback>()
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) { private fun onLinkClick(context: Context, url: String) {
when (val permalink = permalinkParser.parse(url)) { when (val permalink = permalinkParser.parse(url)) {
is PermalinkData.UserLink -> { is PermalinkData.UserLink -> {
// Open the room member profile, it will fallback to // Open the room member profile, it will fallback to
// the user profile if the user is not in the room // the user profile if the user is not in the room
callbacks.forEach { it.navigateToRoomMemberDetails(permalink.userId) } callback.navigateToRoomMemberDetails(permalink.userId)
} }
is PermalinkData.RoomLink -> { is PermalinkData.RoomLink -> {
callbacks.forEach { it.handlePermalinkClick(permalink) } callback.handlePermalinkClick(permalink)
} }
is PermalinkData.FallbackLink, is PermalinkData.FallbackLink,
is PermalinkData.RoomEmailInviteLink -> { is PermalinkData.RoomEmailInviteLink -> {
@@ -91,15 +82,15 @@ class PinnedMessagesListNode(
} }
override fun viewInTimeline(eventId: EventId) { override fun viewInTimeline(eventId: EventId) {
callbacks.forEach { it.viewInTimeline(eventId) } callback.viewInTimeline(eventId)
} }
override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) {
callbacks.forEach { it.navigateToEventDebugInfo(eventId, debugInfo) } callback.navigateToEventDebugInfo(eventId, debugInfo)
} }
override fun forwardEvent(eventId: EventId) { override fun forwardEvent(eventId: EventId) {
callbacks.forEach { it.handleForwardEventClick(eventId) } callback.handleForwardEventClick(eventId)
} }
@Composable @Composable
@@ -113,8 +104,8 @@ class PinnedMessagesListNode(
PinnedMessagesListView( PinnedMessagesListView(
state = state, state = state,
onBackClick = ::navigateUp, onBackClick = ::navigateUp,
onEventClick = ::handleEventClick, onEventClick = callback::handleEventClick,
onUserDataClick = ::navigateToRoomMemberDetails, onUserDataClick = { callback.navigateToRoomMemberDetails(it.userId) },
onLinkClick = { link -> onLinkClick(context, link.url) }, onLinkClick = { link -> onLinkClick(context, link.url) },
onLinkLongClick = { onLinkLongClick = {
view.performHapticFeedback( view.performHapticFeedback(

View File

@@ -22,7 +22,6 @@ import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode 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.browser.openUrlInChromeCustomTab
import io.element.android.libraries.androidutils.system.openUrlInExternalApp import io.element.android.libraries.androidutils.system.openUrlInExternalApp
import io.element.android.libraries.architecture.NodeInputs 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.inputs
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.di.annotations.SessionCoroutineScope
@@ -88,14 +87,13 @@ class ThreadedMessagesNode(
private val permalinkParser: PermalinkParser, private val permalinkParser: PermalinkParser,
private val appNavigationStateService: AppNavigationStateService, private val appNavigationStateService: AppNavigationStateService,
) : Node(buildContext, plugins = plugins), MessagesNavigator { ) : Node(buildContext, plugins = plugins), MessagesNavigator {
private val callbacks = plugins<Callback>()
data class Inputs( data class Inputs(
val threadRootEventId: ThreadId, val threadRootEventId: ThreadId,
val focusedEventId: EventId?, val focusedEventId: EventId?,
) : NodeInputs ) : NodeInputs
private val inputs = inputs<Inputs>() private val inputs = inputs<Inputs>()
private val callback: Callback = callback()
// TODO use a loading state node to preload this instead of using `runBlocking` // 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() } 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( private fun onLinkClick(
activity: Activity, activity: Activity,
darkTheme: Boolean, darkTheme: Boolean,
@@ -170,7 +154,7 @@ class ThreadedMessagesNode(
is PermalinkData.UserLink -> { is PermalinkData.UserLink -> {
// Open the room member profile, it will fallback to // Open the room member profile, it will fallback to
// the user profile if the user is not in the room // the user profile if the user is not in the room
callbacks.forEach { it.navigateToRoomMemberDetails(permalink.userId) } callback.navigateToRoomMemberDetails(permalink.userId)
} }
is PermalinkData.RoomLink -> { is PermalinkData.RoomLink -> {
handleRoomLinkClick(permalink, eventSink) handleRoomLinkClick(permalink, eventSink)
@@ -204,53 +188,41 @@ class ThreadedMessagesNode(
navigateUp() navigateUp()
} }
} else { } else {
callbacks.forEach { it.handlePermalinkClick(roomLink) } callback.handlePermalinkClick(roomLink)
} }
} }
override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) {
callbacks.forEach { it.navigateToEventDebugInfo(eventId, debugInfo) } callback.navigateToEventDebugInfo(eventId, debugInfo)
} }
override fun forwardEvent(eventId: EventId) { override fun forwardEvent(eventId: EventId) {
callbacks.forEach { it.handleForwardEventClick(eventId) } callback.handleForwardEventClick(eventId)
} }
override fun navigateToReportMessage(eventId: EventId, senderId: UserId) { override fun navigateToReportMessage(eventId: EventId, senderId: UserId) {
callbacks.forEach { it.navigateToReportMessage(eventId, senderId) } callback.navigateToReportMessage(eventId, senderId)
} }
override fun navigateToEditPoll(eventId: EventId) { override fun navigateToEditPoll(eventId: EventId) {
callbacks.forEach { it.navigateToEditPoll(eventId) } callback.navigateToEditPoll(eventId)
} }
override fun navigateToPreviewAttachments(attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?) { override fun navigateToPreviewAttachments(attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?) {
callbacks.forEach { it.navigateToPreviewAttachments(attachments, inReplyToEventId) } callback.navigateToPreviewAttachments(attachments, inReplyToEventId)
} }
override fun navigateToRoom(roomId: RoomId, eventId: EventId?, serverNames: List<String>) { override fun navigateToRoom(roomId: RoomId, eventId: EventId?, serverNames: List<String>) {
val permalinkData = PermalinkData.RoomLink(roomId.toRoomIdOrAlias(), eventId, viaParameters = serverNames.toImmutableList()) 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?) { override fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) {
callbacks.forEach { it.navigateToThread(threadRootId, focusedEventId) } callback.navigateToThread(threadRootId, focusedEventId)
} }
override fun onNavigateUp() = navigateUp() 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 @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val activity = requireNotNull(LocalActivity.current) val activity = requireNotNull(LocalActivity.current)
@@ -271,17 +243,17 @@ class ThreadedMessagesNode(
onRoomDetailsClick = {}, onRoomDetailsClick = {},
onEventContentClick = { isLive, event -> onEventContentClick = { isLive, event ->
if (isLive) { if (isLive) {
onEventClick(timelineController.mainTimelineMode(), event) callback.handleEventClick(timelineController.mainTimelineMode(), event)
} else { } else {
val detachedTimelineMode = timelineController.detachedTimelineMode() val detachedTimelineMode = timelineController.detachedTimelineMode()
if (detachedTimelineMode != null) { if (detachedTimelineMode != null) {
onEventClick(detachedTimelineMode, event) callback.handleEventClick(detachedTimelineMode, event)
} else { } else {
false false
} }
} }
}, },
onUserDataClick = this::navigateToRoomMemberDetails, onUserDataClick = callback::navigateToRoomMemberDetails,
onLinkClick = { url, customTab -> onLinkClick = { url, customTab ->
onLinkClick( onLinkClick(
activity = activity, activity = activity,
@@ -291,9 +263,9 @@ class ThreadedMessagesNode(
customTab = customTab, customTab = customTab,
) )
}, },
onSendLocationClick = this::navigateToSendLocation, onSendLocationClick = callback::navigateToSendLocation,
onCreatePollClick = this::navigateToCreatePoll, onCreatePollClick = callback::navigateToCreatePoll,
onJoinCallClick = this::navigateToRoomCall, onJoinCallClick = { callback.navigateToRoomCall(room.roomId) },
onViewAllPinnedMessagesClick = {}, onViewAllPinnedMessagesClick = {},
modifier = modifier, modifier = modifier,
knockRequestsBannerView = {}, knockRequestsBannerView = {},

View File

@@ -12,10 +12,10 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode 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.di.RoomScope
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
@@ -33,16 +33,14 @@ class PollHistoryNode(
fun navigateToEditPoll(pollStartEventId: EventId) fun navigateToEditPoll(pollStartEventId: EventId)
} }
private fun onEditPoll(pollStartEventId: EventId) { private val callback: Callback = callback()
plugins<Callback>().forEach { it.navigateToEditPoll(pollStartEventId) }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
PollHistoryView( PollHistoryView(
state = presenter.present(), state = presenter.present(),
modifier = modifier, modifier = modifier,
onEditPoll = ::onEditPoll, onEditPoll = callback::navigateToEditPoll,
goBack = this::navigateUp, goBack = this::navigateUp,
) )
} }

View File

@@ -13,7 +13,6 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.pop
import com.bumble.appyx.navmodel.backstack.operation.push 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.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.appyx.canPop 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.architecture.createNode
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
@@ -116,20 +116,22 @@ class PreferencesFlowNode(
data object OssLicenses : NavTarget data object OssLicenses : NavTarget
} }
private val callback: PreferencesEntryPoint.Callback = callback()
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
return when (navTarget) { return when (navTarget) {
NavTarget.Root -> { NavTarget.Root -> {
val callback = object : PreferencesRootNode.Callback { val callback = object : PreferencesRootNode.Callback {
override fun navigateToAddAccount() { override fun navigateToAddAccount() {
plugins<PreferencesEntryPoint.Callback>().forEach { it.navigateToAddAccount() } callback.navigateToAddAccount()
} }
override fun navigateToBugReport() { override fun navigateToBugReport() {
plugins<PreferencesEntryPoint.Callback>().forEach { it.navigateToBugReport() } callback.navigateToBugReport()
} }
override fun navigateToSecureBackup() { override fun navigateToSecureBackup() {
plugins<PreferencesEntryPoint.Callback>().forEach { it.navigateToSecureBackup() } callback.navigateToSecureBackup()
} }
override fun navigateToAnalyticsSettings() { override fun navigateToAnalyticsSettings() {
@@ -241,7 +243,7 @@ class PreferencesFlowNode(
} }
override fun navigateToEvent(roomId: RoomId, eventId: EventId) { override fun navigateToEvent(roomId: RoomId, eventId: EventId) {
plugins<PreferencesEntryPoint.Callback>().forEach { it.navigateToEvent(roomId, eventId) } callback.navigateToEvent(roomId, eventId)
} }
}) })
.build() .build()
@@ -249,7 +251,7 @@ class PreferencesFlowNode(
is NavTarget.EditDefaultNotificationSetting -> { is NavTarget.EditDefaultNotificationSetting -> {
val callback = object : EditDefaultNotificationSettingNode.Callback { val callback = object : EditDefaultNotificationSettingNode.Callback {
override fun navigateToRoomNotificationSettings(roomId: RoomId) { override fun navigateToRoomNotificationSettings(roomId: RoomId) {
plugins<PreferencesEntryPoint.Callback>().forEach { it.navigateToRoomNotificationSettings(roomId) } callback.navigateToRoomNotificationSettings(roomId)
} }
} }
val input = EditDefaultNotificationSettingNode.Inputs(navTarget.isOneToOne) val input = EditDefaultNotificationSettingNode.Inputs(navTarget.isOneToOne)
@@ -271,7 +273,7 @@ class PreferencesFlowNode(
NavTarget.SignOut -> { NavTarget.SignOut -> {
val callBack: LogoutEntryPoint.Callback = object : LogoutEntryPoint.Callback { val callBack: LogoutEntryPoint.Callback = object : LogoutEntryPoint.Callback {
override fun navigateToSecureBackup() { override fun navigateToSecureBackup() {
plugins<PreferencesEntryPoint.Callback>().forEach { it.navigateToSecureBackup() } callback.navigateToSecureBackup()
} }
} }
logoutEntryPoint.nodeBuilder(this, buildContext) logoutEntryPoint.nodeBuilder(this, buildContext)

View File

@@ -19,6 +19,7 @@ import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab 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.di.SessionScope
@ContributesNode(SessionScope::class) @ContributesNode(SessionScope::class)
@@ -32,6 +33,8 @@ class AboutNode(
fun navigateToOssLicenses() fun navigateToOssLicenses()
} }
private val callback: Callback = callback()
private fun onElementLegalClick( private fun onElementLegalClick(
activity: Activity, activity: Activity,
darkTheme: Boolean, darkTheme: Boolean,
@@ -51,9 +54,7 @@ class AboutNode(
onElementLegalClick = { elementLegal -> onElementLegalClick = { elementLegal ->
onElementLegalClick(activity, isDark, elementLegal) onElementLegalClick(activity, isDark, elementLegal)
}, },
onOpenSourceLicensesClick = { onOpenSourceLicensesClick = callback::navigateToOssLicenses,
plugins.filterIsInstance<Callback>().forEach { it.navigateToOssLicenses() }
},
modifier = modifier modifier = modifier
) )
} }

View File

@@ -14,10 +14,10 @@ import com.airbnb.android.showkase.models.Showkase
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode 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.designsystem.showkase.getBrowserIntent
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
@@ -32,11 +32,7 @@ class DeveloperSettingsNode(
fun navigateToPushHistory() fun navigateToPushHistory()
} }
private val callbacks = plugins<Callback>() private val callback: Callback = callback()
private fun navigateToPushHistory() {
callbacks.forEach { it.navigateToPushHistory() }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
@@ -51,7 +47,7 @@ class DeveloperSettingsNode(
state = state, state = state,
modifier = modifier, modifier = modifier,
onOpenShowkase = ::openShowkase, onOpenShowkase = ::openShowkase,
onPushHistoryClick = ::navigateToPushHistory, onPushHistoryClick = callback::navigateToPushHistory,
onBackClick = ::navigateUp onBackClick = ::navigateUp
) )
} }

View File

@@ -12,10 +12,10 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode 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.di.SessionScope
@ContributesNode(SessionScope::class) @ContributesNode(SessionScope::class)
@@ -30,24 +30,16 @@ class NotificationSettingsNode(
fun navigateToTroubleshootNotifications() fun navigateToTroubleshootNotifications()
} }
private val callbacks = plugins<Callback>() private val callback: Callback = callback()
private fun navigateToEditDefaultNotificationSetting(isOneToOne: Boolean) {
callbacks.forEach { it.navigateToEditDefaultNotificationSetting(isOneToOne) }
}
private fun navigateToTroubleshootNotifications() {
callbacks.forEach { it.navigateToTroubleshootNotifications() }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
NotificationSettingsView( NotificationSettingsView(
state = state, state = state,
onOpenEditDefault = ::navigateToEditDefaultNotificationSetting, onOpenEditDefault = callback::navigateToEditDefaultNotificationSetting,
onBackClick = ::navigateUp, onBackClick = ::navigateUp,
onTroubleshootNotificationsClick = ::navigateToTroubleshootNotifications, onTroubleshootNotificationsClick = callback::navigateToTroubleshootNotifications,
modifier = modifier, modifier = modifier,
) )
} }

View File

@@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.libraries.architecture.NodeInputs 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.inputs
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
@@ -36,20 +36,16 @@ class EditDefaultNotificationSettingNode(
val isOneToOne: Boolean val isOneToOne: Boolean
) : NodeInputs ) : NodeInputs
private val callback: Callback = callback()
private val inputs = inputs<Inputs>() private val inputs = inputs<Inputs>()
private val callbacks = plugins<Callback>()
private val presenter = presenterFactory.create(inputs.isOneToOne) private val presenter = presenterFactory.create(inputs.isOneToOne)
private fun navigateToRoomNotificationSettings(roomId: RoomId) {
callbacks.forEach { it.navigateToRoomNotificationSettings(roomId) }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
EditDefaultNotificationSettingView( EditDefaultNotificationSettingView(
state = state, state = state,
openRoomNotificationSettings = { navigateToRoomNotificationSettings(it) }, openRoomNotificationSettings = callback::navigateToRoomNotificationSettings,
onBackClick = ::navigateUp, onBackClick = ::navigateUp,
modifier = modifier, modifier = modifier,
) )

View File

@@ -14,7 +14,6 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode 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.DirectLogoutEvents
import io.element.android.features.logout.api.direct.DirectLogoutView import io.element.android.features.logout.api.direct.DirectLogoutView
import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab 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.di.SessionScope
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
@@ -50,37 +50,7 @@ class PreferencesRootNode(
fun startAccountDeactivationFlow() fun startAccountDeactivationFlow()
} }
private fun onAddAccount() { private val callback: Callback = callback()
plugins<Callback>().forEach { it.navigateToAddAccount() }
}
private fun onOpenBugReport() {
plugins<Callback>().forEach { it.navigateToBugReport() }
}
private fun onSecureBackupClick() {
plugins<Callback>().forEach { it.navigateToSecureBackup() }
}
private fun onOpenDeveloperSettings() {
plugins<Callback>().forEach { it.navigateToDeveloperSettings() }
}
private fun onOpenAdvancedSettings() {
plugins<Callback>().forEach { it.navigateToAdvancedSettings() }
}
private fun onOpenLabs() {
plugins<Callback>().forEach { it.navigateToLabs() }
}
private fun onOpenAnalytics() {
plugins<Callback>().forEach { it.navigateToAnalyticsSettings() }
}
private fun onOpenAbout() {
plugins<Callback>().forEach { it.navigateToAbout() }
}
private fun onManageAccountClick( private fun onManageAccountClick(
activity: Activity, activity: Activity,
@@ -96,30 +66,6 @@ class PreferencesRootNode(
} }
} }
private fun onOpenNotificationSettings() {
plugins<Callback>().forEach { it.navigateToNotificationSettings() }
}
private fun onOpenLockScreenSettings() {
plugins<Callback>().forEach { it.navigateToLockScreenSettings() }
}
private fun onOpenUserProfile(matrixUser: MatrixUser) {
plugins<Callback>().forEach { it.navigateToUserProfile(matrixUser) }
}
private fun onOpenBlockedUsers() {
plugins<Callback>().forEach { it.navigateToBlockedUsers() }
}
private fun onSignOutClick() {
plugins<Callback>().forEach { it.startSignOutFlow() }
}
private fun onOpenAccountDeactivation() {
plugins<Callback>().forEach { it.startAccountDeactivationFlow() }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
@@ -129,27 +75,27 @@ class PreferencesRootNode(
state = state, state = state,
modifier = modifier, modifier = modifier,
onBackClick = this::navigateUp, onBackClick = this::navigateUp,
onAddAccountClick = this::onAddAccount, onAddAccountClick = callback::navigateToAddAccount,
onOpenRageShake = this::onOpenBugReport, onOpenRageShake = callback::navigateToBugReport,
onOpenAnalytics = this::onOpenAnalytics, onOpenAnalytics = callback::navigateToAnalyticsSettings,
onOpenAbout = this::onOpenAbout, onOpenAbout = callback::navigateToAbout,
onSecureBackupClick = this::onSecureBackupClick, onSecureBackupClick = callback::navigateToSecureBackup,
onOpenDeveloperSettings = this::onOpenDeveloperSettings, onOpenDeveloperSettings = callback::navigateToDeveloperSettings,
onOpenAdvancedSettings = this::onOpenAdvancedSettings, onOpenAdvancedSettings = callback::navigateToAdvancedSettings,
onOpenLabs = this::onOpenLabs, onOpenLabs = callback::navigateToLabs,
onManageAccountClick = { onManageAccountClick(activity, it, isDark) }, onManageAccountClick = { onManageAccountClick(activity, it, isDark) },
onOpenNotificationSettings = this::onOpenNotificationSettings, onOpenNotificationSettings = callback::navigateToNotificationSettings,
onOpenLockScreenSettings = this::onOpenLockScreenSettings, onOpenLockScreenSettings = callback::navigateToLockScreenSettings,
onOpenUserProfile = this::onOpenUserProfile, onOpenUserProfile = callback::navigateToUserProfile,
onOpenBlockedUsers = this::onOpenBlockedUsers, onOpenBlockedUsers = callback::navigateToBlockedUsers,
onSignOutClick = { onSignOutClick = {
if (state.directLogoutState.canDoDirectSignOut) { if (state.directLogoutState.canDoDirectSignOut) {
state.directLogoutState.eventSink(DirectLogoutEvents.Logout(ignoreSdkError = false)) state.directLogoutState.eventSink(DirectLogoutEvents.Logout(ignoreSdkError = false))
} else { } else {
onSignOutClick() callback.startSignOutFlow()
} }
}, },
onDeactivateClick = this::onOpenAccountDeactivation onDeactivateClick = callback::startAccountDeactivationFlow
) )
directLogoutView.Render(state = state.directLogoutState) directLogoutView.Render(state = state.directLogoutState)

View File

@@ -13,7 +13,6 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.pop
import com.bumble.appyx.navmodel.backstack.operation.push 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.features.viewfolder.api.ViewFolderEntryPoint
import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode 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.createNode
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@@ -42,9 +42,7 @@ class BugReportFlowNode(
buildContext = buildContext, buildContext = buildContext,
plugins = plugins plugins = plugins
) { ) {
private fun onDone() { private val callback: BugReportEntryPoint.Callback = callback()
plugins<BugReportEntryPoint.Callback>().forEach { it.onDone() }
}
sealed interface NavTarget : Parcelable { sealed interface NavTarget : Parcelable {
@Parcelize @Parcelize
@@ -61,7 +59,7 @@ class BugReportFlowNode(
NavTarget.Root -> { NavTarget.Root -> {
val callback = object : BugReportNode.Callback { val callback = object : BugReportNode.Callback {
override fun onDone() { override fun onDone() {
this@BugReportFlowNode.onDone() callback.onDone()
} }
override fun navigateToViewLogs(basePath: String) { override fun navigateToViewLogs(basePath: String) {

View File

@@ -13,13 +13,13 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.rageshake.api.reporter.BugReporter import io.element.android.features.rageshake.api.reporter.BugReporter
import io.element.android.libraries.androidutils.system.toast import io.element.android.libraries.androidutils.system.toast
import io.element.android.libraries.architecture.callback
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@ContributesNode(AppScope::class) @ContributesNode(AppScope::class)
@@ -35,13 +35,7 @@ class BugReportNode(
fun navigateToViewLogs(basePath: String) fun navigateToViewLogs(basePath: String)
} }
private fun onViewLogs(basePath: String) { private val callback: Callback = callback()
plugins<Callback>().forEach { it.navigateToViewLogs(basePath) }
}
private fun onDone() {
plugins<Callback>().forEach { it.onDone() }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
@@ -53,12 +47,12 @@ class BugReportNode(
onBackClick = { navigateUp() }, onBackClick = { navigateUp() },
onSuccess = { onSuccess = {
activity?.toast(CommonStrings.common_report_submitted) activity?.toast(CommonStrings.common_report_submitted)
onDone() callback.onDone()
}, },
onViewLogs = { onViewLogs = {
// Force a logcat dump // Force a logcat dump
bugReporter.saveLogCat() bugReporter.saveLogCat()
onViewLogs(bugReporter.logDirectory().absolutePath) callback.navigateToViewLogs(bugReporter.logDirectory().absolutePath)
} }
) )
} }

View File

@@ -12,14 +12,13 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint 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.architecture.inputs
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
@ContributesNode(SessionScope::class) @ContributesNode(SessionScope::class)
@AssistedInject @AssistedInject
@@ -28,22 +27,19 @@ class RoomAliasResolverNode(
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
presenterFactory: RoomAliasResolverPresenter.Factory, presenterFactory: RoomAliasResolverPresenter.Factory,
) : Node(buildContext, plugins = plugins) { ) : Node(buildContext, plugins = plugins) {
private val callback: RoomAliasResolverEntryPoint.Callback = callback()
private val inputs = inputs<RoomAliasResolverEntryPoint.Params>() private val inputs = inputs<RoomAliasResolverEntryPoint.Params>()
private val presenter = presenterFactory.create( private val presenter = presenterFactory.create(
inputs.roomAlias inputs.roomAlias
) )
private fun onAliasResolved(data: ResolvedRoomAlias) {
plugins<RoomAliasResolverEntryPoint.Callback>().forEach { it.onAliasResolved(data) }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
RoomAliasResolverView( RoomAliasResolverView(
state = state, state = state,
onSuccess = ::onAliasResolved, onSuccess = callback::onAliasResolved,
onBackClick = ::navigateUp, onBackClick = ::navigateUp,
modifier = modifier modifier = modifier
) )

View File

@@ -16,7 +16,6 @@ import androidx.lifecycle.coroutineScope
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.pop
import com.bumble.appyx.navmodel.backstack.operation.push 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.features.verifysession.api.OutgoingVerificationEntryPoint
import io.element.android.libraries.architecture.BackstackWithOverlayBox import io.element.android.libraries.architecture.BackstackWithOverlayBox
import io.element.android.libraries.architecture.BaseFlowNode 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.createNode
import io.element.android.libraries.architecture.overlay.operation.hide import io.element.android.libraries.architecture.overlay.operation.hide
import io.element.android.libraries.architecture.overlay.operation.show import io.element.android.libraries.architecture.overlay.operation.show
@@ -146,6 +146,8 @@ class RoomDetailsFlowNode(
data object SelectNewOwnersWhenLeaving : NavTarget data object SelectNewOwnersWhenLeaving : NavTarget
} }
private val callback: RoomDetailsEntryPoint.Callback = callback()
override fun onBuilt() { override fun onBuilt() {
super.onBuilt() super.onBuilt()
whenChildrenAttached { whenChildrenAttached {
@@ -260,7 +262,7 @@ class RoomDetailsFlowNode(
val input = RoomNotificationSettingsNode.RoomNotificationSettingInput(navTarget.showUserDefinedSettingStyle) val input = RoomNotificationSettingsNode.RoomNotificationSettingInput(navTarget.showUserDefinedSettingStyle)
val callback = object : RoomNotificationSettingsNode.Callback { val callback = object : RoomNotificationSettingsNode.Callback {
override fun navigateToGlobalNotificationSettings() { override fun navigateToGlobalNotificationSettings() {
plugins<RoomDetailsEntryPoint.Callback>().forEach { it.navigateToGlobalNotificationSettings() } callback.navigateToGlobalNotificationSettings()
} }
} }
createNode<RoomNotificationSettingsNode>(buildContext, listOf(input, callback)) createNode<RoomNotificationSettingsNode>(buildContext, listOf(input, callback))
@@ -273,7 +275,7 @@ class RoomDetailsFlowNode(
} }
override fun navigateToRoom(roomId: RoomId) { override fun navigateToRoom(roomId: RoomId) {
plugins<RoomDetailsEntryPoint.Callback>().forEach { it.navigateToRoom(roomId, emptyList()) } callback.navigateToRoom(roomId, emptyList())
} }
override fun startCall(dmRoomId: RoomId) { override fun startCall(dmRoomId: RoomId) {
@@ -323,13 +325,11 @@ class RoomDetailsFlowNode(
roomIdOrAlias = room.roomId.toRoomIdOrAlias(), roomIdOrAlias = room.roomId.toRoomIdOrAlias(),
eventId = eventId, eventId = eventId,
) )
plugins<RoomDetailsEntryPoint.Callback>().forEach { callback.handlePermalinkClick(permalinkData, pushToBackstack = false)
it.handlePermalinkClick(permalinkData, pushToBackstack = false)
}
} }
override fun forward(eventId: EventId) { override fun forward(eventId: EventId) {
plugins<RoomDetailsEntryPoint.Callback>().forEach { it.startForwardEventFlow(eventId) } callback.startForwardEventFlow(eventId)
} }
} }
mediaGalleryEntryPoint.nodeBuilder(this, buildContext) mediaGalleryEntryPoint.nodeBuilder(this, buildContext)
@@ -350,15 +350,15 @@ class RoomDetailsFlowNode(
override fun navigateToRoomMemberDetails(userId: UserId) = Unit override fun navigateToRoomMemberDetails(userId: UserId) = Unit
override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) { override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) {
plugins<RoomDetailsEntryPoint.Callback>().forEach { it.handlePermalinkClick(data, pushToBackstack) } callback.handlePermalinkClick(data, pushToBackstack)
} }
override fun forwardEvent(eventId: EventId) { override fun forwardEvent(eventId: EventId) {
plugins<RoomDetailsEntryPoint.Callback>().forEach { it.startForwardEventFlow(eventId) } callback.startForwardEventFlow(eventId)
} }
override fun navigateToRoom(roomId: RoomId) { override fun navigateToRoom(roomId: RoomId) {
plugins<RoomDetailsEntryPoint.Callback>().forEach { it.navigateToRoom(roomId, emptyList()) } callback.navigateToRoom(roomId, emptyList())
} }
} }
return messagesEntryPoint.nodeBuilder(this, buildContext) return messagesEntryPoint.nodeBuilder(this, buildContext)

View File

@@ -18,7 +18,6 @@ import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import im.vector.app.features.analytics.plan.MobileScreen 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.features.leaveroom.api.LeaveRoomRenderer
import io.element.android.libraries.androidutils.system.startSharePlainTextIntent import io.element.android.libraries.androidutils.system.startSharePlainTextIntent
import io.element.android.libraries.architecture.appyx.launchMolecule 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.di.RoomScope
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.room.BaseRoom
@@ -63,7 +63,7 @@ class RoomDetailsNode(
fun navigateToSelectNewOwnersWhenLeaving() fun navigateToSelectNewOwnersWhenLeaving()
} }
private val callback = plugins<Callback>().single() private val callback: Callback = callback()
init { init {
lifecycle.subscribe( lifecycle.subscribe(

View File

@@ -13,7 +13,6 @@ import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import im.vector.app.features.analytics.plan.MobileScreen 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.ModerationAction
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
import io.element.android.features.roommembermoderation.api.RoomMemberModerationRenderer 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.di.RoomScope
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.api.AnalyticsService
@@ -39,7 +39,7 @@ class RoomMemberListNode(
fun navigateToInviteMembers() fun navigateToInviteMembers()
} }
private val callbacks = plugins<Callback>() private val callback: Callback = callback()
init { init {
lifecycle.subscribe( lifecycle.subscribe(
@@ -50,15 +50,11 @@ class RoomMemberListNode(
} }
override fun openRoomMemberDetails(roomMemberId: UserId) { override fun openRoomMemberDetails(roomMemberId: UserId) {
callbacks.forEach { callback.navigateToRoomMemberDetails(roomMemberId)
it.navigateToRoomMemberDetails(roomMemberId)
}
} }
override fun openInviteMembers() { override fun openInviteMembers() {
callbacks.forEach { callback.navigateToInviteMembers()
it.navigateToInviteMembers()
}
} }
override fun exitRoomMemberList() { override fun exitRoomMemberList() {

View File

@@ -13,12 +13,12 @@ import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.libraries.architecture.NodeInputs 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.inputs
import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.RoomScope
import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.api.AnalyticsService
@@ -34,17 +34,16 @@ class RoomNotificationSettingsNode(
data class RoomNotificationSettingInput( data class RoomNotificationSettingInput(
val showUserDefinedSettingStyle: Boolean val showUserDefinedSettingStyle: Boolean
) : NodeInputs ) : NodeInputs
interface Callback : Plugin { interface Callback : Plugin {
fun navigateToGlobalNotificationSettings() fun navigateToGlobalNotificationSettings()
} }
private val inputs = inputs<RoomNotificationSettingInput>()
private val callbacks = plugins<Callback>()
private fun navigateToGlobalNotificationSettings() { private val callback: Callback = callback()
callbacks.forEach { it.navigateToGlobalNotificationSettings() } private val inputs = inputs<RoomNotificationSettingInput>()
}
private val presenter = presenterFactory.create(inputs.showUserDefinedSettingStyle) private val presenter = presenterFactory.create(inputs.showUserDefinedSettingStyle)
init { init {
lifecycle.subscribe( lifecycle.subscribe(
onResume = { onResume = {
@@ -59,8 +58,8 @@ class RoomNotificationSettingsNode(
RoomNotificationSettingsView( RoomNotificationSettingsView(
state = state, state = state,
modifier = modifier, modifier = modifier,
onShowGlobalNotifications = this::navigateToGlobalNotificationSettings, onShowGlobalNotifications = callback::navigateToGlobalNotificationSettings,
onBackClick = this::navigateUp, onBackClick = ::navigateUp,
) )
} }
} }

View File

@@ -14,10 +14,10 @@ import androidx.lifecycle.lifecycleScope
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode 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.di.RoomScope
import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.room.BaseRoom
import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMember
@@ -45,7 +45,7 @@ class RolesAndPermissionsNode(
override fun onBackClick() {} override fun onBackClick() {}
} }
private val callback = plugins<Callback>().first() private val callback: Callback = callback()
@Stable @Stable
private val navigator = object : RolesAndPermissionsNavigator by callback { private val navigator = object : RolesAndPermissionsNavigator by callback {

View File

@@ -12,12 +12,11 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode 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.features.roomdirectory.api.RoomDirectoryEntryPoint
import io.element.android.libraries.architecture.callback
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
@ContributesNode(SessionScope::class) @ContributesNode(SessionScope::class)
@@ -27,18 +26,14 @@ class RoomDirectoryNode(
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
private val presenter: RoomDirectoryPresenter, private val presenter: RoomDirectoryPresenter,
) : Node(buildContext, plugins = plugins) { ) : Node(buildContext, plugins = plugins) {
private fun onResultClick(roomDescription: RoomDescription) { private val callback: RoomDirectoryEntryPoint.Callback = callback()
plugins<RoomDirectoryEntryPoint.Callback>().forEach {
it.navigateToRoom(roomDescription)
}
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
RoomDirectoryView( RoomDirectoryView(
state = state, state = state,
onResultClick = ::onResultClick, onResultClick = callback::navigateToRoom,
onBackClick = ::navigateUp, onBackClick = ::navigateUp,
modifier = modifier modifier = modifier
) )

View File

@@ -13,7 +13,6 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.pop
import com.bumble.appyx.navmodel.backstack.operation.push 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.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.appyx.canPop 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.architecture.createNode
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@@ -71,7 +71,7 @@ class SecureBackupFlowNode(
data object ResetIdentity : NavTarget data object ResetIdentity : NavTarget
} }
private val callbacks = plugins<SecureBackupEntryPoint.Callback>() private val callback: SecureBackupEntryPoint.Callback = callback()
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
return when (navTarget) { return when (navTarget) {
@@ -116,7 +116,7 @@ class SecureBackupFlowNode(
if (backstack.canPop()) { if (backstack.canPop()) {
backstack.pop() backstack.pop()
} else { } else {
callbacks.forEach { it.onDone() } callback.onDone()
} }
} }
} }
@@ -125,7 +125,7 @@ class SecureBackupFlowNode(
is NavTarget.ResetIdentity -> { is NavTarget.ResetIdentity -> {
val callback = object : ResetIdentityFlowNode.Callback { val callback = object : ResetIdentityFlowNode.Callback {
override fun onDone() { override fun onDone() {
callbacks.forEach { it.onDone() } callback.onDone()
} }
} }
createNode<ResetIdentityFlowNode>(buildContext, listOf(callback)) createNode<ResetIdentityFlowNode>(buildContext, listOf(callback))

View File

@@ -12,10 +12,10 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode 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.di.SessionScope
@ContributesNode(SessionScope::class) @ContributesNode(SessionScope::class)
@@ -29,7 +29,7 @@ class SecureBackupEnterRecoveryKeyNode(
fun onEnterRecoveryKeySuccess() fun onEnterRecoveryKeySuccess()
} }
private val callback = plugins<Callback>().first() private val callback: Callback = callback()
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {

View File

@@ -20,7 +20,6 @@ import androidx.lifecycle.LifecycleOwner
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.push import com.bumble.appyx.navmodel.backstack.operation.push
import dev.zacsweers.metro.Assisted 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.AsyncData
import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode 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.createNode
import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
@@ -63,6 +63,8 @@ class ResetIdentityFlowNode(
fun onDone() fun onDone()
} }
private val callback: Callback = callback()
sealed interface NavTarget : Parcelable { sealed interface NavTarget : Parcelable {
@Parcelize @Parcelize
data object Root : NavTarget data object Root : NavTarget
@@ -86,7 +88,7 @@ class ResetIdentityFlowNode(
cancelResetJob() cancelResetJob()
resetIdentityFlowManager.whenResetIsDone { resetIdentityFlowManager.whenResetIsDone {
plugins<Callback>().forEach { it.onDone() } callback.onDone()
} }
} }
} }

View File

@@ -15,6 +15,7 @@ import com.bumble.appyx.core.plugin.Plugin
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode 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.di.SessionScope
@ContributesNode(SessionScope::class) @ContributesNode(SessionScope::class)
@@ -27,8 +28,8 @@ class ResetIdentityRootNode(
fun onContinue() fun onContinue()
} }
private val callback: Callback = callback()
private val presenter = ResetIdentityRootPresenter() private val presenter = ResetIdentityRootPresenter()
private val callback: Callback = plugins.filterIsInstance<Callback>().first()
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {

View File

@@ -14,11 +14,11 @@ import androidx.compose.ui.platform.UriHandler
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.appconfig.LearnMoreConfig import io.element.android.appconfig.LearnMoreConfig
import io.element.android.libraries.architecture.callback
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
@ContributesNode(SessionScope::class) @ContributesNode(SessionScope::class)
@@ -38,21 +38,7 @@ class SecureBackupRootNode(
fun navigateToEnterRecoveryKey() fun navigateToEnterRecoveryKey()
} }
private fun onSetupClick() { private val callback: Callback = callback()
plugins<Callback>().forEach { it.navigateToSetup() }
}
private fun onChangeClick() {
plugins<Callback>().forEach { it.navigateToChange() }
}
private fun onDisableClick() {
plugins<Callback>().forEach { it.navigateToDisable() }
}
private fun onConfirmRecoveryKeyClick() {
plugins<Callback>().forEach { it.navigateToEnterRecoveryKey() }
}
private fun onLearnMoreClick(uriHandler: UriHandler) { private fun onLearnMoreClick(uriHandler: UriHandler) {
uriHandler.openUri(LearnMoreConfig.SECURE_BACKUP_URL) uriHandler.openUri(LearnMoreConfig.SECURE_BACKUP_URL)
@@ -65,10 +51,10 @@ class SecureBackupRootNode(
SecureBackupRootView( SecureBackupRootView(
state = state, state = state,
onBackClick = ::navigateUp, onBackClick = ::navigateUp,
onSetupClick = ::onSetupClick, onSetupClick = callback::navigateToSetup,
onChangeClick = ::onChangeClick, onChangeClick = callback::navigateToChange,
onDisableClick = ::onDisableClick, onDisableClick = callback::navigateToDisable,
onConfirmRecoveryKeyClick = ::onConfirmRecoveryKeyClick, onConfirmRecoveryKeyClick = callback::navigateToEnterRecoveryKey,
onLearnMoreClick = { onLearnMoreClick(uriHandler) }, onLearnMoreClick = { onLearnMoreClick(uriHandler) },
modifier = modifier, modifier = modifier,
) )

View File

@@ -23,6 +23,7 @@ import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.share.api.ShareEntryPoint import io.element.android.features.share.api.ShareEntryPoint
import io.element.android.libraries.architecture.NodeInputs 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.inputs
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
@@ -52,7 +53,7 @@ class ShareNode(
private val inputs = inputs<Inputs>() private val inputs = inputs<Inputs>()
private val presenter = presenterFactory.create(inputs.intent) private val presenter = presenterFactory.create(inputs.intent)
private val callbacks = plugins.filterIsInstance<ShareEntryPoint.Callback>() private val callback: ShareEntryPoint.Callback = callback()
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
val callback = object : RoomSelectEntryPoint.Callback { val callback = object : RoomSelectEntryPoint.Callback {
@@ -61,7 +62,7 @@ class ShareNode(
} }
override fun onCancel() { override fun onCancel() {
onShareDone(emptyList()) callback.onDone(emptyList())
} }
} }
@@ -82,12 +83,8 @@ class ShareNode(
val state = presenter.present() val state = presenter.present()
ShareView( ShareView(
state = state, state = state,
onShareSuccess = ::onShareDone, onShareSuccess = callback::onDone,
) )
} }
} }
private fun onShareDone(roomIds: List<RoomId>) {
callbacks.forEach { it.onDone(roomIds) }
}
} }

View File

@@ -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.features.space.impl.root.SpaceNode
import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode 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.createNode
import io.element.android.libraries.architecture.inputs import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.DependencyInjectionGraphOwner import io.element.android.libraries.di.DependencyInjectionGraphOwner
@@ -52,7 +53,7 @@ class SpaceFlowNode(
plugins = plugins, plugins = plugins,
), DependencyInjectionGraphOwner { ), DependencyInjectionGraphOwner {
private val inputs: SpaceEntryPoint.Inputs = inputs() private val inputs: SpaceEntryPoint.Inputs = inputs()
private val callback = plugins.filterIsInstance<SpaceEntryPoint.Callback>().single() private val callback: SpaceEntryPoint.Callback = callback()
private val spaceRoomList = spaceService.spaceRoomList(inputs.roomId) private val spaceRoomList = spaceService.spaceRoomList(inputs.roomId)
override val graph = graphFactory.create(spaceRoomList) override val graph = graphFactory.create(spaceRoomList)

View File

@@ -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.features.space.impl.di.SpaceFlowScope
import io.element.android.libraries.androidutils.R import io.element.android.libraries.androidutils.R
import io.element.android.libraries.androidutils.system.startSharePlainTextIntent 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.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
@@ -46,7 +47,7 @@ class SpaceNode(
fun startLeaveSpaceFlow() fun startLeaveSpaceFlow()
} }
private val callback = plugins.filterIsInstance<Callback>().single() private val callback: Callback = callback()
private fun onShareRoom(context: Context) = lifecycleScope.launch { private fun onShareRoom(context: Context) = lifecycleScope.launch {
matrixClient.getRoom(spaceRoomList.roomId)?.use { room -> matrixClient.getRoom(spaceRoomList.roomId)?.use { room ->

View File

@@ -16,7 +16,6 @@ import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.navigation.transition.JumpToEndTransitionHandler import com.bumble.appyx.core.navigation.transition.JumpToEndTransitionHandler
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.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject 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.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.OverlayView 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.architecture.createNode
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
@@ -60,15 +60,12 @@ class StartChatFlowNode(
data object JoinByAddress : NavTarget data object JoinByAddress : NavTarget
} }
private val callback: StartChatEntryPoint.Callback = callback()
private val navigator = DefaultStartChatNavigator( private val navigator = DefaultStartChatNavigator(
backstack = backstack, backstack = backstack,
overlay = overlay, overlay = overlay,
openRoom = { roomIdOrAlias, viaServers -> openRoom = callback::onRoomCreated,
plugins<StartChatEntryPoint.Callback>().forEach { it.onRoomCreated(roomIdOrAlias, viaServers) } openRoomDirectory = callback::navigateToRoomDirectory,
},
openRoomDirectory = {
plugins<StartChatEntryPoint.Callback>().forEach { it.navigateToRoomDirectory() }
}
) )
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {

View File

@@ -13,7 +13,6 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.pop
import com.bumble.appyx.navmodel.backstack.operation.push 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.features.verifysession.api.OutgoingVerificationEntryPoint
import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode 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.createNode
import io.element.android.libraries.architecture.inputs import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
@@ -67,6 +67,7 @@ class UserProfileFlowNode(
data class VerifyUser(val userId: UserId) : NavTarget data class VerifyUser(val userId: UserId) : NavTarget
} }
private val callback: UserProfileEntryPoint.Callback = callback()
private val inputs = inputs<UserProfileEntryPoint.Params>() private val inputs = inputs<UserProfileEntryPoint.Params>()
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
@@ -78,7 +79,7 @@ class UserProfileFlowNode(
} }
override fun navigateToRoom(roomId: RoomId) { override fun navigateToRoom(roomId: RoomId) {
plugins<UserProfileEntryPoint.Callback>().forEach { it.navigateToRoom(roomId) } callback.navigateToRoom(roomId)
} }
override fun startCall(dmRoomId: RoomId) { override fun startCall(dmRoomId: RoomId) {

View File

@@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint 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.architecture.inputs
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
@@ -28,13 +28,14 @@ class IncomingVerificationNode(
presenterFactory: IncomingVerificationPresenter.Factory, presenterFactory: IncomingVerificationPresenter.Factory,
) : Node(buildContext, plugins = plugins), ) : Node(buildContext, plugins = plugins),
IncomingVerificationNavigator { IncomingVerificationNavigator {
private val callback: IncomingVerificationEntryPoint.Callback = callback()
private val presenter = presenterFactory.create( private val presenter = presenterFactory.create(
verificationRequest = inputs<IncomingVerificationEntryPoint.Params>().verificationRequest, verificationRequest = inputs<IncomingVerificationEntryPoint.Params>().verificationRequest,
navigator = this, navigator = this,
) )
override fun onFinish() { override fun onFinish() {
plugins<IncomingVerificationEntryPoint.Callback>().forEach { it.onDone() } callback.onDone()
} }
@Composable @Composable

View File

@@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint 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.architecture.inputs
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
@@ -27,8 +27,7 @@ class OutgoingVerificationNode(
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
presenterFactory: OutgoingVerificationPresenter.Factory, presenterFactory: OutgoingVerificationPresenter.Factory,
) : Node(buildContext, plugins = plugins) { ) : Node(buildContext, plugins = plugins) {
private val callback = plugins<OutgoingVerificationEntryPoint.Callback>().first() private val callback: OutgoingVerificationEntryPoint.Callback = callback()
private val inputs = inputs<OutgoingVerificationEntryPoint.Params>() private val inputs = inputs<OutgoingVerificationEntryPoint.Params>()
private val presenter = presenterFactory.create( private val presenter = presenterFactory.create(

View File

@@ -12,12 +12,12 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.libraries.architecture.NodeInputs 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.inputs
@ContributesNode(AppScope::class) @ContributesNode(AppScope::class)
@@ -36,6 +36,7 @@ class ViewFileNode(
fun onBackClick() fun onBackClick()
} }
private val callback: Callback = callback()
private val inputs: Inputs = inputs() private val inputs: Inputs = inputs()
private val presenter = presenterFactory.create( private val presenter = presenterFactory.create(
@@ -43,17 +44,13 @@ class ViewFileNode(
name = inputs.name, name = inputs.name,
) )
private fun onBackClick() {
plugins<Callback>().forEach { it.onBackClick() }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
ViewFileView( ViewFileView(
state = state, state = state,
modifier = modifier, modifier = modifier,
onBackClick = ::onBackClick, onBackClick = callback::onBackClick,
) )
} }
} }

View File

@@ -12,13 +12,13 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.viewfolder.impl.model.Item import io.element.android.features.viewfolder.impl.model.Item
import io.element.android.libraries.architecture.NodeInputs 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.inputs
@ContributesNode(AppScope::class) @ContributesNode(AppScope::class)
@@ -38,6 +38,7 @@ class ViewFolderNode(
fun navigateToItem(item: Item) fun navigateToItem(item: Item)
} }
private val callback: Callback = callback()
private val inputs: Inputs = inputs() private val inputs: Inputs = inputs()
private val presenter = presenterFactory.create( private val presenter = presenterFactory.create(
@@ -45,22 +46,14 @@ class ViewFolderNode(
path = inputs.path, path = inputs.path,
) )
private fun onBackClick() {
plugins<Callback>().forEach { it.onBackClick() }
}
private fun onNavigateTo(item: Item) {
plugins<Callback>().forEach { it.navigateToItem(item) }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
ViewFolderView( ViewFolderView(
state = state, state = state,
modifier = modifier, modifier = modifier,
onNavigateTo = ::onNavigateTo, onNavigateTo = callback::navigateToItem,
onBackClick = ::onBackClick, onBackClick = callback::onBackClick,
) )
} }
} }

View File

@@ -13,7 +13,6 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.pop
import com.bumble.appyx.navmodel.backstack.operation.push 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.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.NodeInputs 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.createNode
import io.element.android.libraries.architecture.inputs import io.element.android.libraries.architecture.inputs
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@@ -65,6 +65,7 @@ class ViewFolderFlowNode(
val rootPath: String, val rootPath: String,
) : NodeInputs ) : NodeInputs
private val callback: ViewFolderEntryPoint.Callback = callback()
private val inputs: Inputs = inputs() private val inputs: Inputs = inputs()
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
@@ -108,7 +109,7 @@ class ViewFolderFlowNode(
): Node { ): Node {
val callback: ViewFolderNode.Callback = object : ViewFolderNode.Callback { val callback: ViewFolderNode.Callback = object : ViewFolderNode.Callback {
override fun onBackClick() { override fun onBackClick() {
onDone() callback.onDone()
} }
override fun navigateToItem(item: Item) { override fun navigateToItem(item: Item) {
@@ -133,8 +134,4 @@ class ViewFolderFlowNode(
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
BackstackView() BackstackView()
} }
private fun onDone() {
plugins<ViewFolderEntryPoint.Callback>().forEach { it.onDone() }
}
} }

View File

@@ -17,7 +17,7 @@ import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.libraries.accountselect.api.AccountSelectEntryPoint 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) @ContributesNode(AppScope::class)
@AssistedInject @AssistedInject
@@ -26,23 +26,15 @@ class AccountSelectNode(
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
private val presenter: AccountSelectPresenter, private val presenter: AccountSelectPresenter,
) : Node(buildContext, plugins = plugins) { ) : Node(buildContext, plugins = plugins) {
private val callbacks = plugins.filterIsInstance<AccountSelectEntryPoint.Callback>() private val callback: AccountSelectEntryPoint.Callback = callback()
private fun onDismiss() {
callbacks.forEach { it.onCancel() }
}
private fun onAccountSelected(sessionId: SessionId) {
callbacks.forEach { it.onAccountSelected(sessionId) }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
AccountSelectView( AccountSelectView(
state = state, state = state,
onDismiss = ::onDismiss, onDismiss = callback::onCancel,
onSelectAccount = ::onAccountSelected, onSelectAccount = callback::onAccountSelected,
modifier = modifier, modifier = modifier,
) )
} }

View File

@@ -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 <reified I : Plugin> Node.callback(): I {
return requireNotNull(plugins<I>().singleOrNull()) { "Make sure to actually pass a Callback plugin to your node" }
}

View File

@@ -13,10 +13,10 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode 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.di.RoomScope
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.mediaviewer.impl.gallery.di.LocalMediaItemPresenterFactories import io.element.android.libraries.mediaviewer.impl.gallery.di.LocalMediaItemPresenterFactories
@@ -43,28 +43,14 @@ class MediaGalleryNode(
fun forward(eventId: EventId) fun forward(eventId: EventId)
} }
private fun onBackClick() { private val callback: Callback = callback()
plugins<Callback>().forEach {
it.onBackClick()
}
}
override fun onViewInTimelineClick(eventId: EventId) { override fun onViewInTimelineClick(eventId: EventId) {
plugins<Callback>().forEach { callback.viewInTimeline(eventId)
it.viewInTimeline(eventId)
}
} }
override fun onForwardClick(eventId: EventId) { override fun onForwardClick(eventId: EventId) {
plugins<Callback>().forEach { callback.forward(eventId)
it.forward(eventId)
}
}
private fun onItemClick(item: MediaItem.Event) {
plugins<Callback>().forEach {
it.showItem(item)
}
} }
@Composable @Composable
@@ -75,8 +61,8 @@ class MediaGalleryNode(
val state = presenter.present() val state = presenter.present()
MediaGalleryView( MediaGalleryView(
state = state, state = state,
onBackClick = ::onBackClick, onBackClick = callback::onBackClick,
onItemClick = ::onItemClick, onItemClick = callback::showItem,
modifier = modifier, modifier = modifier,
) )
} }

View File

@@ -13,13 +13,13 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.libraries.architecture.BackstackWithOverlayBox import io.element.android.libraries.architecture.BackstackWithOverlayBox
import io.element.android.libraries.architecture.BaseFlowNode 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.createNode
import io.element.android.libraries.architecture.overlay.Overlay import io.element.android.libraries.architecture.overlay.Overlay
import io.element.android.libraries.architecture.overlay.operation.hide import io.element.android.libraries.architecture.overlay.operation.hide
@@ -70,38 +70,22 @@ class MediaGalleryFlowNode(
) : NavTarget ) : NavTarget
} }
private fun onBackClick() { private val callback: MediaGalleryEntryPoint.Callback = callback()
plugins<MediaGalleryEntryPoint.Callback>().forEach {
it.onBackClick()
}
}
private fun onViewInTimeline(eventId: EventId) {
plugins<MediaGalleryEntryPoint.Callback>().forEach {
it.viewInTimeline(eventId)
}
}
private fun forwardEvent(eventId: EventId) {
plugins<MediaGalleryEntryPoint.Callback>().forEach {
it.forward(eventId)
}
}
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
return when (navTarget) { return when (navTarget) {
NavTarget.Root -> { NavTarget.Root -> {
val callback = object : MediaGalleryNode.Callback { val callback = object : MediaGalleryNode.Callback {
override fun onBackClick() { override fun onBackClick() {
this@MediaGalleryFlowNode.onBackClick() callback.onBackClick()
} }
override fun viewInTimeline(eventId: EventId) { override fun viewInTimeline(eventId: EventId) {
this@MediaGalleryFlowNode.onViewInTimeline(eventId) callback.viewInTimeline(eventId)
} }
override fun forward(eventId: EventId) { override fun forward(eventId: EventId) {
forwardEvent(eventId) callback.forward(eventId)
} }
override fun showItem(item: MediaItem.Event) { override fun showItem(item: MediaItem.Event) {
@@ -132,12 +116,12 @@ class MediaGalleryFlowNode(
} }
override fun viewInTimeline(eventId: EventId) { override fun viewInTimeline(eventId: EventId) {
this@MediaGalleryFlowNode.onViewInTimeline(eventId) callback.viewInTimeline(eventId)
} }
override fun forwardEvent(eventId: EventId) { override fun forwardEvent(eventId: EventId) {
// Need to go to the parent because of the overlay // Need to go to the parent because of the overlay
this@MediaGalleryFlowNode.forwardEvent(eventId) callback.forward(eventId)
} }
} }
mediaViewerEntryPoint.nodeBuilder(this, buildContext) mediaViewerEntryPoint.nodeBuilder(this, buildContext)

View File

@@ -15,7 +15,6 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode 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.compound.theme.ForcedDarkElementTheme
import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.features.viewfolder.api.TextFileViewer 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.architecture.inputs
import io.element.android.libraries.audio.api.AudioFocus import io.element.android.libraries.audio.api.AudioFocus
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
@@ -57,28 +57,19 @@ class MediaViewerNode(
private val enterpriseService: EnterpriseService, private val enterpriseService: EnterpriseService,
) : Node(buildContext, plugins = plugins), ) : Node(buildContext, plugins = plugins),
MediaViewerNavigator { MediaViewerNavigator {
private val callback: MediaViewerEntryPoint.Callback = callback()
private val inputs = inputs<MediaViewerEntryPoint.Params>() private val inputs = inputs<MediaViewerEntryPoint.Params>()
private fun onDone() {
plugins<MediaViewerEntryPoint.Callback>().forEach {
it.onDone()
}
}
override fun onViewInTimelineClick(eventId: EventId) { override fun onViewInTimelineClick(eventId: EventId) {
plugins<MediaViewerEntryPoint.Callback>().forEach { callback.viewInTimeline(eventId)
it.viewInTimeline(eventId)
}
} }
override fun onForwardClick(eventId: EventId) { override fun onForwardClick(eventId: EventId) {
plugins<MediaViewerEntryPoint.Callback>().forEach { callback.forwardEvent(eventId)
it.forwardEvent(eventId)
}
} }
override fun onItemDeleted() { override fun onItemDeleted() {
onDone() callback.onDone()
} }
private val mediaGallerySource = if (inputs.mode == MediaViewerEntryPoint.MediaViewerMode.SingleMedia) { private val mediaGallerySource = if (inputs.mode == MediaViewerEntryPoint.MediaViewerMode.SingleMedia) {
@@ -153,7 +144,7 @@ class MediaViewerNode(
textFileViewer = textFileViewer, textFileViewer = textFileViewer,
modifier = modifier, modifier = modifier,
audioFocus = audioFocus, audioFocus = audioFocus,
onBackClick = ::onDone, onBackClick = callback::onDone,
) )
} }
} }

View File

@@ -62,7 +62,7 @@ class IgnoredUsersTest(
coroutineScope: CoroutineScope, coroutineScope: CoroutineScope,
navigator: NotificationTroubleshootNavigator, navigator: NotificationTroubleshootNavigator,
) { ) {
navigator.openIgnoredUsers() navigator.navigateToBlockedUsers()
} }
override suspend fun reset() = delegate.reset() override suspend fun reset() = delegate.reset()

View File

@@ -39,7 +39,7 @@ class IgnoredUsersTestTest {
) )
val openIgnoredUsersResult = lambdaRecorder<Unit> {} val openIgnoredUsersResult = lambdaRecorder<Unit> {}
val navigator = object : NotificationTroubleshootNavigator { val navigator = object : NotificationTroubleshootNavigator {
override fun openIgnoredUsers() = openIgnoredUsersResult() override fun navigateToBlockedUsers() = openIgnoredUsersResult()
} }
sut.quickFix( sut.quickFix(
coroutineScope = backgroundScope, coroutineScope = backgroundScope,

View File

@@ -16,9 +16,9 @@ import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.libraries.architecture.NodeInputs 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.inputs
import io.element.android.libraries.di.SessionScope 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.RoomSelectEntryPoint
import io.element.android.libraries.roomselect.api.RoomSelectMode import io.element.android.libraries.roomselect.api.RoomSelectMode
@@ -35,24 +35,15 @@ class RoomSelectNode(
private val inputs: Inputs = inputs() private val inputs: Inputs = inputs()
private val presenter = presenterFactory.create(inputs.mode) private val presenter = presenterFactory.create(inputs.mode)
private val callback: RoomSelectEntryPoint.Callback = callback()
private val callbacks = plugins.filterIsInstance<RoomSelectEntryPoint.Callback>()
private fun onDismiss() {
callbacks.forEach { it.onCancel() }
}
private fun onRoomSelected(roomIds: List<RoomId>) {
callbacks.forEach { it.onRoomSelected(roomIds) }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
RoomSelectView( RoomSelectView(
state = state, state = state,
onDismiss = ::onDismiss, onDismiss = callback::onCancel,
onSubmit = ::onRoomSelected, onSubmit = callback::onRoomSelected,
modifier = modifier modifier = modifier
) )
} }

View File

@@ -8,5 +8,5 @@
package io.element.android.libraries.troubleshoot.api.test package io.element.android.libraries.troubleshoot.api.test
interface NotificationTroubleshootNavigator { interface NotificationTroubleshootNavigator {
fun openIgnoredUsers() fun navigateToBlockedUsers()
} }

View File

@@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
import io.element.android.annotations.ContributesNode 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.di.SessionScope
import io.element.android.libraries.troubleshoot.api.NotificationTroubleShootEntryPoint import io.element.android.libraries.troubleshoot.api.NotificationTroubleShootEntryPoint
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
@@ -31,20 +31,13 @@ class TroubleshootNotificationsNode(
factory: TroubleshootNotificationsPresenter.Factory, factory: TroubleshootNotificationsPresenter.Factory,
) : Node(buildContext, plugins = plugins), ) : Node(buildContext, plugins = plugins),
NotificationTroubleshootNavigator { NotificationTroubleshootNavigator {
private val callback: NotificationTroubleShootEntryPoint.Callback = callback()
private val presenter = factory.create( private val presenter = factory.create(
navigator = this, navigator = this,
) )
private fun onDone() { override fun navigateToBlockedUsers() {
plugins<NotificationTroubleShootEntryPoint.Callback>().forEach { callback.navigateToBlockedUsers()
it.onDone()
}
}
override fun openIgnoredUsers() {
plugins<NotificationTroubleShootEntryPoint.Callback>().forEach {
it.navigateToBlockedUsers()
}
} }
@Composable @Composable
@@ -53,7 +46,7 @@ class TroubleshootNotificationsNode(
val state = presenter.present() val state = presenter.present()
TroubleshootNotificationsView( TroubleshootNotificationsView(
state = state, state = state,
onBackClick = ::onDone, onBackClick = callback::onDone,
modifier = modifier, modifier = modifier,
) )
} }

View File

@@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext 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.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
import io.element.android.annotations.ContributesNode 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.di.SessionScope
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
@@ -31,16 +31,10 @@ class PushHistoryNode(
presenterFactory: PushHistoryPresenter.Factory, presenterFactory: PushHistoryPresenter.Factory,
private val screenTracker: ScreenTracker, private val screenTracker: ScreenTracker,
) : Node(buildContext, plugins = plugins), PushHistoryNavigator { ) : Node(buildContext, plugins = plugins), PushHistoryNavigator {
private fun onDone() { private val callback: PushHistoryEntryPoint.Callback = callback()
plugins<PushHistoryEntryPoint.Callback>().forEach {
it.onDone()
}
}
override fun navigateTo(roomId: RoomId, eventId: EventId) { override fun navigateTo(roomId: RoomId, eventId: EventId) {
plugins<PushHistoryEntryPoint.Callback>().forEach { callback.navigateToEvent(roomId, eventId)
it.navigateToEvent(roomId, eventId)
}
} }
private val presenter = presenterFactory.create(this) private val presenter = presenterFactory.create(this)
@@ -51,7 +45,7 @@ class PushHistoryNode(
val state = presenter.present() val state = presenter.present()
PushHistoryView( PushHistoryView(
state = state, state = state,
onBackClick = ::onDone, onBackClick = callback::onDone,
modifier = modifier, modifier = modifier,
) )
} }

View File

@@ -180,7 +180,7 @@ private fun createTroubleshootTestSuite(
internal fun createTroubleshootNotificationsPresenter( internal fun createTroubleshootNotificationsPresenter(
navigator: NotificationTroubleshootNavigator = object : NotificationTroubleshootNavigator { navigator: NotificationTroubleshootNavigator = object : NotificationTroubleshootNavigator {
override fun openIgnoredUsers() = lambdaError() override fun navigateToBlockedUsers() = lambdaError()
}, },
troubleshootTestSuite: TroubleshootTestSuite = createTroubleshootTestSuite(), troubleshootTestSuite: TroubleshootTestSuite = createTroubleshootTestSuite(),
): TroubleshootNotificationsPresenter { ): TroubleshootNotificationsPresenter {

View File

@@ -13,5 +13,5 @@ import io.element.android.tests.testutils.lambda.lambdaError
class FakeNotificationTroubleshootNavigator( class FakeNotificationTroubleshootNavigator(
private val openIgnoredUsersResult: () -> Unit = { lambdaError() }, private val openIgnoredUsersResult: () -> Unit = { lambdaError() },
) : NotificationTroubleshootNavigator { ) : NotificationTroubleshootNavigator {
override fun openIgnoredUsers() = openIgnoredUsersResult() override fun navigateToBlockedUsers() = openIgnoredUsersResult()
} }