Merge pull request #5641 from element-hq/feature/bma/callbackRenaming
Improve architecture around Nodes
This commit is contained in:
commit
f88c2b7d69
260 changed files with 2804 additions and 2675 deletions
|
|
@ -59,6 +59,7 @@ dependencies {
|
||||||
testImplementation(projects.libraries.preferences.test)
|
testImplementation(projects.libraries.preferences.test)
|
||||||
testImplementation(projects.libraries.push.test)
|
testImplementation(projects.libraries.push.test)
|
||||||
testImplementation(projects.libraries.pushproviders.test)
|
testImplementation(projects.libraries.pushproviders.test)
|
||||||
|
testImplementation(projects.features.forward.test)
|
||||||
testImplementation(projects.features.networkmonitor.test)
|
testImplementation(projects.features.networkmonitor.test)
|
||||||
testImplementation(projects.features.rageshake.test)
|
testImplementation(projects.features.rageshake.test)
|
||||||
testImplementation(projects.services.appnavstate.test)
|
testImplementation(projects.services.appnavstate.test)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -56,10 +56,12 @@ class LoggedInAppScopeFlowNode(
|
||||||
plugins = plugins
|
plugins = plugins
|
||||||
), DependencyInjectionGraphOwner {
|
), DependencyInjectionGraphOwner {
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onOpenBugReport()
|
fun navigateToBugReport()
|
||||||
fun onAddAccount()
|
fun navigateToAddAccount()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val callback: Callback = callback()
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
object NavTarget : Parcelable
|
object NavTarget : Parcelable
|
||||||
|
|
||||||
|
|
@ -81,12 +83,12 @@ 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 onOpenBugReport() {
|
override fun navigateToBugReport() {
|
||||||
plugins<Callback>().forEach { it.onOpenBugReport() }
|
callback.navigateToBugReport()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAddAccount() {
|
override fun navigateToAddAccount() {
|
||||||
plugins<Callback>().forEach { it.onAddAccount() }
|
callback.navigateToAddAccount()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return createNode<LoggedInFlowNode>(buildContext, listOf(callback))
|
return createNode<LoggedInFlowNode>(buildContext, listOf(callback))
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -148,10 +148,11 @@ class LoggedInFlowNode(
|
||||||
plugins = plugins
|
plugins = plugins
|
||||||
) {
|
) {
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onOpenBugReport()
|
fun navigateToBugReport()
|
||||||
fun onAddAccount()
|
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,
|
||||||
|
|
@ -282,7 +283,7 @@ class LoggedInFlowNode(
|
||||||
data object Ftue : NavTarget
|
data object Ftue : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data object RoomDirectorySearch : NavTarget
|
data object RoomDirectory : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class IncomingShare(val intent: Intent) : NavTarget
|
data class IncomingShare(val intent: Intent) : NavTarget
|
||||||
|
|
@ -304,46 +305,47 @@ class LoggedInFlowNode(
|
||||||
}
|
}
|
||||||
NavTarget.Home -> {
|
NavTarget.Home -> {
|
||||||
val callback = object : HomeEntryPoint.Callback {
|
val callback = object : HomeEntryPoint.Callback {
|
||||||
override fun onRoomClick(roomId: RoomId) {
|
override fun navigateToRoom(roomId: RoomId) {
|
||||||
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias()))
|
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias()))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSettingsClick() {
|
override fun navigateToSettings() {
|
||||||
backstack.push(NavTarget.Settings())
|
backstack.push(NavTarget.Settings())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartChatClick() {
|
override fun navigateToCreateRoom() {
|
||||||
backstack.push(NavTarget.CreateRoom)
|
backstack.push(NavTarget.CreateRoom)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSetUpRecoveryClick() {
|
override fun navigateToSetUpRecovery() {
|
||||||
backstack.push(NavTarget.SecureBackup(initialElement = SecureBackupEntryPoint.InitialTarget.Root))
|
backstack.push(NavTarget.SecureBackup(initialElement = SecureBackupEntryPoint.InitialTarget.Root))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSessionConfirmRecoveryKeyClick() {
|
override fun navigateToEnterRecoveryKey() {
|
||||||
backstack.push(NavTarget.SecureBackup(initialElement = SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey))
|
backstack.push(NavTarget.SecureBackup(initialElement = SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRoomSettingsClick(roomId: RoomId) {
|
override fun navigateToRoomSettings(roomId: RoomId) {
|
||||||
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Details))
|
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Details))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onReportBugClick() {
|
override fun navigateToBugReport() {
|
||||||
plugins<Callback>().forEach { it.onOpenBugReport() }
|
callback.navigateToBugReport()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
homeEntryPoint
|
homeEntryPoint.createNode(
|
||||||
.nodeBuilder(this, buildContext)
|
parentNode = this,
|
||||||
.callback(callback)
|
buildContext = buildContext,
|
||||||
.build()
|
callback = callback,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
is NavTarget.Room -> {
|
is NavTarget.Room -> {
|
||||||
val joinedRoomCallback = object : JoinedRoomLoadedFlowNode.Callback {
|
val joinedRoomCallback = object : JoinedRoomLoadedFlowNode.Callback {
|
||||||
override fun onOpenRoom(roomId: RoomId, serverNames: List<String>) {
|
override fun navigateToRoom(roomId: RoomId, serverNames: List<String>) {
|
||||||
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), serverNames))
|
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), serverNames))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean) {
|
override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) {
|
||||||
when (data) {
|
when (data) {
|
||||||
is PermalinkData.UserLink -> {
|
is PermalinkData.UserLink -> {
|
||||||
// Should not happen (handled by MessagesNode)
|
// Should not happen (handled by MessagesNode)
|
||||||
|
|
@ -369,7 +371,7 @@ class LoggedInFlowNode(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenGlobalNotificationSettings() {
|
override fun navigateToGlobalNotificationSettings() {
|
||||||
backstack.push(NavTarget.Settings(PreferencesEntryPoint.InitialTarget.NotificationSettings))
|
backstack.push(NavTarget.Settings(PreferencesEntryPoint.InitialTarget.NotificationSettings))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -384,76 +386,85 @@ class LoggedInFlowNode(
|
||||||
}
|
}
|
||||||
is NavTarget.UserProfile -> {
|
is NavTarget.UserProfile -> {
|
||||||
val callback = object : UserProfileEntryPoint.Callback {
|
val callback = object : UserProfileEntryPoint.Callback {
|
||||||
override fun onOpenRoom(roomId: RoomId) {
|
override fun navigateToRoom(roomId: RoomId) {
|
||||||
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias()))
|
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
userProfileEntryPoint.nodeBuilder(this, buildContext)
|
userProfileEntryPoint.createNode(
|
||||||
.params(UserProfileEntryPoint.Params(userId = navTarget.userId))
|
parentNode = this,
|
||||||
.callback(callback)
|
buildContext = buildContext,
|
||||||
.build()
|
params = UserProfileEntryPoint.Params(userId = navTarget.userId),
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
is NavTarget.Settings -> {
|
is NavTarget.Settings -> {
|
||||||
val callback = object : PreferencesEntryPoint.Callback {
|
val callback = object : PreferencesEntryPoint.Callback {
|
||||||
override fun onAddAccount() {
|
override fun navigateToAddAccount() {
|
||||||
plugins<Callback>().forEach { it.onAddAccount() }
|
callback.navigateToAddAccount()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenBugReport() {
|
override fun navigateToBugReport() {
|
||||||
plugins<Callback>().forEach { it.onOpenBugReport() }
|
callback.navigateToAddAccount()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSecureBackupClick() {
|
override fun navigateToSecureBackup() {
|
||||||
backstack.push(NavTarget.SecureBackup())
|
backstack.push(NavTarget.SecureBackup())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenRoomNotificationSettings(roomId: RoomId) {
|
override fun navigateToRoomNotificationSettings(roomId: RoomId) {
|
||||||
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.NotificationSettings))
|
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.NotificationSettings))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun navigateTo(roomId: RoomId, eventId: EventId) {
|
override fun navigateToEvent(roomId: RoomId, eventId: EventId) {
|
||||||
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Root(eventId)))
|
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Root(eventId)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val inputs = PreferencesEntryPoint.Params(navTarget.initialElement)
|
val inputs = PreferencesEntryPoint.Params(navTarget.initialElement)
|
||||||
preferencesEntryPoint.nodeBuilder(this, buildContext)
|
preferencesEntryPoint.createNode(
|
||||||
.params(inputs)
|
parentNode = this,
|
||||||
.callback(callback)
|
buildContext = buildContext,
|
||||||
.build()
|
params = inputs,
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
NavTarget.CreateRoom -> {
|
NavTarget.CreateRoom -> {
|
||||||
val callback = object : StartChatEntryPoint.Callback {
|
val callback = object : StartChatEntryPoint.Callback {
|
||||||
override fun onOpenRoom(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>) {
|
override fun onRoomCreated(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>) {
|
||||||
backstack.replace(NavTarget.Room(roomIdOrAlias = roomIdOrAlias, serverNames = serverNames))
|
backstack.replace(NavTarget.Room(roomIdOrAlias = roomIdOrAlias, serverNames = serverNames))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenRoomDirectory() {
|
override fun navigateToRoomDirectory() {
|
||||||
backstack.push(NavTarget.RoomDirectorySearch)
|
backstack.push(NavTarget.RoomDirectory)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
startChatEntryPoint
|
startChatEntryPoint.createNode(
|
||||||
.nodeBuilder(this, buildContext)
|
parentNode = this,
|
||||||
.callback(callback)
|
buildContext = buildContext,
|
||||||
.build()
|
callback = callback,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
is NavTarget.SecureBackup -> {
|
is NavTarget.SecureBackup -> {
|
||||||
secureBackupEntryPoint.nodeBuilder(this, buildContext)
|
secureBackupEntryPoint.createNode(
|
||||||
.params(SecureBackupEntryPoint.Params(initialElement = navTarget.initialElement))
|
parentNode = this,
|
||||||
.callback(object : SecureBackupEntryPoint.Callback {
|
buildContext = buildContext,
|
||||||
|
params = SecureBackupEntryPoint.Params(initialElement = navTarget.initialElement),
|
||||||
|
callback = object : SecureBackupEntryPoint.Callback {
|
||||||
override fun onDone() {
|
override fun onDone() {
|
||||||
backstack.pop()
|
backstack.pop()
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
.build()
|
)
|
||||||
}
|
}
|
||||||
NavTarget.Ftue -> {
|
NavTarget.Ftue -> {
|
||||||
ftueEntryPoint.createNode(this, buildContext)
|
ftueEntryPoint.createNode(this, buildContext)
|
||||||
}
|
}
|
||||||
NavTarget.RoomDirectorySearch -> {
|
NavTarget.RoomDirectory -> {
|
||||||
roomDirectoryEntryPoint.nodeBuilder(this, buildContext)
|
roomDirectoryEntryPoint.createNode(
|
||||||
.callback(object : RoomDirectoryEntryPoint.Callback {
|
parentNode = this,
|
||||||
override fun onResultClick(roomDescription: RoomDescription) {
|
buildContext = buildContext,
|
||||||
|
callback = object : RoomDirectoryEntryPoint.Callback {
|
||||||
|
override fun navigateToRoom(roomDescription: RoomDescription) {
|
||||||
backstack.push(
|
backstack.push(
|
||||||
NavTarget.Room(
|
NavTarget.Room(
|
||||||
roomIdOrAlias = roomDescription.roomId.toRoomIdOrAlias(),
|
roomIdOrAlias = roomDescription.roomId.toRoomIdOrAlias(),
|
||||||
|
|
@ -462,31 +473,35 @@ class LoggedInFlowNode(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
.build()
|
)
|
||||||
}
|
}
|
||||||
is NavTarget.IncomingShare -> {
|
is NavTarget.IncomingShare -> {
|
||||||
shareEntryPoint.nodeBuilder(this, buildContext)
|
shareEntryPoint.createNode(
|
||||||
.callback(object : ShareEntryPoint.Callback {
|
parentNode = this,
|
||||||
|
buildContext = buildContext,
|
||||||
|
params = ShareEntryPoint.Params(intent = navTarget.intent),
|
||||||
|
callback = object : ShareEntryPoint.Callback {
|
||||||
override fun onDone(roomIds: List<RoomId>) {
|
override fun onDone(roomIds: List<RoomId>) {
|
||||||
navigateUp()
|
navigateUp()
|
||||||
roomIds.singleOrNull()?.let { roomId ->
|
roomIds.singleOrNull()?.let { roomId ->
|
||||||
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias()))
|
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
.params(ShareEntryPoint.Params(intent = navTarget.intent))
|
)
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
is NavTarget.IncomingVerificationRequest -> {
|
is NavTarget.IncomingVerificationRequest -> {
|
||||||
incomingVerificationEntryPoint.nodeBuilder(this, buildContext)
|
incomingVerificationEntryPoint.createNode(
|
||||||
.params(IncomingVerificationEntryPoint.Params(navTarget.data))
|
parentNode = this,
|
||||||
.callback(object : IncomingVerificationEntryPoint.Callback {
|
buildContext = buildContext,
|
||||||
|
params = IncomingVerificationEntryPoint.Params(navTarget.data),
|
||||||
|
callback = object : IncomingVerificationEntryPoint.Callback {
|
||||||
override fun onDone() {
|
override fun onDone() {
|
||||||
backstack.pop()
|
backstack.pop()
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
.build()
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -55,9 +55,10 @@ class NotLoggedInFlowNode(
|
||||||
) : NodeInputs
|
) : NodeInputs
|
||||||
|
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onOpenBugReport()
|
fun navigateToBugReport()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val callback: Callback = callback()
|
||||||
private val inputs = inputs<Params>()
|
private val inputs = inputs<Params>()
|
||||||
|
|
||||||
override fun onBuilt() {
|
override fun onBuilt() {
|
||||||
|
|
@ -78,20 +79,19 @@ class NotLoggedInFlowNode(
|
||||||
return when (navTarget) {
|
return when (navTarget) {
|
||||||
NavTarget.Root -> {
|
NavTarget.Root -> {
|
||||||
val callback = object : LoginEntryPoint.Callback {
|
val callback = object : LoginEntryPoint.Callback {
|
||||||
override fun onReportProblem() {
|
override fun navigateToBugReport() {
|
||||||
plugins<Callback>().forEach { it.onOpenBugReport() }
|
callback.navigateToBugReport()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loginEntryPoint
|
loginEntryPoint.createNode(
|
||||||
.nodeBuilder(this, buildContext)
|
parentNode = this,
|
||||||
.params(
|
buildContext = buildContext,
|
||||||
LoginEntryPoint.Params(
|
params = LoginEntryPoint.Params(
|
||||||
accountProvider = inputs.loginParams?.accountProvider,
|
accountProvider = inputs.loginParams?.accountProvider,
|
||||||
loginHint = inputs.loginParams?.loginHint,
|
loginHint = inputs.loginParams?.loginHint,
|
||||||
|
),
|
||||||
|
callback = callback,
|
||||||
)
|
)
|
||||||
)
|
|
||||||
.callback(callback)
|
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -227,11 +227,11 @@ class RootFlowNode(
|
||||||
}
|
}
|
||||||
val inputs = LoggedInAppScopeFlowNode.Inputs(matrixClient)
|
val inputs = LoggedInAppScopeFlowNode.Inputs(matrixClient)
|
||||||
val callback = object : LoggedInAppScopeFlowNode.Callback {
|
val callback = object : LoggedInAppScopeFlowNode.Callback {
|
||||||
override fun onOpenBugReport() {
|
override fun navigateToBugReport() {
|
||||||
backstack.push(NavTarget.BugReport)
|
backstack.push(NavTarget.BugReport)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAddAccount() {
|
override fun navigateToAddAccount() {
|
||||||
backstack.push(NavTarget.NotLoggedInFlow(null))
|
backstack.push(NavTarget.NotLoggedInFlow(null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -239,7 +239,7 @@ class RootFlowNode(
|
||||||
}
|
}
|
||||||
is NavTarget.NotLoggedInFlow -> {
|
is NavTarget.NotLoggedInFlow -> {
|
||||||
val callback = object : NotLoggedInFlowNode.Callback {
|
val callback = object : NotLoggedInFlowNode.Callback {
|
||||||
override fun onOpenBugReport() {
|
override fun navigateToBugReport() {
|
||||||
backstack.push(NavTarget.BugReport)
|
backstack.push(NavTarget.BugReport)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -249,11 +249,13 @@ class RootFlowNode(
|
||||||
createNode<NotLoggedInFlowNode>(buildContext, plugins = listOf(params, callback))
|
createNode<NotLoggedInFlowNode>(buildContext, plugins = listOf(params, callback))
|
||||||
}
|
}
|
||||||
is NavTarget.SignedOutFlow -> {
|
is NavTarget.SignedOutFlow -> {
|
||||||
signedOutEntryPoint.nodeBuilder(this, buildContext).params(
|
signedOutEntryPoint.createNode(
|
||||||
SignedOutEntryPoint.Params(
|
parentNode = this,
|
||||||
sessionId = navTarget.sessionId
|
buildContext = buildContext,
|
||||||
|
params = SignedOutEntryPoint.Params(
|
||||||
|
sessionId = navTarget.sessionId,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
).build()
|
|
||||||
}
|
}
|
||||||
NavTarget.SplashScreen -> emptyNode(buildContext)
|
NavTarget.SplashScreen -> emptyNode(buildContext)
|
||||||
NavTarget.BugReport -> {
|
NavTarget.BugReport -> {
|
||||||
|
|
@ -262,11 +264,15 @@ class RootFlowNode(
|
||||||
backstack.pop()
|
backstack.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bugReportEntryPoint.nodeBuilder(this, buildContext).callback(callback).build()
|
bugReportEntryPoint.createNode(
|
||||||
|
parentNode = this,
|
||||||
|
buildContext = buildContext,
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
is NavTarget.AccountSelect -> {
|
is NavTarget.AccountSelect -> {
|
||||||
val callback: AccountSelectEntryPoint.Callback = object : AccountSelectEntryPoint.Callback {
|
val callback: AccountSelectEntryPoint.Callback = object : AccountSelectEntryPoint.Callback {
|
||||||
override fun onSelectAccount(sessionId: SessionId) {
|
override fun onAccountSelected(sessionId: SessionId) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
if (sessionId == navTarget.currentSessionId) {
|
if (sessionId == navTarget.currentSessionId) {
|
||||||
// Ensure that the account selection Node is removed from the backstack
|
// Ensure that the account selection Node is removed from the backstack
|
||||||
|
|
@ -287,7 +293,11 @@ class RootFlowNode(
|
||||||
backstack.pop()
|
backstack.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
accountSelectEntryPoint.nodeBuilder(this, buildContext).callback(callback).build()
|
accountSelectEntryPoint.createNode(
|
||||||
|
parentNode = this,
|
||||||
|
buildContext = buildContext,
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -180,10 +180,12 @@ class RoomFlowNode(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val params = Params(navTarget.roomAlias)
|
val params = Params(navTarget.roomAlias)
|
||||||
roomAliasResolverEntryPoint.nodeBuilder(this, buildContext)
|
roomAliasResolverEntryPoint.createNode(
|
||||||
.callback(callback)
|
parentNode = this,
|
||||||
.params(params)
|
buildContext = buildContext,
|
||||||
.build()
|
params = params,
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
is NavTarget.JoinRoom -> {
|
is NavTarget.JoinRoom -> {
|
||||||
val inputs = JoinRoomEntryPoint.Inputs(
|
val inputs = JoinRoomEntryPoint.Inputs(
|
||||||
|
|
@ -193,7 +195,11 @@ class RoomFlowNode(
|
||||||
serverNames = navTarget.serverNames,
|
serverNames = navTarget.serverNames,
|
||||||
trigger = navTarget.trigger,
|
trigger = navTarget.trigger,
|
||||||
)
|
)
|
||||||
joinRoomEntryPoint.createNode(this, buildContext, inputs)
|
joinRoomEntryPoint.createNode(
|
||||||
|
parentNode = this,
|
||||||
|
buildContext = buildContext,
|
||||||
|
inputs = inputs,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
is NavTarget.JoinedRoom -> {
|
is NavTarget.JoinedRoom -> {
|
||||||
val roomFlowNodeCallback = plugins<JoinedRoomLoadedFlowNode.Callback>()
|
val roomFlowNodeCallback = plugins<JoinedRoomLoadedFlowNode.Callback>()
|
||||||
|
|
@ -205,10 +211,12 @@ class RoomFlowNode(
|
||||||
}
|
}
|
||||||
is NavTarget.JoinedSpace -> {
|
is NavTarget.JoinedSpace -> {
|
||||||
val spaceCallback = plugins<SpaceEntryPoint.Callback>().single()
|
val spaceCallback = plugins<SpaceEntryPoint.Callback>().single()
|
||||||
spaceEntryPoint.nodeBuilder(this, buildContext)
|
spaceEntryPoint.createNode(
|
||||||
.inputs(SpaceEntryPoint.Inputs(roomId = navTarget.spaceId))
|
parentNode = this,
|
||||||
.callback(spaceCallback)
|
buildContext = buildContext,
|
||||||
.build()
|
inputs = SpaceEntryPoint.Inputs(roomId = navTarget.spaceId),
|
||||||
|
callback = spaceCallback,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -76,9 +77,9 @@ class JoinedRoomLoadedFlowNode(
|
||||||
plugins = plugins,
|
plugins = plugins,
|
||||||
), DependencyInjectionGraphOwner {
|
), DependencyInjectionGraphOwner {
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onOpenRoom(roomId: RoomId, serverNames: List<String>)
|
fun navigateToRoom(roomId: RoomId, serverNames: List<String>)
|
||||||
fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean)
|
fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean)
|
||||||
fun onOpenGlobalNotificationSettings()
|
fun navigateToGlobalNotificationSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Inputs(
|
data class Inputs(
|
||||||
|
|
@ -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 {
|
||||||
|
|
@ -123,26 +124,28 @@ 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 onOpenGlobalNotificationSettings() {
|
override fun navigateToGlobalNotificationSettings() {
|
||||||
callbacks.forEach { it.onOpenGlobalNotificationSettings() }
|
callback.navigateToGlobalNotificationSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenRoom(roomId: RoomId, serverNames: List<String>) {
|
override fun navigateToRoom(roomId: RoomId, serverNames: List<String>) {
|
||||||
callbacks.forEach { it.onOpenRoom(roomId, serverNames) }
|
callback.navigateToRoom(roomId, serverNames)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean) {
|
override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) {
|
||||||
callbacks.forEach { it.onPermalinkClick(data, pushToBackstack) }
|
callback.handlePermalinkClick(data, pushToBackstack)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun forwardEvent(eventId: EventId) {
|
override fun startForwardEventFlow(eventId: EventId) {
|
||||||
backstack.push(NavTarget.ForwardEvent(eventId))
|
backstack.push(NavTarget.ForwardEvent(eventId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return roomDetailsEntryPoint.nodeBuilder(this, buildContext)
|
return roomDetailsEntryPoint.createNode(
|
||||||
.params(RoomDetailsEntryPoint.Params(initialTarget))
|
parentNode = this,
|
||||||
.callback(callback)
|
buildContext = buildContext,
|
||||||
.build()
|
params = RoomDetailsEntryPoint.Params(initialTarget),
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||||
|
|
@ -172,36 +175,40 @@ 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.onOpenRoom(roomId, emptyList()) }
|
callback.navigateToRoom(roomId, emptyList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
forwardEntryPoint.nodeBuilder(this, buildContext)
|
forwardEntryPoint.createNode(
|
||||||
.params(params)
|
parentNode = this,
|
||||||
.callback(callback)
|
buildContext = buildContext,
|
||||||
.build()
|
params = params,
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSpaceNode(buildContext: BuildContext): Node {
|
private fun createSpaceNode(buildContext: BuildContext): Node {
|
||||||
val callback = object : SpaceEntryPoint.Callback {
|
val callback = object : SpaceEntryPoint.Callback {
|
||||||
override fun onOpenRoom(roomId: RoomId, viaParameters: List<String>) {
|
override fun navigateToRoom(roomId: RoomId, viaParameters: List<String>) {
|
||||||
callbacks.forEach { it.onOpenRoom(roomId, viaParameters) }
|
callback.navigateToRoom(roomId, viaParameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenDetails() {
|
override fun navigateToRoomDetails() {
|
||||||
backstack.push(NavTarget.RoomDetails)
|
backstack.push(NavTarget.RoomDetails)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenMemberList() {
|
override fun navigateToRoomMemberList() {
|
||||||
backstack.push(NavTarget.RoomMemberList)
|
backstack.push(NavTarget.RoomMemberList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return spaceEntryPoint.nodeBuilder(this, buildContext)
|
return spaceEntryPoint.createNode(
|
||||||
.inputs(SpaceEntryPoint.Inputs(roomId = inputs.room.roomId))
|
parentNode = this,
|
||||||
.callback(callback)
|
buildContext = buildContext,
|
||||||
.build()
|
inputs = SpaceEntryPoint.Inputs(roomId = inputs.room.roomId),
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createMessagesNode(
|
private fun createMessagesNode(
|
||||||
|
|
@ -209,33 +216,35 @@ class JoinedRoomLoadedFlowNode(
|
||||||
navTarget: NavTarget.Messages,
|
navTarget: NavTarget.Messages,
|
||||||
): Node {
|
): Node {
|
||||||
val callback = object : MessagesEntryPoint.Callback {
|
val callback = object : MessagesEntryPoint.Callback {
|
||||||
override fun onRoomDetailsClick() {
|
override fun navigateToRoomDetails() {
|
||||||
backstack.push(NavTarget.RoomDetails)
|
backstack.push(NavTarget.RoomDetails)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUserDataClick(userId: UserId) {
|
override fun navigateToRoomMemberDetails(userId: UserId) {
|
||||||
backstack.push(NavTarget.RoomMemberDetails(userId))
|
backstack.push(NavTarget.RoomMemberDetails(userId))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean) {
|
override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) {
|
||||||
callbacks.forEach { it.onPermalinkClick(data, pushToBackstack) }
|
callback.handlePermalinkClick(data, pushToBackstack)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun forwardEvent(eventId: EventId) {
|
override fun forwardEvent(eventId: EventId) {
|
||||||
backstack.push(NavTarget.ForwardEvent(eventId))
|
backstack.push(NavTarget.ForwardEvent(eventId))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openRoom(roomId: RoomId) {
|
override fun navigateToRoom(roomId: RoomId) {
|
||||||
callbacks.forEach { it.onOpenRoom(roomId, emptyList()) }
|
callback.navigateToRoom(roomId, emptyList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val params = MessagesEntryPoint.Params(
|
val params = MessagesEntryPoint.Params(
|
||||||
MessagesEntryPoint.InitialTarget.Messages(navTarget.focusedEventId)
|
MessagesEntryPoint.InitialTarget.Messages(navTarget.focusedEventId)
|
||||||
)
|
)
|
||||||
return messagesEntryPoint.nodeBuilder(this, buildContext)
|
return messagesEntryPoint.createNode(
|
||||||
.params(params)
|
parentNode = this,
|
||||||
.callback(callback)
|
buildContext = buildContext,
|
||||||
.build()
|
params = params,
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed interface NavTarget : Parcelable {
|
sealed interface NavTarget : Parcelable {
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ import com.bumble.appyx.testing.unit.common.helper.parentNodeTestHelper
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.appnav.di.RoomGraphFactory
|
import io.element.android.appnav.di.RoomGraphFactory
|
||||||
import io.element.android.appnav.room.RoomNavigationTarget
|
import io.element.android.appnav.room.RoomNavigationTarget
|
||||||
|
import io.element.android.appnav.room.joined.FakeJoinedRoomLoadedFlowNodeCallback
|
||||||
import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode
|
import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode
|
||||||
import io.element.android.features.forward.api.ForwardEntryPoint
|
import io.element.android.features.forward.api.ForwardEntryPoint
|
||||||
|
import io.element.android.features.forward.test.FakeForwardEntryPoint
|
||||||
import io.element.android.features.messages.api.MessagesEntryPoint
|
import io.element.android.features.messages.api.MessagesEntryPoint
|
||||||
import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint
|
import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint
|
||||||
import io.element.android.features.space.api.SpaceEntryPoint
|
import io.element.android.features.space.api.SpaceEntryPoint
|
||||||
|
|
@ -48,29 +50,20 @@ class JoinedRoomLoadedFlowNodeTest {
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val mainDispatcherRule = MainDispatcherRule()
|
val mainDispatcherRule = MainDispatcherRule()
|
||||||
|
|
||||||
private class FakeMessagesEntryPoint : MessagesEntryPoint, MessagesEntryPoint.NodeBuilder {
|
private class FakeMessagesEntryPoint : MessagesEntryPoint {
|
||||||
var buildContext: BuildContext? = null
|
|
||||||
var nodeId: String? = null
|
var nodeId: String? = null
|
||||||
var parameters: MessagesEntryPoint.Params? = null
|
var parameters: MessagesEntryPoint.Params? = null
|
||||||
var callback: MessagesEntryPoint.Callback? = null
|
var callback: MessagesEntryPoint.Callback? = null
|
||||||
|
|
||||||
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): MessagesEntryPoint.NodeBuilder {
|
override fun createNode(
|
||||||
this.buildContext = buildContext
|
parentNode: Node,
|
||||||
return this
|
buildContext: BuildContext,
|
||||||
}
|
params: MessagesEntryPoint.Params,
|
||||||
|
callback: MessagesEntryPoint.Callback,
|
||||||
override fun params(params: MessagesEntryPoint.Params): MessagesEntryPoint.NodeBuilder {
|
): Node {
|
||||||
parameters = params
|
parameters = params
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun callback(callback: MessagesEntryPoint.Callback): MessagesEntryPoint.NodeBuilder {
|
|
||||||
this.callback = callback
|
this.callback = callback
|
||||||
return this
|
return node(buildContext) {}.also {
|
||||||
}
|
|
||||||
|
|
||||||
override fun build(): Node {
|
|
||||||
return node(buildContext!!) {}.also {
|
|
||||||
nodeId = it.id
|
nodeId = it.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -85,56 +78,28 @@ class JoinedRoomLoadedFlowNodeTest {
|
||||||
private class FakeRoomDetailsEntryPoint : RoomDetailsEntryPoint {
|
private class FakeRoomDetailsEntryPoint : RoomDetailsEntryPoint {
|
||||||
var nodeId: String? = null
|
var nodeId: String? = null
|
||||||
|
|
||||||
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): RoomDetailsEntryPoint.NodeBuilder {
|
override fun createNode(
|
||||||
return object : RoomDetailsEntryPoint.NodeBuilder {
|
parentNode: Node,
|
||||||
override fun params(params: RoomDetailsEntryPoint.Params): RoomDetailsEntryPoint.NodeBuilder {
|
buildContext: BuildContext,
|
||||||
return this
|
params: RoomDetailsEntryPoint.Params,
|
||||||
}
|
callback: RoomDetailsEntryPoint.Callback,
|
||||||
|
) = node(buildContext) {}.also {
|
||||||
override fun callback(callback: RoomDetailsEntryPoint.Callback): RoomDetailsEntryPoint.NodeBuilder {
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun build(): Node {
|
|
||||||
return node(buildContext) {}.also {
|
|
||||||
nodeId = it.id
|
nodeId = it.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FakeSpaceEntryPoint : SpaceEntryPoint {
|
private class FakeSpaceEntryPoint : SpaceEntryPoint {
|
||||||
var nodeId: String? = null
|
var nodeId: String? = null
|
||||||
|
|
||||||
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): SpaceEntryPoint.NodeBuilder {
|
override fun createNode(
|
||||||
return object : SpaceEntryPoint.NodeBuilder {
|
parentNode: Node,
|
||||||
override fun inputs(inputs: SpaceEntryPoint.Inputs): SpaceEntryPoint.NodeBuilder {
|
buildContext: BuildContext,
|
||||||
return this
|
inputs: SpaceEntryPoint.Inputs,
|
||||||
}
|
callback: SpaceEntryPoint.Callback,
|
||||||
|
) = node(buildContext) {}.also {
|
||||||
override fun callback(callback: SpaceEntryPoint.Callback): SpaceEntryPoint.NodeBuilder {
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun build(): Node {
|
|
||||||
return node(buildContext) {}.also {
|
|
||||||
nodeId = it.id
|
nodeId = it.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FakeForwardEntryPoint : ForwardEntryPoint {
|
|
||||||
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): ForwardEntryPoint.NodeBuilder {
|
|
||||||
return object : ForwardEntryPoint.NodeBuilder {
|
|
||||||
override fun params(params: ForwardEntryPoint.Params) = this
|
|
||||||
override fun callback(callback: ForwardEntryPoint.Callback) = this
|
|
||||||
override fun build() = node(buildContext) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun TestScope.createJoinedRoomLoadedFlowNode(
|
private fun TestScope.createJoinedRoomLoadedFlowNode(
|
||||||
plugins: List<Plugin>,
|
plugins: List<Plugin>,
|
||||||
|
|
@ -165,7 +130,7 @@ class JoinedRoomLoadedFlowNodeTest {
|
||||||
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
|
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
|
||||||
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Root())
|
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Root())
|
||||||
val roomFlowNode = createJoinedRoomLoadedFlowNode(
|
val roomFlowNode = createJoinedRoomLoadedFlowNode(
|
||||||
plugins = listOf(inputs),
|
plugins = listOf(inputs, FakeJoinedRoomLoadedFlowNodeCallback()),
|
||||||
messagesEntryPoint = fakeMessagesEntryPoint,
|
messagesEntryPoint = fakeMessagesEntryPoint,
|
||||||
)
|
)
|
||||||
// WHEN
|
// WHEN
|
||||||
|
|
@ -185,7 +150,7 @@ class JoinedRoomLoadedFlowNodeTest {
|
||||||
val spaceEntryPoint = FakeSpaceEntryPoint()
|
val spaceEntryPoint = FakeSpaceEntryPoint()
|
||||||
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Root())
|
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Root())
|
||||||
val roomFlowNode = createJoinedRoomLoadedFlowNode(
|
val roomFlowNode = createJoinedRoomLoadedFlowNode(
|
||||||
plugins = listOf(inputs),
|
plugins = listOf(inputs, FakeJoinedRoomLoadedFlowNodeCallback()),
|
||||||
spaceEntryPoint = spaceEntryPoint,
|
spaceEntryPoint = spaceEntryPoint,
|
||||||
)
|
)
|
||||||
// WHEN
|
// WHEN
|
||||||
|
|
@ -206,13 +171,13 @@ class JoinedRoomLoadedFlowNodeTest {
|
||||||
val fakeRoomDetailsEntryPoint = FakeRoomDetailsEntryPoint()
|
val fakeRoomDetailsEntryPoint = FakeRoomDetailsEntryPoint()
|
||||||
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Root())
|
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Root())
|
||||||
val roomFlowNode = createJoinedRoomLoadedFlowNode(
|
val roomFlowNode = createJoinedRoomLoadedFlowNode(
|
||||||
plugins = listOf(inputs),
|
plugins = listOf(inputs, FakeJoinedRoomLoadedFlowNodeCallback()),
|
||||||
messagesEntryPoint = fakeMessagesEntryPoint,
|
messagesEntryPoint = fakeMessagesEntryPoint,
|
||||||
roomDetailsEntryPoint = fakeRoomDetailsEntryPoint,
|
roomDetailsEntryPoint = fakeRoomDetailsEntryPoint,
|
||||||
)
|
)
|
||||||
val roomFlowNodeTestHelper = roomFlowNode.parentNodeTestHelper()
|
val roomFlowNodeTestHelper = roomFlowNode.parentNodeTestHelper()
|
||||||
// WHEN
|
// WHEN
|
||||||
fakeMessagesEntryPoint.callback?.onRoomDetailsClick()
|
fakeMessagesEntryPoint.callback?.navigateToRoomDetails()
|
||||||
// THEN
|
// THEN
|
||||||
roomFlowNodeTestHelper.assertChildHasLifecycle(JoinedRoomLoadedFlowNode.NavTarget.RoomDetails, Lifecycle.State.CREATED)
|
roomFlowNodeTestHelper.assertChildHasLifecycle(JoinedRoomLoadedFlowNode.NavTarget.RoomDetails, Lifecycle.State.CREATED)
|
||||||
val roomDetailsNode = roomFlowNode.childNode(JoinedRoomLoadedFlowNode.NavTarget.RoomDetails)!!
|
val roomDetailsNode = roomFlowNode.childNode(JoinedRoomLoadedFlowNode.NavTarget.RoomDetails)!!
|
||||||
|
|
@ -228,7 +193,7 @@ class JoinedRoomLoadedFlowNodeTest {
|
||||||
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Root())
|
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Root())
|
||||||
val activeRoomsHolder = ActiveRoomsHolder()
|
val activeRoomsHolder = ActiveRoomsHolder()
|
||||||
val roomFlowNode = createJoinedRoomLoadedFlowNode(
|
val roomFlowNode = createJoinedRoomLoadedFlowNode(
|
||||||
plugins = listOf(inputs),
|
plugins = listOf(inputs, FakeJoinedRoomLoadedFlowNodeCallback()),
|
||||||
messagesEntryPoint = fakeMessagesEntryPoint,
|
messagesEntryPoint = fakeMessagesEntryPoint,
|
||||||
roomDetailsEntryPoint = fakeRoomDetailsEntryPoint,
|
roomDetailsEntryPoint = fakeRoomDetailsEntryPoint,
|
||||||
activeRoomsHolder = activeRoomsHolder,
|
activeRoomsHolder = activeRoomsHolder,
|
||||||
|
|
@ -253,7 +218,7 @@ class JoinedRoomLoadedFlowNodeTest {
|
||||||
addRoom(room)
|
addRoom(room)
|
||||||
}
|
}
|
||||||
val roomFlowNode = createJoinedRoomLoadedFlowNode(
|
val roomFlowNode = createJoinedRoomLoadedFlowNode(
|
||||||
plugins = listOf(inputs),
|
plugins = listOf(inputs, FakeJoinedRoomLoadedFlowNodeCallback()),
|
||||||
messagesEntryPoint = fakeMessagesEntryPoint,
|
messagesEntryPoint = fakeMessagesEntryPoint,
|
||||||
roomDetailsEntryPoint = fakeRoomDetailsEntryPoint,
|
roomDetailsEntryPoint = fakeRoomDetailsEntryPoint,
|
||||||
activeRoomsHolder = activeRoomsHolder,
|
activeRoomsHolder = activeRoomsHolder,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.appnav.room.joined
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
|
||||||
|
class FakeJoinedRoomLoadedFlowNodeCallback : JoinedRoomLoadedFlowNode.Callback {
|
||||||
|
override fun navigateToRoom(roomId: RoomId, serverNames: List<String>) = lambdaError()
|
||||||
|
override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) = lambdaError()
|
||||||
|
override fun navigateToGlobalNotificationSettings() = lambdaError()
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.changeroommemberroes.api
|
package io.element.android.features.changeroommemberroles.api
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -15,13 +15,12 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
|
|
||||||
fun interface ChangeRoomMemberRolesEntryPoint : FeatureEntryPoint {
|
fun interface ChangeRoomMemberRolesEntryPoint : FeatureEntryPoint {
|
||||||
fun builder(parentNode: Node, buildContext: BuildContext): Builder
|
fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
interface Builder {
|
buildContext: BuildContext,
|
||||||
fun room(room: JoinedRoom): Builder
|
room: JoinedRoom,
|
||||||
fun listType(changeRoomMemberRolesListType: ChangeRoomMemberRolesListType): Builder
|
listType: ChangeRoomMemberRolesListType,
|
||||||
fun build(): Node
|
): Node
|
||||||
}
|
|
||||||
|
|
||||||
interface NodeProxy {
|
interface NodeProxy {
|
||||||
val roomId: RoomId
|
val roomId: RoomId
|
||||||
|
|
@ -17,7 +17,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.features.changeroommemberroes.api.ChangeRoomMemberRolesListType
|
import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType
|
||||||
import io.element.android.libraries.architecture.NodeInputs
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
import io.element.android.libraries.architecture.appyx.launchMolecule
|
import io.element.android.libraries.architecture.appyx.launchMolecule
|
||||||
import io.element.android.libraries.architecture.inputs
|
import io.element.android.libraries.architecture.inputs
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ 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.RoomGraphFactory
|
import io.element.android.appnav.di.RoomGraphFactory
|
||||||
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint
|
import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesEntryPoint
|
||||||
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType
|
import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType
|
||||||
import io.element.android.libraries.architecture.NodeInputs
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -10,37 +10,25 @@ package io.element.android.features.changeroommemberroles.impl
|
||||||
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 dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint
|
import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesEntryPoint
|
||||||
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType
|
import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType
|
||||||
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.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
|
|
||||||
@ContributesBinding(SessionScope::class)
|
@ContributesBinding(SessionScope::class)
|
||||||
class DefaultChangeRoomMemberRolesEntyPoint : ChangeRoomMemberRolesEntryPoint {
|
class DefaultChangeRoomMemberRolesEntyPoint : ChangeRoomMemberRolesEntryPoint {
|
||||||
override fun builder(parentNode: Node, buildContext: BuildContext): ChangeRoomMemberRolesEntryPoint.Builder {
|
override fun createNode(
|
||||||
return object : ChangeRoomMemberRolesEntryPoint.Builder {
|
parentNode: Node,
|
||||||
private lateinit var changeRoomMemberRolesListType: ChangeRoomMemberRolesListType
|
buildContext: BuildContext,
|
||||||
private lateinit var room: JoinedRoom
|
room: JoinedRoom,
|
||||||
|
listType: ChangeRoomMemberRolesListType,
|
||||||
override fun room(room: JoinedRoom): ChangeRoomMemberRolesEntryPoint.Builder {
|
): Node {
|
||||||
this.room = room
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun listType(changeRoomMemberRolesListType: ChangeRoomMemberRolesListType): ChangeRoomMemberRolesEntryPoint.Builder {
|
|
||||||
this.changeRoomMemberRolesListType = changeRoomMemberRolesListType
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun build(): Node {
|
|
||||||
return parentNode.createNode<ChangeRoomMemberRolesRootNode>(
|
return parentNode.createNode<ChangeRoomMemberRolesRootNode>(
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
plugins = listOf(
|
plugins = listOf(
|
||||||
ChangeRoomMemberRolesRootNode.Inputs(joinedRoom = room, listType = changeRoomMemberRolesListType),
|
ChangeRoomMemberRolesRootNode.Inputs(joinedRoom = room, listType = listType),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
package io.element.android.features.changeroommemberroles.impl
|
package io.element.android.features.changeroommemberroles.impl
|
||||||
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType
|
import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ package io.element.android.features.changeroommemberroles.impl
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.bumble.appyx.core.modality.BuildContext
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType
|
import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType
|
||||||
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
||||||
import io.element.android.tests.testutils.node.TestParentNode
|
import io.element.android.tests.testutils.node.TestParentNode
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
|
|
@ -31,10 +31,12 @@ class DefaultChangeRoomMemberRolesEntyPointTest {
|
||||||
}
|
}
|
||||||
val room = FakeJoinedRoom()
|
val room = FakeJoinedRoom()
|
||||||
val listType = ChangeRoomMemberRolesListType.Admins
|
val listType = ChangeRoomMemberRolesListType.Admins
|
||||||
val result = entryPoint.builder(parentNode, BuildContext.root(null))
|
val result = entryPoint.createNode(
|
||||||
.room(FakeJoinedRoom())
|
parentNode = parentNode,
|
||||||
.listType(listType)
|
buildContext = BuildContext.root(null),
|
||||||
.build()
|
room = FakeJoinedRoom(),
|
||||||
|
listType = listType,
|
||||||
|
)
|
||||||
assertThat(result).isInstanceOf(ChangeRoomMemberRolesRootNode::class.java)
|
assertThat(result).isInstanceOf(ChangeRoomMemberRolesRootNode::class.java)
|
||||||
// Search for the Inputs plugin
|
// Search for the Inputs plugin
|
||||||
val input = result.plugins.filterIsInstance<ChangeRoomMemberRolesRootNode.Inputs>().single()
|
val input = result.plugins.filterIsInstance<ChangeRoomMemberRolesRootNode.Inputs>().single()
|
||||||
|
|
|
||||||
21
features/changeroommemberroles/test/build.gradle.kts
Normal file
21
features/changeroommemberroles/test/build.gradle.kts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("io.element.android-library")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "io.element.android.features.changeroommemberroles.test"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(projects.features.changeroommemberroles.api)
|
||||||
|
implementation(projects.libraries.architecture)
|
||||||
|
implementation(projects.libraries.matrix.api)
|
||||||
|
implementation(projects.tests.testutils)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.features.changeroommemberroles.test
|
||||||
|
|
||||||
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
import com.bumble.appyx.core.node.Node
|
||||||
|
import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesEntryPoint
|
||||||
|
import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType
|
||||||
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
|
||||||
|
class FakeChangeRoomMemberRolesEntryPoint : ChangeRoomMemberRolesEntryPoint {
|
||||||
|
override fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
room: JoinedRoom,
|
||||||
|
listType: ChangeRoomMemberRolesListType,
|
||||||
|
): Node {
|
||||||
|
lambdaError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,12 +14,11 @@ import io.element.android.libraries.architecture.FeatureEntryPoint
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
||||||
interface CreateRoomEntryPoint : FeatureEntryPoint {
|
interface CreateRoomEntryPoint : FeatureEntryPoint {
|
||||||
fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder
|
fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
interface NodeBuilder {
|
buildContext: BuildContext,
|
||||||
fun callback(callback: Callback): NodeBuilder
|
callback: Callback,
|
||||||
fun build(): Node
|
): Node
|
||||||
}
|
|
||||||
|
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onRoomCreated(roomId: RoomId)
|
fun onRoomCreated(roomId: RoomId)
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ package io.element.android.features.createroom.impl
|
||||||
|
|
||||||
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 dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
||||||
import io.element.android.libraries.architecture.createNode
|
import io.element.android.libraries.architecture.createNode
|
||||||
|
|
@ -17,18 +16,11 @@ import io.element.android.libraries.di.SessionScope
|
||||||
|
|
||||||
@ContributesBinding(SessionScope::class)
|
@ContributesBinding(SessionScope::class)
|
||||||
class DefaultCreateRoomEntryPoint : CreateRoomEntryPoint {
|
class DefaultCreateRoomEntryPoint : CreateRoomEntryPoint {
|
||||||
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): CreateRoomEntryPoint.NodeBuilder {
|
override fun createNode(
|
||||||
val plugins = ArrayList<Plugin>()
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
return object : CreateRoomEntryPoint.NodeBuilder {
|
callback: CreateRoomEntryPoint.Callback,
|
||||||
override fun callback(callback: CreateRoomEntryPoint.Callback): CreateRoomEntryPoint.NodeBuilder {
|
): Node {
|
||||||
plugins += callback
|
return parentNode.createNode<CreateRoomFlowNode>(buildContext, listOf(callback))
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun build(): Node {
|
|
||||||
return parentNode.createNode<CreateRoomFlowNode>(buildContext, plugins)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,9 +38,11 @@ class DefaultCreateRoomEntryPointTest {
|
||||||
val callback = object : CreateRoomEntryPoint.Callback {
|
val callback = object : CreateRoomEntryPoint.Callback {
|
||||||
override fun onRoomCreated(roomId: RoomId) = lambdaError()
|
override fun onRoomCreated(roomId: RoomId) = lambdaError()
|
||||||
}
|
}
|
||||||
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
|
val result = entryPoint.createNode(
|
||||||
.callback(callback)
|
parentNode = parentNode,
|
||||||
.build()
|
buildContext = BuildContext.root(null),
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
assertThat(result.plugins).contains(callback)
|
assertThat(result.plugins).contains(callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
19
features/createroom/test/build.gradle.kts
Normal file
19
features/createroom/test/build.gradle.kts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.
|
||||||
|
*/
|
||||||
|
plugins {
|
||||||
|
id("io.element.android-library")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "io.element.android.features.createroom.test"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(projects.features.createroom.api)
|
||||||
|
implementation(projects.libraries.architecture)
|
||||||
|
implementation(projects.tests.testutils)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.features.createroom.api
|
||||||
|
|
||||||
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
import com.bumble.appyx.core.node.Node
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
|
||||||
|
class FakeCreateRoomEntryPoint : CreateRoomEntryPoint {
|
||||||
|
override fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
callback: CreateRoomEntryPoint.Callback,
|
||||||
|
): Node = lambdaError()
|
||||||
|
}
|
||||||
19
features/deactivation/test/build.gradle.kts
Normal file
19
features/deactivation/test/build.gradle.kts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.
|
||||||
|
*/
|
||||||
|
plugins {
|
||||||
|
id("io.element.android-library")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "io.element.android.features.deactivation.test"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(projects.features.deactivation.api)
|
||||||
|
implementation(projects.libraries.architecture)
|
||||||
|
implementation(projects.tests.testutils)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.features.deactivation.test
|
||||||
|
|
||||||
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
import com.bumble.appyx.core.node.Node
|
||||||
|
import io.element.android.features.deactivation.api.AccountDeactivationEntryPoint
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
|
||||||
|
class FakeAccountDeactivationEntryPoint : AccountDeactivationEntryPoint {
|
||||||
|
override fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
): Node {
|
||||||
|
lambdaError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,12 +17,6 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.timeline.TimelineProvider
|
import io.element.android.libraries.matrix.api.timeline.TimelineProvider
|
||||||
|
|
||||||
interface ForwardEntryPoint : FeatureEntryPoint {
|
interface ForwardEntryPoint : FeatureEntryPoint {
|
||||||
interface NodeBuilder {
|
|
||||||
fun params(params: Params): NodeBuilder
|
|
||||||
fun callback(callback: Callback): NodeBuilder
|
|
||||||
fun build(): Node
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onDone(roomIds: List<RoomId>)
|
fun onDone(roomIds: List<RoomId>)
|
||||||
}
|
}
|
||||||
|
|
@ -32,5 +26,10 @@ interface ForwardEntryPoint : FeatureEntryPoint {
|
||||||
val timelineProvider: TimelineProvider,
|
val timelineProvider: TimelineProvider,
|
||||||
) : NodeInputs
|
) : NodeInputs
|
||||||
|
|
||||||
fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder
|
fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
params: Params,
|
||||||
|
callback: Callback,
|
||||||
|
): Node
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,5 +34,6 @@ dependencies {
|
||||||
|
|
||||||
testCommonDependencies(libs, true)
|
testCommonDependencies(libs, true)
|
||||||
testImplementation(projects.libraries.matrix.test)
|
testImplementation(projects.libraries.matrix.test)
|
||||||
|
testImplementation(projects.libraries.roomselect.test)
|
||||||
testImplementation(projects.libraries.testtags)
|
testImplementation(projects.libraries.testtags)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ package io.element.android.features.forward.impl
|
||||||
|
|
||||||
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 dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import io.element.android.features.forward.api.ForwardEntryPoint
|
import io.element.android.features.forward.api.ForwardEntryPoint
|
||||||
import io.element.android.libraries.architecture.createNode
|
import io.element.android.libraries.architecture.createNode
|
||||||
|
|
@ -17,26 +16,21 @@ import io.element.android.libraries.di.SessionScope
|
||||||
|
|
||||||
@ContributesBinding(SessionScope::class)
|
@ContributesBinding(SessionScope::class)
|
||||||
class DefaultForwardEntryPoint : ForwardEntryPoint {
|
class DefaultForwardEntryPoint : ForwardEntryPoint {
|
||||||
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): ForwardEntryPoint.NodeBuilder {
|
override fun createNode(
|
||||||
val plugins = ArrayList<Plugin>()
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
return object : ForwardEntryPoint.NodeBuilder {
|
params: ForwardEntryPoint.Params,
|
||||||
override fun params(params: ForwardEntryPoint.Params): ForwardEntryPoint.NodeBuilder {
|
callback: ForwardEntryPoint.Callback,
|
||||||
plugins += ForwardMessagesNode.Inputs(
|
): Node {
|
||||||
|
return parentNode.createNode<ForwardMessagesNode>(
|
||||||
|
buildContext = buildContext,
|
||||||
|
plugins = listOf(
|
||||||
|
ForwardMessagesNode.Inputs(
|
||||||
eventId = params.eventId,
|
eventId = params.eventId,
|
||||||
timelineProvider = params.timelineProvider,
|
timelineProvider = params.timelineProvider,
|
||||||
|
),
|
||||||
|
callback,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun callback(callback: ForwardEntryPoint.Callback): ForwardEntryPoint.NodeBuilder {
|
|
||||||
plugins += callback
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun build(): Node {
|
|
||||||
return parentNode.createNode<ForwardMessagesNode>(buildContext, plugins)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,14 +66,16 @@ class ForwardMessagesNode(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCancel() {
|
override fun onCancel() {
|
||||||
onForwardDone(emptyList())
|
callback.onDone(emptyList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return roomSelectEntryPoint.nodeBuilder(this, buildContext)
|
return roomSelectEntryPoint.createNode(
|
||||||
.callback(callback)
|
parentNode = this,
|
||||||
.params(RoomSelectEntryPoint.Params(mode = RoomSelectMode.Forward))
|
buildContext = buildContext,
|
||||||
.build()
|
params = RoomSelectEntryPoint.Params(mode = RoomSelectMode.Forward),
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -86,12 +89,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) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,13 @@ package io.element.android.features.forward.impl
|
||||||
|
|
||||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||||
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.testing.junit4.util.MainDispatcherRule
|
import com.bumble.appyx.testing.junit4.util.MainDispatcherRule
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.forward.api.ForwardEntryPoint
|
import io.element.android.features.forward.api.ForwardEntryPoint
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||||
import io.element.android.libraries.matrix.test.timeline.FakeTimelineProvider
|
import io.element.android.libraries.matrix.test.timeline.FakeTimelineProvider
|
||||||
import io.element.android.libraries.roomselect.api.RoomSelectEntryPoint
|
import io.element.android.libraries.roomselect.test.FakeRoomSelectEntryPoint
|
||||||
import io.element.android.tests.testutils.lambda.lambdaError
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
import io.element.android.tests.testutils.node.TestParentNode
|
import io.element.android.tests.testutils.node.TestParentNode
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
|
|
@ -38,11 +37,7 @@ class DefaultForwardEntryPointTest {
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
plugins = plugins,
|
plugins = plugins,
|
||||||
presenterFactory = { _, _ -> createForwardMessagesPresenter() },
|
presenterFactory = { _, _ -> createForwardMessagesPresenter() },
|
||||||
roomSelectEntryPoint = object : RoomSelectEntryPoint {
|
roomSelectEntryPoint = FakeRoomSelectEntryPoint(),
|
||||||
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): RoomSelectEntryPoint.NodeBuilder {
|
|
||||||
lambdaError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val callback = object : ForwardEntryPoint.Callback {
|
val callback = object : ForwardEntryPoint.Callback {
|
||||||
|
|
@ -52,10 +47,12 @@ class DefaultForwardEntryPointTest {
|
||||||
eventId = AN_EVENT_ID,
|
eventId = AN_EVENT_ID,
|
||||||
timelineProvider = FakeTimelineProvider(),
|
timelineProvider = FakeTimelineProvider(),
|
||||||
)
|
)
|
||||||
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
|
val result = entryPoint.createNode(
|
||||||
.params(params)
|
parentNode = parentNode,
|
||||||
.callback(callback)
|
buildContext = BuildContext.root(null),
|
||||||
.build()
|
params = params,
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
assertThat(result).isInstanceOf(ForwardMessagesNode::class.java)
|
assertThat(result).isInstanceOf(ForwardMessagesNode::class.java)
|
||||||
assertThat(result.plugins).contains(
|
assertThat(result.plugins).contains(
|
||||||
ForwardMessagesNode.Inputs(
|
ForwardMessagesNode.Inputs(
|
||||||
|
|
|
||||||
20
features/forward/test/build.gradle.kts
Normal file
20
features/forward/test/build.gradle.kts
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.
|
||||||
|
*/
|
||||||
|
plugins {
|
||||||
|
id("io.element.android-library")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "io.element.android.features.forward.test"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(projects.libraries.architecture)
|
||||||
|
implementation(projects.libraries.matrix.api)
|
||||||
|
implementation(projects.features.forward.api)
|
||||||
|
implementation(projects.tests.testutils)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.features.forward.test
|
||||||
|
|
||||||
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
import com.bumble.appyx.core.node.Node
|
||||||
|
import io.element.android.features.forward.api.ForwardEntryPoint
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
|
||||||
|
class FakeForwardEntryPoint : ForwardEntryPoint {
|
||||||
|
override fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
params: ForwardEntryPoint.Params,
|
||||||
|
callback: ForwardEntryPoint.Callback,
|
||||||
|
): Node = lambdaError()
|
||||||
|
}
|
||||||
|
|
@ -110,9 +110,12 @@ class FtueFlowNode(
|
||||||
defaultFtueService.updateFtueStep()
|
defaultFtueService.updateFtueStep()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lockScreenEntryPoint.nodeBuilder(this, buildContext, LockScreenEntryPoint.Target.Setup)
|
lockScreenEntryPoint.createNode(
|
||||||
.callback(callback)
|
parentNode = this,
|
||||||
.build()
|
buildContext = buildContext,
|
||||||
|
navTarget = LockScreenEntryPoint.Target.Setup,
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
@ -82,62 +84,67 @@ class FtueSessionVerificationFlowNode(
|
||||||
return when (navTarget) {
|
return when (navTarget) {
|
||||||
is NavTarget.Root -> {
|
is NavTarget.Root -> {
|
||||||
val callback = object : ChooseSelfVerificationModeNode.Callback {
|
val callback = object : ChooseSelfVerificationModeNode.Callback {
|
||||||
override fun onUseAnotherDevice() {
|
override fun navigateToUseAnotherDevice() {
|
||||||
backstack.push(NavTarget.UseAnotherDevice)
|
backstack.push(NavTarget.UseAnotherDevice)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUseRecoveryKey() {
|
override fun navigateToUseRecoveryKey() {
|
||||||
backstack.push(NavTarget.EnterRecoveryKey)
|
backstack.push(NavTarget.EnterRecoveryKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResetKey() {
|
override fun navigateToResetKey() {
|
||||||
backstack.push(NavTarget.ResetIdentity)
|
backstack.push(NavTarget.ResetIdentity)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLearnMoreAboutEncryption() {
|
override fun navigateToLearnMoreAboutEncryption() {
|
||||||
learnMoreUrl.value = LearnMoreConfig.DEVICE_VERIFICATION_URL
|
learnMoreUrl.value = LearnMoreConfig.DEVICE_VERIFICATION_URL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createNode<ChooseSelfVerificationModeNode>(buildContext, plugins = listOf(callback))
|
createNode<ChooseSelfVerificationModeNode>(buildContext, plugins = listOf(callback))
|
||||||
}
|
}
|
||||||
is NavTarget.UseAnotherDevice -> {
|
is NavTarget.UseAnotherDevice -> {
|
||||||
outgoingVerificationEntryPoint.nodeBuilder(this, buildContext)
|
outgoingVerificationEntryPoint.createNode(
|
||||||
.params(OutgoingVerificationEntryPoint.Params(
|
parentNode = this,
|
||||||
|
buildContext = buildContext,
|
||||||
|
params = OutgoingVerificationEntryPoint.Params(
|
||||||
showDeviceVerifiedScreen = true,
|
showDeviceVerifiedScreen = true,
|
||||||
verificationRequest = VerificationRequest.Outgoing.CurrentSession,
|
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() {
|
||||||
backstack.pop()
|
backstack.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLearnMoreAboutEncryption() {
|
override fun navigateToLearnMoreAboutEncryption() {
|
||||||
// Note that this callback is never called. The "Learn more" link is not displayed
|
// Note that this callback is never called. The "Learn more" link is not displayed
|
||||||
// for the self session interactive verification.
|
// for the self session interactive verification.
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.build()
|
)
|
||||||
}
|
}
|
||||||
is NavTarget.EnterRecoveryKey -> {
|
is NavTarget.EnterRecoveryKey -> {
|
||||||
secureBackupEntryPoint.nodeBuilder(this, buildContext)
|
secureBackupEntryPoint.createNode(
|
||||||
.params(SecureBackupEntryPoint.Params(SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey))
|
parentNode = this,
|
||||||
.callback(secureBackupEntryPointCallback)
|
buildContext = buildContext,
|
||||||
.build()
|
params = SecureBackupEntryPoint.Params(SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey),
|
||||||
|
callback = secureBackupEntryPointCallback
|
||||||
|
)
|
||||||
}
|
}
|
||||||
is NavTarget.ResetIdentity -> {
|
is NavTarget.ResetIdentity -> {
|
||||||
secureBackupEntryPoint.nodeBuilder(this, buildContext)
|
secureBackupEntryPoint.createNode(
|
||||||
.params(SecureBackupEntryPoint.Params(SecureBackupEntryPoint.InitialTarget.ResetIdentity))
|
parentNode = this,
|
||||||
.callback(object : SecureBackupEntryPoint.Callback {
|
buildContext = buildContext,
|
||||||
|
params = SecureBackupEntryPoint.Params(SecureBackupEntryPoint.InitialTarget.ResetIdentity),
|
||||||
|
callback = object : SecureBackupEntryPoint.Callback {
|
||||||
override fun onDone() {
|
override fun onDone() {
|
||||||
plugins<Callback>().forEach { it.onDone() }
|
callback.onDone()
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
.build()
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -29,13 +29,13 @@ class ChooseSelfVerificationModeNode(
|
||||||
private val directLogoutView: DirectLogoutView,
|
private val directLogoutView: DirectLogoutView,
|
||||||
) : Node(buildContext, plugins = plugins) {
|
) : Node(buildContext, plugins = plugins) {
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onUseAnotherDevice()
|
fun navigateToUseAnotherDevice()
|
||||||
fun onUseRecoveryKey()
|
fun navigateToUseRecoveryKey()
|
||||||
fun onResetKey()
|
fun navigateToResetKey()
|
||||||
fun onLearnMoreAboutEncryption()
|
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) {
|
||||||
|
|
@ -43,10 +43,10 @@ class ChooseSelfVerificationModeNode(
|
||||||
|
|
||||||
ChooseSelfVerificationModeView(
|
ChooseSelfVerificationModeView(
|
||||||
state = state,
|
state = state,
|
||||||
onUseAnotherDevice = callback::onUseAnotherDevice,
|
onUseAnotherDevice = callback::navigateToUseAnotherDevice,
|
||||||
onUseRecoveryKey = callback::onUseRecoveryKey,
|
onUseRecoveryKey = callback::navigateToUseRecoveryKey,
|
||||||
onResetKey = callback::onResetKey,
|
onResetKey = callback::navigateToResetKey,
|
||||||
onLearnMore = callback::onLearnMoreAboutEncryption,
|
onLearnMore = callback::navigateToLearnMoreAboutEncryption,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,11 @@
|
||||||
|
|
||||||
package io.element.android.features.ftue.impl
|
package io.element.android.features.ftue.impl
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||||
import com.bumble.appyx.core.modality.BuildContext
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.testing.junit4.util.MainDispatcherRule
|
import com.bumble.appyx.testing.junit4.util.MainDispatcherRule
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.lockscreen.api.LockScreenEntryPoint
|
import io.element.android.features.lockscreen.test.FakeLockScreenEntryPoint
|
||||||
import io.element.android.tests.testutils.lambda.lambdaError
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
import io.element.android.tests.testutils.node.TestParentNode
|
import io.element.android.tests.testutils.node.TestParentNode
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
|
|
@ -36,19 +34,7 @@ class DefaultFtueEntryPointTest {
|
||||||
plugins = plugins,
|
plugins = plugins,
|
||||||
analyticsEntryPoint = { _, _ -> lambdaError() },
|
analyticsEntryPoint = { _, _ -> lambdaError() },
|
||||||
defaultFtueService = createDefaultFtueService(),
|
defaultFtueService = createDefaultFtueService(),
|
||||||
lockScreenEntryPoint = object : LockScreenEntryPoint {
|
lockScreenEntryPoint = FakeLockScreenEntryPoint(),
|
||||||
override fun nodeBuilder(
|
|
||||||
parentNode: com.bumble.appyx.core.node.Node,
|
|
||||||
buildContext: BuildContext,
|
|
||||||
navTarget: LockScreenEntryPoint.Target
|
|
||||||
): LockScreenEntryPoint.NodeBuilder {
|
|
||||||
lambdaError()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pinUnlockIntent(context: Context): Intent {
|
|
||||||
lambdaError()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val result = entryPoint.createNode(parentNode, BuildContext.root(null))
|
val result = entryPoint.createNode(parentNode, BuildContext.root(null))
|
||||||
|
|
|
||||||
|
|
@ -14,19 +14,19 @@ import io.element.android.libraries.architecture.FeatureEntryPoint
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
||||||
interface HomeEntryPoint : FeatureEntryPoint {
|
interface HomeEntryPoint : FeatureEntryPoint {
|
||||||
fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder
|
fun createNode(
|
||||||
interface NodeBuilder {
|
parentNode: Node,
|
||||||
fun callback(callback: Callback): NodeBuilder
|
buildContext: BuildContext,
|
||||||
fun build(): Node
|
callback: Callback,
|
||||||
}
|
): Node
|
||||||
|
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onRoomClick(roomId: RoomId)
|
fun navigateToRoom(roomId: RoomId)
|
||||||
fun onStartChatClick()
|
fun navigateToCreateRoom()
|
||||||
fun onSettingsClick()
|
fun navigateToSettings()
|
||||||
fun onSetUpRecoveryClick()
|
fun navigateToSetUpRecovery()
|
||||||
fun onSessionConfirmRecoveryKeyClick()
|
fun navigateToEnterRecoveryKey()
|
||||||
fun onRoomSettingsClick(roomId: RoomId)
|
fun navigateToRoomSettings(roomId: RoomId)
|
||||||
fun onReportBugClick()
|
fun navigateToBugReport()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ package io.element.android.features.home.impl
|
||||||
|
|
||||||
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 dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import io.element.android.features.home.api.HomeEntryPoint
|
import io.element.android.features.home.api.HomeEntryPoint
|
||||||
|
|
@ -17,18 +16,11 @@ import io.element.android.libraries.architecture.createNode
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
class DefaultHomeEntryPoint : HomeEntryPoint {
|
class DefaultHomeEntryPoint : HomeEntryPoint {
|
||||||
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): HomeEntryPoint.NodeBuilder {
|
override fun createNode(
|
||||||
val plugins = ArrayList<Plugin>()
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
return object : HomeEntryPoint.NodeBuilder {
|
callback: HomeEntryPoint.Callback,
|
||||||
override fun callback(callback: HomeEntryPoint.Callback): HomeEntryPoint.NodeBuilder {
|
): Node {
|
||||||
plugins += callback
|
return parentNode.createNode<HomeFlowNode>(buildContext, listOf(callback))
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun build(): Node {
|
|
||||||
return parentNode.createNode<HomeFlowNode>(buildContext, plugins)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -29,8 +28,8 @@ 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.features.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint
|
import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesEntryPoint
|
||||||
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType
|
import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType
|
||||||
import io.element.android.features.home.api.HomeEntryPoint
|
import io.element.android.features.home.api.HomeEntryPoint
|
||||||
import io.element.android.features.home.impl.components.RoomListMenuAction
|
import io.element.android.features.home.impl.components.RoomListMenuAction
|
||||||
import io.element.android.features.home.impl.model.RoomListRoomSummary
|
import io.element.android.features.home.impl.model.RoomListRoomSummary
|
||||||
|
|
@ -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.onRoomClick(roomId) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onOpenSettings() {
|
|
||||||
plugins<HomeEntryPoint.Callback>().forEach { it.onSettingsClick() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onStartChatClick() {
|
|
||||||
plugins<HomeEntryPoint.Callback>().forEach { it.onStartChatClick() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onSetUpRecoveryClick() {
|
|
||||||
plugins<HomeEntryPoint.Callback>().forEach { it.onSetUpRecoveryClick() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onSessionConfirmRecoveryKeyClick() {
|
|
||||||
plugins<HomeEntryPoint.Callback>().forEach { it.onSessionConfirmRecoveryKeyClick() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onRoomSettingsClick(roomId: RoomId) {
|
|
||||||
plugins<HomeEntryPoint.Callback>().forEach { it.onRoomSettingsClick(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.onReportBugClick() }
|
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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -209,14 +186,28 @@ class HomeFlowNode(
|
||||||
|
|
||||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||||
return when (navTarget) {
|
return when (navTarget) {
|
||||||
is NavTarget.ReportRoom -> reportRoomEntryPoint.createNode(this, buildContext, navTarget.roomId)
|
is NavTarget.ReportRoom -> {
|
||||||
is NavTarget.DeclineInviteAndBlockUser -> declineInviteAndBlockUserEntryPoint.createNode(this, buildContext, navTarget.inviteData)
|
reportRoomEntryPoint.createNode(
|
||||||
|
parentNode = this,
|
||||||
|
buildContext = buildContext,
|
||||||
|
roomId = navTarget.roomId,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is NavTarget.DeclineInviteAndBlockUser -> {
|
||||||
|
declineInviteAndBlockUserEntryPoint.createNode(
|
||||||
|
parentNode = this,
|
||||||
|
buildContext = buildContext,
|
||||||
|
inviteData = navTarget.inviteData,
|
||||||
|
)
|
||||||
|
}
|
||||||
is NavTarget.SelectNewOwnersWhenLeavingRoom -> {
|
is NavTarget.SelectNewOwnersWhenLeavingRoom -> {
|
||||||
val room = runBlocking { matrixClient.getJoinedRoom(navTarget.roomId) } ?: error("Room ${navTarget.roomId} not found")
|
val room = runBlocking { matrixClient.getJoinedRoom(navTarget.roomId) } ?: error("Room ${navTarget.roomId} not found")
|
||||||
changeRoomMemberRolesEntryPoint.builder(this, buildContext)
|
changeRoomMemberRolesEntryPoint.createNode(
|
||||||
.room(room)
|
parentNode = this,
|
||||||
.listType(ChangeRoomMemberRolesListType.SelectNewOwnersWhenLeaving)
|
buildContext = buildContext,
|
||||||
.build()
|
room = room,
|
||||||
|
listType = ChangeRoomMemberRolesListType.SelectNewOwnersWhenLeaving,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
NavTarget.Root -> rootNode(buildContext)
|
NavTarget.Root -> rootNode(buildContext)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,22 +36,24 @@ class DefaultHomeEntryPointTest {
|
||||||
directLogoutView = { _ -> lambdaError() },
|
directLogoutView = { _ -> lambdaError() },
|
||||||
reportRoomEntryPoint = { _, _, _ -> lambdaError() },
|
reportRoomEntryPoint = { _, _, _ -> lambdaError() },
|
||||||
declineInviteAndBlockUserEntryPoint = { _, _, _ -> lambdaError() },
|
declineInviteAndBlockUserEntryPoint = { _, _, _ -> lambdaError() },
|
||||||
changeRoomMemberRolesEntryPoint = { _, _ -> lambdaError() },
|
changeRoomMemberRolesEntryPoint = { _, _, _, _ -> lambdaError() },
|
||||||
leaveRoomRenderer = { _, _, _ -> lambdaError() },
|
leaveRoomRenderer = { _, _, _ -> lambdaError() },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val callback = object : HomeEntryPoint.Callback {
|
val callback = object : HomeEntryPoint.Callback {
|
||||||
override fun onRoomClick(roomId: RoomId) = lambdaError()
|
override fun navigateToRoom(roomId: RoomId) = lambdaError()
|
||||||
override fun onStartChatClick() = lambdaError()
|
override fun navigateToCreateRoom() = lambdaError()
|
||||||
override fun onSettingsClick() = lambdaError()
|
override fun navigateToSettings() = lambdaError()
|
||||||
override fun onSetUpRecoveryClick() = lambdaError()
|
override fun navigateToSetUpRecovery() = lambdaError()
|
||||||
override fun onSessionConfirmRecoveryKeyClick() = lambdaError()
|
override fun navigateToEnterRecoveryKey() = lambdaError()
|
||||||
override fun onRoomSettingsClick(roomId: RoomId) = lambdaError()
|
override fun navigateToRoomSettings(roomId: RoomId) = lambdaError()
|
||||||
override fun onReportBugClick() = lambdaError()
|
override fun navigateToBugReport() = lambdaError()
|
||||||
}
|
}
|
||||||
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
|
val result = entryPoint.createNode(
|
||||||
.callback(callback)
|
parentNode = parentNode,
|
||||||
.build()
|
buildContext = BuildContext.root(null),
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
assertThat(result).isInstanceOf(HomeFlowNode::class.java)
|
assertThat(result).isInstanceOf(HomeFlowNode::class.java)
|
||||||
assertThat(result.plugins).contains(callback)
|
assertThat(result.plugins).contains(callback)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,5 +13,9 @@ import io.element.android.features.invite.api.InviteData
|
||||||
import io.element.android.libraries.architecture.FeatureEntryPoint
|
import io.element.android.libraries.architecture.FeatureEntryPoint
|
||||||
|
|
||||||
fun interface DeclineInviteAndBlockEntryPoint : FeatureEntryPoint {
|
fun interface DeclineInviteAndBlockEntryPoint : FeatureEntryPoint {
|
||||||
fun createNode(parentNode: Node, buildContext: BuildContext, inviteData: InviteData): Node
|
fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
inviteData: InviteData,
|
||||||
|
): Node
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,11 @@ import io.element.android.libraries.architecture.createNode
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
class DefaultDeclineAndBlockEntryPoint : DeclineInviteAndBlockEntryPoint {
|
class DefaultDeclineAndBlockEntryPoint : DeclineInviteAndBlockEntryPoint {
|
||||||
override fun createNode(parentNode: Node, buildContext: BuildContext, inviteData: InviteData): Node {
|
override fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
inviteData: InviteData,
|
||||||
|
): Node {
|
||||||
val inputs = DeclineAndBlockNode.Inputs(inviteData)
|
val inputs = DeclineAndBlockNode.Inputs(inviteData)
|
||||||
return parentNode.createNode<DeclineAndBlockNode>(buildContext, plugins = listOf(inputs))
|
return parentNode.createNode<DeclineAndBlockNode>(buildContext, plugins = listOf(inputs))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,5 +26,6 @@ dependencies {
|
||||||
implementation(libs.coroutines.core)
|
implementation(libs.coroutines.core)
|
||||||
implementation(projects.libraries.matrix.api)
|
implementation(projects.libraries.matrix.api)
|
||||||
implementation(projects.libraries.matrix.test)
|
implementation(projects.libraries.matrix.test)
|
||||||
|
implementation(projects.tests.testutils)
|
||||||
api(projects.features.invite.api)
|
api(projects.features.invite.api)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.features.invite.test.declineandblock
|
||||||
|
|
||||||
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
import com.bumble.appyx.core.node.Node
|
||||||
|
import io.element.android.features.invite.api.InviteData
|
||||||
|
import io.element.android.features.invite.api.declineandblock.DeclineInviteAndBlockEntryPoint
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
|
||||||
|
class FakeDeclineInviteAndBlockEntryPoint : DeclineInviteAndBlockEntryPoint {
|
||||||
|
override fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
inviteData: InviteData,
|
||||||
|
): Node {
|
||||||
|
lambdaError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,7 +18,11 @@ import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
|
|
||||||
interface JoinRoomEntryPoint : FeatureEntryPoint {
|
interface JoinRoomEntryPoint : FeatureEntryPoint {
|
||||||
fun createNode(parentNode: Node, buildContext: BuildContext, inputs: Inputs): Node
|
fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
inputs: Inputs,
|
||||||
|
): Node
|
||||||
|
|
||||||
data class Inputs(
|
data class Inputs(
|
||||||
val roomId: RoomId,
|
val roomId: RoomId,
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,11 @@ import io.element.android.libraries.architecture.createNode
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
class DefaultJoinRoomEntryPoint : JoinRoomEntryPoint {
|
class DefaultJoinRoomEntryPoint : JoinRoomEntryPoint {
|
||||||
override fun createNode(parentNode: Node, buildContext: BuildContext, inputs: JoinRoomEntryPoint.Inputs): Node {
|
override fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
inputs: JoinRoomEntryPoint.Inputs,
|
||||||
|
): Node {
|
||||||
return parentNode.createNode<JoinRoomFlowNode>(
|
return parentNode.createNode<JoinRoomFlowNode>(
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
plugins = listOf(inputs)
|
plugins = listOf(inputs)
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,11 @@ class JoinRoomFlowNode(
|
||||||
|
|
||||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||||
return when (navTarget) {
|
return when (navTarget) {
|
||||||
is NavTarget.DeclineInviteAndBlockUser -> declineAndBlockEntryPoint.createNode(this, buildContext, navTarget.inviteData)
|
is NavTarget.DeclineInviteAndBlockUser -> declineAndBlockEntryPoint.createNode(
|
||||||
|
parentNode = this,
|
||||||
|
buildContext = buildContext,
|
||||||
|
inviteData = navTarget.inviteData,
|
||||||
|
)
|
||||||
NavTarget.Root -> rootNode(buildContext)
|
NavTarget.Root -> rootNode(buildContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,10 @@ package io.element.android.features.joinroom.impl
|
||||||
|
|
||||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||||
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.testing.junit4.util.MainDispatcherRule
|
import com.bumble.appyx.testing.junit4.util.MainDispatcherRule
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||||
import io.element.android.features.invite.api.InviteData
|
import io.element.android.features.invite.test.declineandblock.FakeDeclineInviteAndBlockEntryPoint
|
||||||
import io.element.android.features.invite.api.declineandblock.DeclineInviteAndBlockEntryPoint
|
|
||||||
import io.element.android.features.joinroom.api.JoinRoomEntryPoint
|
import io.element.android.features.joinroom.api.JoinRoomEntryPoint
|
||||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
|
|
@ -40,9 +38,7 @@ class DefaultJoinRoomEntryPointTest {
|
||||||
plugins = plugins,
|
plugins = plugins,
|
||||||
presenterFactory = { _, _, _, _, _ -> createJoinRoomPresenter() },
|
presenterFactory = { _, _, _, _, _ -> createJoinRoomPresenter() },
|
||||||
acceptDeclineInviteView = { _, _, _, _ -> lambdaError() },
|
acceptDeclineInviteView = { _, _, _, _ -> lambdaError() },
|
||||||
declineAndBlockEntryPoint = object : DeclineInviteAndBlockEntryPoint {
|
declineAndBlockEntryPoint = FakeDeclineInviteAndBlockEntryPoint(),
|
||||||
override fun createNode(parentNode: Node, buildContext: BuildContext, inviteData: InviteData) = lambdaError()
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val inputs = JoinRoomEntryPoint.Inputs(
|
val inputs = JoinRoomEntryPoint.Inputs(
|
||||||
|
|
@ -52,7 +48,11 @@ class DefaultJoinRoomEntryPointTest {
|
||||||
serverNames = emptyList(),
|
serverNames = emptyList(),
|
||||||
trigger = JoinedRoom.Trigger.RoomDirectory,
|
trigger = JoinedRoom.Trigger.RoomDirectory,
|
||||||
)
|
)
|
||||||
val result = entryPoint.createNode(parentNode, BuildContext.root(null), inputs)
|
val result = entryPoint.createNode(
|
||||||
|
parentNode = parentNode,
|
||||||
|
buildContext = BuildContext.root(null),
|
||||||
|
inputs = inputs,
|
||||||
|
)
|
||||||
assertThat(result).isInstanceOf(JoinRoomFlowNode::class.java)
|
assertThat(result).isInstanceOf(JoinRoomFlowNode::class.java)
|
||||||
assertThat(result.plugins).contains(inputs)
|
assertThat(result.plugins).contains(inputs)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
21
features/knockrequests/test/build.gradle.kts
Normal file
21
features/knockrequests/test/build.gradle.kts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("io.element.android-library")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "io.element.android.features.knockrequests.test"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(projects.features.knockrequests.api)
|
||||||
|
implementation(projects.libraries.architecture)
|
||||||
|
implementation(projects.libraries.matrix.api)
|
||||||
|
implementation(projects.tests.testutils)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.features.knockrequests.test
|
||||||
|
|
||||||
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
import com.bumble.appyx.core.node.Node
|
||||||
|
import io.element.android.features.knockrequests.api.list.KnockRequestsListEntryPoint
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
|
||||||
|
class FakeKnockRequestsListEntryPoint : KnockRequestsListEntryPoint {
|
||||||
|
override fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
): Node = lambdaError()
|
||||||
|
}
|
||||||
|
|
@ -52,7 +52,7 @@ class DependenciesFlowNode(
|
||||||
return when (navTarget) {
|
return when (navTarget) {
|
||||||
is NavTarget.LicensesList -> {
|
is NavTarget.LicensesList -> {
|
||||||
val callback = object : DependencyLicensesListNode.Callback {
|
val callback = object : DependencyLicensesListNode.Callback {
|
||||||
override fun onOpenLicense(license: DependencyLicenseItem) {
|
override fun navigateToLicense(license: DependencyLicenseItem) {
|
||||||
backstack.push(NavTarget.LicenseDetails(license))
|
backstack.push(NavTarget.LicenseDetails(license))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -30,13 +30,10 @@ class DependencyLicensesListNode(
|
||||||
plugins = plugins
|
plugins = plugins
|
||||||
) {
|
) {
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onOpenLicense(license: DependencyLicenseItem)
|
fun navigateToLicense(license: DependencyLicenseItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onOpenLicense(license: DependencyLicenseItem) {
|
private val callback: Callback = callback()
|
||||||
plugins<Callback>()
|
|
||||||
.forEach { it.onOpenLicense(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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
19
features/licenses/test/build.gradle.kts
Normal file
19
features/licenses/test/build.gradle.kts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.
|
||||||
|
*/
|
||||||
|
plugins {
|
||||||
|
id("io.element.android-library")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "io.element.android.features.licenses.test"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(projects.features.licenses.api)
|
||||||
|
implementation(projects.libraries.architecture)
|
||||||
|
implementation(projects.tests.testutils)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.features.licenses.test
|
||||||
|
|
||||||
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
import com.bumble.appyx.core.node.Node
|
||||||
|
import io.element.android.features.licenses.api.OpenSourceLicensesEntryPoint
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
|
||||||
|
class FakeOpenSourceLicensesEntryPoint : OpenSourceLicensesEntryPoint {
|
||||||
|
override fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
): Node {
|
||||||
|
lambdaError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,8 +18,9 @@ import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||||
* Allows a user to share a location message within a room.
|
* Allows a user to share a location message within a room.
|
||||||
*/
|
*/
|
||||||
interface SendLocationEntryPoint : FeatureEntryPoint {
|
interface SendLocationEntryPoint : FeatureEntryPoint {
|
||||||
fun builder(timelineMode: Timeline.Mode): Builder
|
fun createNode(
|
||||||
interface Builder {
|
parentNode: Node,
|
||||||
fun build(parentNode: Node, buildContext: BuildContext): Node
|
buildContext: BuildContext,
|
||||||
}
|
timelineMode: Timeline.Mode,
|
||||||
|
): Node
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,14 @@ import io.element.android.libraries.architecture.FeatureEntryPoint
|
||||||
import io.element.android.libraries.architecture.NodeInputs
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
|
|
||||||
interface ShowLocationEntryPoint : FeatureEntryPoint {
|
interface ShowLocationEntryPoint : FeatureEntryPoint {
|
||||||
data class Inputs(val location: Location, val description: String?) : NodeInputs
|
data class Inputs(
|
||||||
|
val location: Location,
|
||||||
|
val description: String?,
|
||||||
|
) : NodeInputs
|
||||||
|
|
||||||
fun createNode(parentNode: Node, buildContext: BuildContext, inputs: Inputs): Node
|
fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
inputs: Inputs,
|
||||||
|
): Node
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,16 +17,14 @@ import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
class DefaultSendLocationEntryPoint : SendLocationEntryPoint {
|
class DefaultSendLocationEntryPoint : SendLocationEntryPoint {
|
||||||
override fun builder(timelineMode: Timeline.Mode): SendLocationEntryPoint.Builder {
|
override fun createNode(
|
||||||
return Builder(timelineMode)
|
parentNode: Node,
|
||||||
}
|
buildContext: BuildContext,
|
||||||
|
timelineMode: Timeline.Mode,
|
||||||
class Builder(private val timelineMode: Timeline.Mode) : SendLocationEntryPoint.Builder {
|
): Node {
|
||||||
override fun build(parentNode: Node, buildContext: BuildContext): Node {
|
|
||||||
return parentNode.createNode<SendLocationNode>(
|
return parentNode.createNode<SendLocationNode>(
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
plugins = listOf(SendLocationNode.Inputs(timelineMode))
|
plugins = listOf(SendLocationNode.Inputs(timelineMode))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,11 @@ import io.element.android.libraries.architecture.createNode
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
class DefaultShowLocationEntryPoint : ShowLocationEntryPoint {
|
class DefaultShowLocationEntryPoint : ShowLocationEntryPoint {
|
||||||
override fun createNode(parentNode: Node, buildContext: BuildContext, inputs: ShowLocationEntryPoint.Inputs): Node {
|
override fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
inputs: ShowLocationEntryPoint.Inputs,
|
||||||
|
): Node {
|
||||||
return parentNode.createNode<ShowLocationNode>(buildContext, listOf(inputs))
|
return parentNode.createNode<ShowLocationNode>(buildContext, listOf(inputs))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,11 @@ class DefaultSendLocationEntryPointTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val timelineMode = Timeline.Mode.Live
|
val timelineMode = Timeline.Mode.Live
|
||||||
val result = entryPoint.builder(timelineMode)
|
val result = entryPoint.createNode(
|
||||||
.build(parentNode, BuildContext.root(null))
|
parentNode = parentNode,
|
||||||
|
buildContext = BuildContext.root(null),
|
||||||
|
timelineMode = timelineMode,
|
||||||
|
)
|
||||||
assertThat(result).isInstanceOf(SendLocationNode::class.java)
|
assertThat(result).isInstanceOf(SendLocationNode::class.java)
|
||||||
assertThat(result.plugins).contains(SendLocationNode.Inputs(timelineMode))
|
assertThat(result.plugins).contains(SendLocationNode.Inputs(timelineMode))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,8 @@ class DefaultShowLocationEntryPointTest {
|
||||||
description = "My location",
|
description = "My location",
|
||||||
)
|
)
|
||||||
val result = entryPoint.createNode(
|
val result = entryPoint.createNode(
|
||||||
parentNode,
|
parentNode = parentNode,
|
||||||
BuildContext.root(null),
|
buildContext = BuildContext.root(null),
|
||||||
inputs = inputs,
|
inputs = inputs,
|
||||||
)
|
)
|
||||||
assertThat(result).isInstanceOf(ShowLocationNode::class.java)
|
assertThat(result).isInstanceOf(ShowLocationNode::class.java)
|
||||||
|
|
|
||||||
|
|
@ -14,5 +14,8 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(projects.features.location.api)
|
api(projects.features.location.api)
|
||||||
|
implementation(projects.libraries.matrix.api)
|
||||||
|
implementation(libs.appyx.core)
|
||||||
|
implementation(projects.tests.testutils)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.features.location.test
|
||||||
|
|
||||||
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
import com.bumble.appyx.core.node.Node
|
||||||
|
import io.element.android.features.location.api.SendLocationEntryPoint
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
|
||||||
|
class FakeSendLocationEntryPoint : SendLocationEntryPoint {
|
||||||
|
override fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
timelineMode: Timeline.Mode,
|
||||||
|
): Node = lambdaError()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.features.location.test
|
||||||
|
|
||||||
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
import com.bumble.appyx.core.node.Node
|
||||||
|
import io.element.android.features.location.api.ShowLocationEntryPoint
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
|
||||||
|
class FakeShowLocationEntryPoint : ShowLocationEntryPoint {
|
||||||
|
override fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
inputs: ShowLocationEntryPoint.Inputs,
|
||||||
|
): Node = lambdaError()
|
||||||
|
}
|
||||||
|
|
@ -15,13 +15,14 @@ import com.bumble.appyx.core.plugin.Plugin
|
||||||
import io.element.android.libraries.architecture.FeatureEntryPoint
|
import io.element.android.libraries.architecture.FeatureEntryPoint
|
||||||
|
|
||||||
interface LockScreenEntryPoint : FeatureEntryPoint {
|
interface LockScreenEntryPoint : FeatureEntryPoint {
|
||||||
fun nodeBuilder(parentNode: Node, buildContext: BuildContext, navTarget: Target): NodeBuilder
|
fun createNode(
|
||||||
fun pinUnlockIntent(context: Context): Intent
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
navTarget: Target,
|
||||||
|
callback: Callback,
|
||||||
|
): Node
|
||||||
|
|
||||||
interface NodeBuilder {
|
fun pinUnlockIntent(context: Context): Intent
|
||||||
fun callback(callback: Callback): NodeBuilder
|
|
||||||
fun build(): Node
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onSetupDone()
|
fun onSetupDone()
|
||||||
|
|
|
||||||
|
|
@ -19,26 +19,24 @@ import io.element.android.libraries.architecture.createNode
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
class DefaultLockScreenEntryPoint : LockScreenEntryPoint {
|
class DefaultLockScreenEntryPoint : LockScreenEntryPoint {
|
||||||
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext, navTarget: LockScreenEntryPoint.Target): LockScreenEntryPoint.NodeBuilder {
|
override fun createNode(
|
||||||
val callbacks = mutableListOf<LockScreenEntryPoint.Callback>()
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
return object : LockScreenEntryPoint.NodeBuilder {
|
navTarget: LockScreenEntryPoint.Target,
|
||||||
override fun callback(callback: LockScreenEntryPoint.Callback): LockScreenEntryPoint.NodeBuilder {
|
callback: LockScreenEntryPoint.Callback,
|
||||||
callbacks += callback
|
): Node {
|
||||||
return this
|
return parentNode.createNode<LockScreenFlowNode>(
|
||||||
}
|
buildContext = buildContext,
|
||||||
|
plugins = listOf(
|
||||||
override fun build(): Node {
|
LockScreenFlowNode.Inputs(
|
||||||
val inputs = LockScreenFlowNode.Inputs(
|
|
||||||
when (navTarget) {
|
when (navTarget) {
|
||||||
LockScreenEntryPoint.Target.Setup -> LockScreenFlowNode.NavTarget.Setup
|
LockScreenEntryPoint.Target.Setup -> LockScreenFlowNode.NavTarget.Setup
|
||||||
LockScreenEntryPoint.Target.Settings -> LockScreenFlowNode.NavTarget.Settings
|
LockScreenEntryPoint.Target.Settings -> LockScreenFlowNode.NavTarget.Settings
|
||||||
}
|
}
|
||||||
|
),
|
||||||
|
callback,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
val plugins = listOf(inputs) + callbacks
|
|
||||||
return parentNode.createNode<LockScreenFlowNode>(buildContext, plugins)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pinUnlockIntent(context: Context): Intent {
|
override fun pinUnlockIntent(context: Context): Intent {
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ class LockScreenSettingsFlowNode(
|
||||||
}
|
}
|
||||||
NavTarget.Settings -> {
|
NavTarget.Settings -> {
|
||||||
val callback = object : LockScreenSettingsNode.Callback {
|
val callback = object : LockScreenSettingsNode.Callback {
|
||||||
override fun onChangePinClick() {
|
override fun navigateToSetupPin() {
|
||||||
backstack.push(NavTarget.SetupPin)
|
backstack.push(NavTarget.SetupPin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -26,12 +26,10 @@ class LockScreenSettingsNode(
|
||||||
private val presenter: LockScreenSettingsPresenter,
|
private val presenter: LockScreenSettingsPresenter,
|
||||||
) : Node(buildContext, plugins = plugins) {
|
) : Node(buildContext, plugins = plugins) {
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onChangePinClick()
|
fun navigateToSetupPin()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onChangePinClick() {
|
private val callback: Callback = callback()
|
||||||
plugins<Callback>().forEach { it.onChangePinClick() }
|
|
||||||
}
|
|
||||||
|
|
||||||
@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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,12 @@ class DefaultLockScreenEntryPointTest {
|
||||||
override fun onSetupDone() = lambdaError()
|
override fun onSetupDone() = lambdaError()
|
||||||
}
|
}
|
||||||
val navTarget = LockScreenEntryPoint.Target.Setup
|
val navTarget = LockScreenEntryPoint.Target.Setup
|
||||||
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null), navTarget)
|
val result = entryPoint.createNode(
|
||||||
.callback(callback)
|
parentNode = parentNode,
|
||||||
.build()
|
buildContext = BuildContext.root(null),
|
||||||
|
navTarget = navTarget,
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
assertThat(result).isInstanceOf(LockScreenFlowNode::class.java)
|
assertThat(result).isInstanceOf(LockScreenFlowNode::class.java)
|
||||||
assertThat(result.plugins).contains(LockScreenFlowNode.Inputs(LockScreenFlowNode.NavTarget.Setup))
|
assertThat(result.plugins).contains(LockScreenFlowNode.Inputs(LockScreenFlowNode.NavTarget.Setup))
|
||||||
assertThat(result.plugins).contains(callback)
|
assertThat(result.plugins).contains(callback)
|
||||||
|
|
@ -58,9 +61,12 @@ class DefaultLockScreenEntryPointTest {
|
||||||
override fun onSetupDone() = lambdaError()
|
override fun onSetupDone() = lambdaError()
|
||||||
}
|
}
|
||||||
val navTarget = LockScreenEntryPoint.Target.Settings
|
val navTarget = LockScreenEntryPoint.Target.Settings
|
||||||
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null), navTarget)
|
val result = entryPoint.createNode(
|
||||||
.callback(callback)
|
parentNode = parentNode,
|
||||||
.build()
|
buildContext = BuildContext.root(null),
|
||||||
|
navTarget = navTarget,
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
assertThat(result).isInstanceOf(LockScreenFlowNode::class.java)
|
assertThat(result).isInstanceOf(LockScreenFlowNode::class.java)
|
||||||
assertThat(result.plugins).contains(LockScreenFlowNode.Inputs(LockScreenFlowNode.NavTarget.Settings))
|
assertThat(result.plugins).contains(LockScreenFlowNode.Inputs(LockScreenFlowNode.NavTarget.Settings))
|
||||||
assertThat(result.plugins).contains(callback)
|
assertThat(result.plugins).contains(callback)
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.coroutines.core)
|
|
||||||
api(projects.features.lockscreen.api)
|
api(projects.features.lockscreen.api)
|
||||||
|
implementation(libs.coroutines.core)
|
||||||
|
implementation(projects.libraries.architecture)
|
||||||
|
implementation(projects.tests.testutils)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.features.lockscreen.test
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
import com.bumble.appyx.core.node.Node
|
||||||
|
import io.element.android.features.lockscreen.api.LockScreenEntryPoint
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
|
||||||
|
class FakeLockScreenEntryPoint : LockScreenEntryPoint {
|
||||||
|
override fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
navTarget: LockScreenEntryPoint.Target,
|
||||||
|
callback: LockScreenEntryPoint.Callback,
|
||||||
|
): Node = lambdaError()
|
||||||
|
|
||||||
|
override fun pinUnlockIntent(context: Context): Intent = lambdaError()
|
||||||
|
}
|
||||||
|
|
@ -19,14 +19,13 @@ interface LoginEntryPoint : FeatureEntryPoint {
|
||||||
)
|
)
|
||||||
|
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onReportProblem()
|
fun navigateToBugReport()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder
|
fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
interface NodeBuilder {
|
buildContext: BuildContext,
|
||||||
fun params(params: Params): NodeBuilder
|
params: Params,
|
||||||
fun callback(callback: Callback): NodeBuilder
|
callback: Callback,
|
||||||
fun build(): Node
|
): Node
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ package io.element.android.features.login.impl
|
||||||
|
|
||||||
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 dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import io.element.android.features.login.api.LoginEntryPoint
|
import io.element.android.features.login.api.LoginEntryPoint
|
||||||
|
|
@ -17,26 +16,21 @@ import io.element.android.libraries.architecture.createNode
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
class DefaultLoginEntryPoint : LoginEntryPoint {
|
class DefaultLoginEntryPoint : LoginEntryPoint {
|
||||||
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): LoginEntryPoint.NodeBuilder {
|
override fun createNode(
|
||||||
val plugins = ArrayList<Plugin>()
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
return object : LoginEntryPoint.NodeBuilder {
|
params: LoginEntryPoint.Params,
|
||||||
override fun params(params: LoginEntryPoint.Params): LoginEntryPoint.NodeBuilder {
|
callback: LoginEntryPoint.Callback,
|
||||||
plugins += LoginFlowNode.Params(
|
): Node {
|
||||||
|
return parentNode.createNode<LoginFlowNode>(
|
||||||
|
buildContext = buildContext,
|
||||||
|
plugins = listOf(
|
||||||
|
LoginFlowNode.Params(
|
||||||
accountProvider = params.accountProvider,
|
accountProvider = params.accountProvider,
|
||||||
loginHint = params.loginHint,
|
loginHint = params.loginHint,
|
||||||
|
),
|
||||||
|
callback,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun callback(callback: LoginEntryPoint.Callback): LoginEntryPoint.NodeBuilder {
|
|
||||||
plugins += callback
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun build(): Node {
|
|
||||||
return parentNode.createNode<LoginFlowNode>(buildContext, plugins)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -126,13 +127,13 @@ class LoginFlowNode(
|
||||||
return when (navTarget) {
|
return when (navTarget) {
|
||||||
NavTarget.OnBoarding -> {
|
NavTarget.OnBoarding -> {
|
||||||
val callback = object : OnBoardingNode.Callback {
|
val callback = object : OnBoardingNode.Callback {
|
||||||
override fun onSignUp() {
|
override fun navigateToSignUpFlow() {
|
||||||
backstack.push(
|
backstack.push(
|
||||||
NavTarget.ConfirmAccountProvider(isAccountCreation = true)
|
NavTarget.ConfirmAccountProvider(isAccountCreation = true)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSignIn(mustChooseAccountProvider: Boolean) {
|
override fun navigateToSignInFlow(mustChooseAccountProvider: Boolean) {
|
||||||
backstack.push(
|
backstack.push(
|
||||||
if (mustChooseAccountProvider) {
|
if (mustChooseAccountProvider) {
|
||||||
NavTarget.ChooseAccountProvider
|
NavTarget.ChooseAccountProvider
|
||||||
|
|
@ -142,23 +143,23 @@ class LoginFlowNode(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSignInWithQrCode() {
|
override fun navigateToQrCode() {
|
||||||
backstack.push(NavTarget.QrCode)
|
backstack.push(NavTarget.QrCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onReportProblem() {
|
override fun navigateToBugReport() {
|
||||||
plugins<LoginEntryPoint.Callback>().forEach { it.onReportProblem() }
|
callback.navigateToBugReport()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOidcDetails(oidcDetails: OidcDetails) {
|
override fun navigateToOidc(oidcDetails: OidcDetails) {
|
||||||
navigateToMas(oidcDetails)
|
navigateToMas(oidcDetails)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateAccountContinue(url: String) {
|
override fun navigateToCreateAccount(url: String) {
|
||||||
backstack.push(NavTarget.CreateAccount(url))
|
backstack.push(NavTarget.CreateAccount(url))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoginPasswordNeeded() {
|
override fun navigateToLoginPassword() {
|
||||||
backstack.push(NavTarget.LoginPassword)
|
backstack.push(NavTarget.LoginPassword)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -171,15 +172,15 @@ class LoginFlowNode(
|
||||||
}
|
}
|
||||||
NavTarget.ChooseAccountProvider -> {
|
NavTarget.ChooseAccountProvider -> {
|
||||||
val callback = object : ChooseAccountProviderNode.Callback {
|
val callback = object : ChooseAccountProviderNode.Callback {
|
||||||
override fun onOidcDetails(oidcDetails: OidcDetails) {
|
override fun navigateToOidc(oidcDetails: OidcDetails) {
|
||||||
navigateToMas(oidcDetails)
|
navigateToMas(oidcDetails)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateAccountContinue(url: String) {
|
override fun navigateToCreateAccount(url: String) {
|
||||||
backstack.push(NavTarget.CreateAccount(url))
|
backstack.push(NavTarget.CreateAccount(url))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoginPasswordNeeded() {
|
override fun navigateToLoginPassword() {
|
||||||
backstack.push(NavTarget.LoginPassword)
|
backstack.push(NavTarget.LoginPassword)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -193,19 +194,19 @@ class LoginFlowNode(
|
||||||
isAccountCreation = navTarget.isAccountCreation,
|
isAccountCreation = navTarget.isAccountCreation,
|
||||||
)
|
)
|
||||||
val callback = object : ConfirmAccountProviderNode.Callback {
|
val callback = object : ConfirmAccountProviderNode.Callback {
|
||||||
override fun onOidcDetails(oidcDetails: OidcDetails) {
|
override fun navigateToOidc(oidcDetails: OidcDetails) {
|
||||||
navigateToMas(oidcDetails)
|
navigateToMas(oidcDetails)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateAccountContinue(url: String) {
|
override fun navigateToCreateAccount(url: String) {
|
||||||
backstack.push(NavTarget.CreateAccount(url))
|
backstack.push(NavTarget.CreateAccount(url))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoginPasswordNeeded() {
|
override fun navigateToLoginPassword() {
|
||||||
backstack.push(NavTarget.LoginPassword)
|
backstack.push(NavTarget.LoginPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onChangeAccountProvider() {
|
override fun navigateToChangeAccountProvider() {
|
||||||
backstack.push(NavTarget.ChangeAccountProvider)
|
backstack.push(NavTarget.ChangeAccountProvider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -221,7 +222,7 @@ class LoginFlowNode(
|
||||||
backstack.singleTop(confirmAccountProvider)
|
backstack.singleTop(confirmAccountProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOtherClick() {
|
override fun navigateToSearchAccountProvider() {
|
||||||
backstack.push(NavTarget.SearchAccountProvider)
|
backstack.push(NavTarget.SearchAccountProvider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -147,11 +147,11 @@ class QrCodeLoginFlowNode(
|
||||||
return when (navTarget) {
|
return when (navTarget) {
|
||||||
is NavTarget.Initial -> {
|
is NavTarget.Initial -> {
|
||||||
val callback = object : QrCodeIntroNode.Callback {
|
val callback = object : QrCodeIntroNode.Callback {
|
||||||
override fun onCancelClicked() {
|
override fun cancel() {
|
||||||
navigateUp()
|
navigateUp()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onContinue() {
|
override fun navigateToQrCodeScan() {
|
||||||
backstack.push(NavTarget.QrCodeScan)
|
backstack.push(NavTarget.QrCodeScan)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -159,11 +159,11 @@ class QrCodeLoginFlowNode(
|
||||||
}
|
}
|
||||||
is NavTarget.QrCodeScan -> {
|
is NavTarget.QrCodeScan -> {
|
||||||
val callback = object : QrCodeScanNode.Callback {
|
val callback = object : QrCodeScanNode.Callback {
|
||||||
override fun onScannedCode(qrCodeLoginData: MatrixQrCodeLoginData) {
|
override fun handleScannedCode(qrCodeLoginData: MatrixQrCodeLoginData) {
|
||||||
lifecycleScope.startAuthentication(qrCodeLoginData)
|
lifecycleScope.startAuthentication(qrCodeLoginData)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCancelClicked() {
|
override fun cancel() {
|
||||||
backstack.pop()
|
backstack.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -29,16 +29,10 @@ class ChangeAccountProviderNode(
|
||||||
) : Node(buildContext, plugins = plugins) {
|
) : Node(buildContext, plugins = plugins) {
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onDone()
|
fun onDone()
|
||||||
fun onOtherClick()
|
fun navigateToSearchAccountProvider()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onDone() {
|
private val callback: Callback = callback()
|
||||||
plugins<Callback>().forEach { it.onDone() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onOtherClick() {
|
|
||||||
plugins<Callback>().forEach { it.onOtherClick() }
|
|
||||||
}
|
|
||||||
|
|
||||||
@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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -29,22 +29,12 @@ class ChooseAccountProviderNode(
|
||||||
private val presenter: ChooseAccountProviderPresenter,
|
private val presenter: ChooseAccountProviderPresenter,
|
||||||
) : Node(buildContext, plugins = plugins) {
|
) : Node(buildContext, plugins = plugins) {
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onLoginPasswordNeeded()
|
fun navigateToLoginPassword()
|
||||||
fun onOidcDetails(oidcDetails: OidcDetails)
|
fun navigateToOidc(oidcDetails: OidcDetails)
|
||||||
fun onCreateAccountContinue(url: String)
|
fun navigateToCreateAccount(url: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onOidcDetails(oidcDetails: OidcDetails) {
|
private val callback: Callback = callback()
|
||||||
plugins<Callback>().forEach { it.onOidcDetails(oidcDetails) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onLoginPasswordNeeded() {
|
|
||||||
plugins<Callback>().forEach { it.onLoginPasswordNeeded() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onCreateAccountContinue(url: String) {
|
|
||||||
plugins<Callback>().forEach { it.onCreateAccountContinue(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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -42,27 +42,13 @@ class ConfirmAccountProviderNode(
|
||||||
)
|
)
|
||||||
|
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onLoginPasswordNeeded()
|
fun navigateToLoginPassword()
|
||||||
fun onOidcDetails(oidcDetails: OidcDetails)
|
fun navigateToOidc(oidcDetails: OidcDetails)
|
||||||
fun onCreateAccountContinue(url: String)
|
fun navigateToCreateAccount(url: String)
|
||||||
fun onChangeAccountProvider()
|
fun navigateToChangeAccountProvider()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onOidcDetails(data: OidcDetails) {
|
private val callback: Callback = callback()
|
||||||
plugins<Callback>().forEach { it.onOidcDetails(data) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onLoginPasswordNeeded() {
|
|
||||||
plugins<Callback>().forEach { it.onLoginPasswordNeeded() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onCreateAccountContinue(url: String) {
|
|
||||||
plugins<Callback>().forEach { it.onCreateAccountContinue(url) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onChangeAccountProvider() {
|
|
||||||
plugins<Callback>().forEach { it.onChangeAccountProvider() }
|
|
||||||
}
|
|
||||||
|
|
||||||
@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) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -34,13 +34,13 @@ class OnBoardingNode(
|
||||||
plugins = plugins
|
plugins = plugins
|
||||||
) {
|
) {
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onSignUp()
|
fun navigateToSignUpFlow()
|
||||||
fun onSignIn(mustChooseAccountProvider: Boolean)
|
fun navigateToSignInFlow(mustChooseAccountProvider: Boolean)
|
||||||
fun onSignInWithQrCode()
|
fun navigateToQrCode()
|
||||||
fun onReportProblem()
|
fun navigateToBugReport()
|
||||||
fun onLoginPasswordNeeded()
|
fun navigateToLoginPassword()
|
||||||
fun onOidcDetails(oidcDetails: OidcDetails)
|
fun navigateToOidc(oidcDetails: OidcDetails)
|
||||||
fun onCreateAccountContinue(url: String)
|
fun navigateToCreateAccount(url: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Params(
|
data class Params(
|
||||||
|
|
@ -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.onSignIn(mustChooseAccountProvider) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onSignUp() {
|
|
||||||
plugins<Callback>().forEach { it.onSignUp() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onSignInWithQrCode() {
|
|
||||||
plugins<Callback>().forEach { it.onSignInWithQrCode() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onReportProblem() {
|
|
||||||
plugins<Callback>().forEach { it.onReportProblem() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onOidcDetails(data: OidcDetails) {
|
|
||||||
plugins<Callback>().forEach { it.onOidcDetails(data) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onLoginPasswordNeeded() {
|
|
||||||
plugins<Callback>().forEach { it.onLoginPasswordNeeded() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onCreateAccountContinue(url: String) {
|
|
||||||
plugins<Callback>().forEach { it.onCreateAccountContinue(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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -26,25 +26,19 @@ class QrCodeIntroNode(
|
||||||
private val presenter: QrCodeIntroPresenter,
|
private val presenter: QrCodeIntroPresenter,
|
||||||
) : Node(buildContext, plugins = plugins) {
|
) : Node(buildContext, plugins = plugins) {
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onCancelClicked()
|
fun cancel()
|
||||||
fun onContinue()
|
fun navigateToQrCodeScan()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onCancelClicked() {
|
private val callback: Callback = callback()
|
||||||
plugins<Callback>().forEach { it.onCancelClicked() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onContinue() {
|
|
||||||
plugins<Callback>().forEach { it.onContinue() }
|
|
||||||
}
|
|
||||||
|
|
||||||
@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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -27,25 +27,19 @@ class QrCodeScanNode(
|
||||||
private val presenter: QrCodeScanPresenter,
|
private val presenter: QrCodeScanPresenter,
|
||||||
) : Node(buildContext, plugins = plugins) {
|
) : Node(buildContext, plugins = plugins) {
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onScannedCode(qrCodeLoginData: MatrixQrCodeLoginData)
|
fun handleScannedCode(qrCodeLoginData: MatrixQrCodeLoginData)
|
||||||
fun onCancelClicked()
|
fun cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onQrCodeDataReady(qrCodeLoginData: MatrixQrCodeLoginData) {
|
private val callback: Callback = callback()
|
||||||
plugins<Callback>().forEach { it.onScannedCode(qrCodeLoginData) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onCancelClicked() {
|
|
||||||
plugins<Callback>().forEach { it.onCancelClicked() }
|
|
||||||
}
|
|
||||||
|
|
||||||
@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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,16 +39,18 @@ class DefaultLoginEntryPointTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val callback = object : LoginEntryPoint.Callback {
|
val callback = object : LoginEntryPoint.Callback {
|
||||||
override fun onReportProblem() = lambdaError()
|
override fun navigateToBugReport() = lambdaError()
|
||||||
}
|
}
|
||||||
val params = LoginEntryPoint.Params(
|
val params = LoginEntryPoint.Params(
|
||||||
accountProvider = "ac",
|
accountProvider = "ac",
|
||||||
loginHint = "lh",
|
loginHint = "lh",
|
||||||
)
|
)
|
||||||
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
|
val result = entryPoint.createNode(
|
||||||
.params(params)
|
parentNode = parentNode,
|
||||||
.callback(callback)
|
buildContext = BuildContext.root(null),
|
||||||
.build()
|
params = params,
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
assertThat(result).isInstanceOf(LoginFlowNode::class.java)
|
assertThat(result).isInstanceOf(LoginFlowNode::class.java)
|
||||||
assertThat(result.plugins).contains(LoginFlowNode.Params(params.accountProvider, params.loginHint))
|
assertThat(result.plugins).contains(LoginFlowNode.Params(params.accountProvider, params.loginHint))
|
||||||
assertThat(result.plugins).contains(callback)
|
assertThat(result.plugins).contains(callback)
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,13 @@ import com.bumble.appyx.core.plugin.Plugin
|
||||||
import io.element.android.libraries.architecture.FeatureEntryPoint
|
import io.element.android.libraries.architecture.FeatureEntryPoint
|
||||||
|
|
||||||
interface LogoutEntryPoint : FeatureEntryPoint {
|
interface LogoutEntryPoint : FeatureEntryPoint {
|
||||||
fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder
|
fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
interface NodeBuilder {
|
buildContext: BuildContext,
|
||||||
fun callback(callback: Callback): NodeBuilder
|
callback: Callback,
|
||||||
fun build(): Node
|
): Node
|
||||||
}
|
|
||||||
|
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onChangeRecoveryKeyClick()
|
fun navigateToSecureBackup()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ package io.element.android.features.logout.impl
|
||||||
|
|
||||||
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 dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import io.element.android.features.logout.api.LogoutEntryPoint
|
import io.element.android.features.logout.api.LogoutEntryPoint
|
||||||
|
|
@ -17,18 +16,11 @@ import io.element.android.libraries.architecture.createNode
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
class DefaultLogoutEntryPoint : LogoutEntryPoint {
|
class DefaultLogoutEntryPoint : LogoutEntryPoint {
|
||||||
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): LogoutEntryPoint.NodeBuilder {
|
override fun createNode(
|
||||||
val plugins = ArrayList<Plugin>()
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
return object : LogoutEntryPoint.NodeBuilder {
|
callback: LogoutEntryPoint.Callback,
|
||||||
override fun callback(callback: LogoutEntryPoint.Callback): LogoutEntryPoint.NodeBuilder {
|
): Node {
|
||||||
plugins += callback
|
return parentNode.createNode<LogoutNode>(buildContext, listOf(callback))
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun build(): Node {
|
|
||||||
return parentNode.createNode<LogoutNode>(buildContext, plugins)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.onChangeRecoveryKeyClick() }
|
|
||||||
}
|
|
||||||
|
|
||||||
@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,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,13 @@ class DefaultLogoutEntryPointTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val callback = object : LogoutEntryPoint.Callback {
|
val callback = object : LogoutEntryPoint.Callback {
|
||||||
override fun onChangeRecoveryKeyClick() = lambdaError()
|
override fun navigateToSecureBackup() = lambdaError()
|
||||||
}
|
}
|
||||||
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
|
val result = entryPoint.createNode(
|
||||||
.callback(callback)
|
parentNode = parentNode,
|
||||||
.build()
|
buildContext = BuildContext.root(null),
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
assertThat(result).isInstanceOf(LogoutNode::class.java)
|
assertThat(result).isInstanceOf(LogoutNode::class.java)
|
||||||
assertThat(result.plugins).contains(callback)
|
assertThat(result.plugins).contains(callback)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ android {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.coroutines.core)
|
implementation(libs.coroutines.core)
|
||||||
|
implementation(projects.libraries.architecture)
|
||||||
implementation(projects.tests.testutils)
|
implementation(projects.tests.testutils)
|
||||||
api(projects.features.logout.api)
|
api(projects.features.logout.api)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.features.logout.test
|
||||||
|
|
||||||
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
import com.bumble.appyx.core.node.Node
|
||||||
|
import io.element.android.features.logout.api.LogoutEntryPoint
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
|
||||||
|
class FakeLogoutEntryPoint : LogoutEntryPoint {
|
||||||
|
override fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
callback: LogoutEntryPoint.Callback,
|
||||||
|
): Node = lambdaError()
|
||||||
|
}
|
||||||
|
|
@ -31,23 +31,22 @@ interface MessagesEntryPoint : FeatureEntryPoint {
|
||||||
data object PinnedMessages : InitialTarget
|
data object PinnedMessages : InitialTarget
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NodeBuilder {
|
|
||||||
fun params(params: Params): NodeBuilder
|
|
||||||
fun callback(callback: Callback): NodeBuilder
|
|
||||||
fun build(): Node
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onRoomDetailsClick()
|
fun navigateToRoomDetails()
|
||||||
fun onUserDataClick(userId: UserId)
|
fun navigateToRoomMemberDetails(userId: UserId)
|
||||||
fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean)
|
fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean)
|
||||||
fun forwardEvent(eventId: EventId)
|
fun forwardEvent(eventId: EventId)
|
||||||
fun openRoom(roomId: RoomId)
|
fun navigateToRoom(roomId: RoomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Params(val initialTarget: InitialTarget) : NodeInputs
|
data class Params(val initialTarget: InitialTarget) : NodeInputs
|
||||||
|
|
||||||
fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder
|
fun createNode(
|
||||||
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
params: Params,
|
||||||
|
callback: Callback,
|
||||||
|
): Node
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MessagesEntryPointNode {
|
interface MessagesEntryPointNode {
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,9 @@ dependencies {
|
||||||
testImplementation(projects.libraries.matrix.test)
|
testImplementation(projects.libraries.matrix.test)
|
||||||
testImplementation(projects.libraries.dateformatter.test)
|
testImplementation(projects.libraries.dateformatter.test)
|
||||||
testImplementation(projects.libraries.push.test)
|
testImplementation(projects.libraries.push.test)
|
||||||
|
testImplementation(projects.features.call.test)
|
||||||
|
testImplementation(projects.features.forward.test)
|
||||||
|
testImplementation(projects.features.knockrequests.test)
|
||||||
testImplementation(projects.features.location.test)
|
testImplementation(projects.features.location.test)
|
||||||
testImplementation(projects.features.networkmonitor.test)
|
testImplementation(projects.features.networkmonitor.test)
|
||||||
testImplementation(projects.features.messages.test)
|
testImplementation(projects.features.messages.test)
|
||||||
|
|
|
||||||
|
|
@ -9,34 +9,20 @@ package io.element.android.features.messages.impl
|
||||||
|
|
||||||
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 dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import io.element.android.features.messages.api.MessagesEntryPoint
|
import io.element.android.features.messages.api.MessagesEntryPoint
|
||||||
import io.element.android.libraries.architecture.NodeFactoriesBindings
|
import io.element.android.libraries.architecture.createNode
|
||||||
import io.element.android.libraries.architecture.bindings
|
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
|
|
||||||
@ContributesBinding(SessionScope::class)
|
@ContributesBinding(SessionScope::class)
|
||||||
class DefaultMessagesEntryPoint : MessagesEntryPoint {
|
class DefaultMessagesEntryPoint : MessagesEntryPoint {
|
||||||
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): MessagesEntryPoint.NodeBuilder {
|
override fun createNode(
|
||||||
val nodeFactories = parentNode.bindings<NodeFactoriesBindings>().nodeFactories()
|
parentNode: Node,
|
||||||
val plugins = ArrayList<Plugin>()
|
buildContext: BuildContext,
|
||||||
|
params: MessagesEntryPoint.Params,
|
||||||
return object : MessagesEntryPoint.NodeBuilder {
|
callback: MessagesEntryPoint.Callback,
|
||||||
override fun params(params: MessagesEntryPoint.Params): MessagesEntryPoint.NodeBuilder {
|
): Node {
|
||||||
plugins += MessagesEntryPoint.Params(params.initialTarget)
|
return parentNode.createNode<MessagesFlowNode>(buildContext, listOf(params, callback))
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun callback(callback: MessagesEntryPoint.Callback): MessagesEntryPoint.NodeBuilder {
|
|
||||||
plugins += callback
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun build(): Node {
|
|
||||||
return nodeFactories[MessagesFlowNode::class]!!.create(buildContext, plugins)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -220,18 +220,18 @@ class MessagesFlowNode(
|
||||||
return when (navTarget) {
|
return when (navTarget) {
|
||||||
is NavTarget.Messages -> {
|
is NavTarget.Messages -> {
|
||||||
val callback = object : MessagesNode.Callback {
|
val callback = object : MessagesNode.Callback {
|
||||||
override fun onRoomDetailsClick() {
|
override fun navigateToRoomDetails() {
|
||||||
callbacks.forEach { it.onRoomDetailsClick() }
|
callback.navigateToRoomDetails()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean {
|
override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean {
|
||||||
return processEventClick(
|
return processEventClick(
|
||||||
timelineMode = timelineMode,
|
timelineMode = timelineMode,
|
||||||
event = event,
|
event = event,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPreviewAttachments(attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?) {
|
override fun navigateToPreviewAttachments(attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?) {
|
||||||
backstack.push(
|
backstack.push(
|
||||||
NavTarget.AttachmentPreview(
|
NavTarget.AttachmentPreview(
|
||||||
attachment = attachments.first(),
|
attachment = attachments.first(),
|
||||||
|
|
@ -241,39 +241,39 @@ class MessagesFlowNode(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUserDataClick(userId: UserId) {
|
override fun navigateToRoomMemberDetails(userId: UserId) {
|
||||||
callbacks.forEach { it.onUserDataClick(userId) }
|
callback.navigateToRoomMemberDetails(userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPermalinkClick(data: PermalinkData) {
|
override fun handlePermalinkClick(data: PermalinkData) {
|
||||||
callbacks.forEach { it.onPermalinkClick(data, pushToBackstack = true) }
|
callback.handlePermalinkClick(data, pushToBackstack = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) {
|
override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) {
|
||||||
backstack.push(NavTarget.EventDebugInfo(eventId, debugInfo))
|
backstack.push(NavTarget.EventDebugInfo(eventId, debugInfo))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onForwardEventClick(eventId: EventId) {
|
override fun forwardEvent(eventId: EventId) {
|
||||||
backstack.push(NavTarget.ForwardEvent(eventId, fromPinnedEvents = false))
|
backstack.push(NavTarget.ForwardEvent(eventId, fromPinnedEvents = false))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onReportMessage(eventId: EventId, senderId: UserId) {
|
override fun navigateToReportMessage(eventId: EventId, senderId: UserId) {
|
||||||
backstack.push(NavTarget.ReportMessage(eventId, senderId))
|
backstack.push(NavTarget.ReportMessage(eventId, senderId))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSendLocationClick() {
|
override fun navigateToSendLocation() {
|
||||||
backstack.push(NavTarget.SendLocation(Timeline.Mode.Live))
|
backstack.push(NavTarget.SendLocation(Timeline.Mode.Live))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreatePollClick() {
|
override fun navigateToCreatePoll() {
|
||||||
backstack.push(NavTarget.CreatePoll(Timeline.Mode.Live))
|
backstack.push(NavTarget.CreatePoll(Timeline.Mode.Live))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onEditPollClick(eventId: EventId) {
|
override fun navigateToEditPoll(eventId: EventId) {
|
||||||
backstack.push(NavTarget.EditPoll(Timeline.Mode.Live, eventId))
|
backstack.push(NavTarget.EditPoll(Timeline.Mode.Live, eventId))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onJoinCallClick(roomId: RoomId) {
|
override fun navigateToRoomCall(roomId: RoomId) {
|
||||||
val callType = CallType.RoomCall(
|
val callType = CallType.RoomCall(
|
||||||
sessionId = sessionId,
|
sessionId = sessionId,
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
|
|
@ -282,15 +282,15 @@ class MessagesFlowNode(
|
||||||
elementCallEntryPoint.startCall(callType)
|
elementCallEntryPoint.startCall(callType)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewAllPinnedEvents() {
|
override fun navigateToPinnedMessagesList() {
|
||||||
backstack.push(NavTarget.PinnedMessagesList)
|
backstack.push(NavTarget.PinnedMessagesList)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewKnockRequests() {
|
override fun navigateToKnockRequestsList() {
|
||||||
backstack.push(NavTarget.KnockRequestsList)
|
backstack.push(NavTarget.KnockRequestsList)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) {
|
override fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) {
|
||||||
backstack.push(NavTarget.Thread(threadRootId, focusedEventId))
|
backstack.push(NavTarget.Thread(threadRootId, focusedEventId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -311,19 +311,21 @@ class MessagesFlowNode(
|
||||||
overlay.hide()
|
overlay.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewInTimeline(eventId: EventId) {
|
override fun viewInTimeline(eventId: EventId) {
|
||||||
viewInTimeline(eventId)
|
this@MessagesFlowNode.viewInTimeline(eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onForwardEvent(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
|
||||||
forwardEvent(eventId)
|
callback.forwardEvent(eventId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mediaViewerEntryPoint.nodeBuilder(this, buildContext)
|
mediaViewerEntryPoint.createNode(
|
||||||
.params(params)
|
parentNode = this,
|
||||||
.callback(callback)
|
buildContext = buildContext,
|
||||||
.build()
|
params = params,
|
||||||
|
callback = callback
|
||||||
|
)
|
||||||
}
|
}
|
||||||
is NavTarget.AttachmentPreview -> {
|
is NavTarget.AttachmentPreview -> {
|
||||||
val inputs = AttachmentsPreviewNode.Inputs(
|
val inputs = AttachmentsPreviewNode.Inputs(
|
||||||
|
|
@ -335,7 +337,11 @@ class MessagesFlowNode(
|
||||||
}
|
}
|
||||||
is NavTarget.LocationViewer -> {
|
is NavTarget.LocationViewer -> {
|
||||||
val inputs = ShowLocationEntryPoint.Inputs(navTarget.location, navTarget.description)
|
val inputs = ShowLocationEntryPoint.Inputs(navTarget.location, navTarget.description)
|
||||||
showLocationEntryPoint.createNode(this, buildContext, inputs)
|
showLocationEntryPoint.createNode(
|
||||||
|
parentNode = this,
|
||||||
|
buildContext = buildContext,
|
||||||
|
inputs = inputs,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
is NavTarget.EventDebugInfo -> {
|
is NavTarget.EventDebugInfo -> {
|
||||||
val inputs = EventDebugInfoNode.Inputs(navTarget.eventId, navTarget.debugInfo)
|
val inputs = EventDebugInfoNode.Inputs(navTarget.eventId, navTarget.debugInfo)
|
||||||
|
|
@ -352,70 +358,74 @@ 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.openRoom(roomId) }
|
callback.navigateToRoom(roomId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
forwardEntryPoint.nodeBuilder(this, buildContext)
|
forwardEntryPoint.createNode(
|
||||||
.params(params)
|
parentNode = this,
|
||||||
.callback(callback)
|
buildContext = buildContext,
|
||||||
.build()
|
params = params,
|
||||||
|
callback = callback,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
is NavTarget.ReportMessage -> {
|
is NavTarget.ReportMessage -> {
|
||||||
val inputs = ReportMessageNode.Inputs(navTarget.eventId, navTarget.senderId)
|
val inputs = ReportMessageNode.Inputs(navTarget.eventId, navTarget.senderId)
|
||||||
createNode<ReportMessageNode>(buildContext, listOf(inputs))
|
createNode<ReportMessageNode>(buildContext, listOf(inputs))
|
||||||
}
|
}
|
||||||
is NavTarget.SendLocation -> {
|
is NavTarget.SendLocation -> {
|
||||||
sendLocationEntryPoint
|
sendLocationEntryPoint.createNode(
|
||||||
.builder(navTarget.timelineMode)
|
parentNode = this,
|
||||||
.build(this, buildContext)
|
buildContext = buildContext,
|
||||||
|
timelineMode = navTarget.timelineMode,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
is NavTarget.CreatePoll -> {
|
is NavTarget.CreatePoll -> {
|
||||||
createPollEntryPoint.nodeBuilder(this, buildContext)
|
createPollEntryPoint.createNode(
|
||||||
.params(
|
parentNode = this,
|
||||||
CreatePollEntryPoint.Params(
|
buildContext = buildContext,
|
||||||
|
params = CreatePollEntryPoint.Params(
|
||||||
timelineMode = navTarget.timelineMode,
|
timelineMode = navTarget.timelineMode,
|
||||||
mode = CreatePollMode.NewPoll
|
mode = CreatePollMode.NewPoll
|
||||||
|
),
|
||||||
)
|
)
|
||||||
)
|
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
is NavTarget.EditPoll -> {
|
is NavTarget.EditPoll -> {
|
||||||
createPollEntryPoint.nodeBuilder(this, buildContext)
|
createPollEntryPoint.createNode(
|
||||||
.params(
|
parentNode = this,
|
||||||
CreatePollEntryPoint.Params(
|
buildContext = buildContext,
|
||||||
|
params = CreatePollEntryPoint.Params(
|
||||||
timelineMode = navTarget.timelineMode,
|
timelineMode = navTarget.timelineMode,
|
||||||
mode = CreatePollMode.EditPoll(eventId = navTarget.eventId)
|
mode = CreatePollMode.EditPoll(eventId = navTarget.eventId)
|
||||||
|
),
|
||||||
)
|
)
|
||||||
)
|
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
NavTarget.PinnedMessagesList -> {
|
NavTarget.PinnedMessagesList -> {
|
||||||
val callback = object : PinnedMessagesListNode.Callback {
|
val callback = object : PinnedMessagesListNode.Callback {
|
||||||
override fun onEventClick(event: TimelineItem.Event) {
|
override fun handleEventClick(event: TimelineItem.Event) {
|
||||||
processEventClick(
|
processEventClick(
|
||||||
timelineMode = Timeline.Mode.PinnedEvents,
|
timelineMode = Timeline.Mode.PinnedEvents,
|
||||||
event = event,
|
event = event,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUserDataClick(userId: UserId) {
|
override fun navigateToRoomMemberDetails(userId: UserId) {
|
||||||
callbacks.forEach { it.onUserDataClick(userId) }
|
callback.navigateToRoomMemberDetails(userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewInTimelineClick(eventId: EventId) {
|
override fun viewInTimeline(eventId: EventId) {
|
||||||
viewInTimeline(eventId)
|
this@MessagesFlowNode.viewInTimeline(eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRoomPermalinkClick(data: PermalinkData.RoomLink) {
|
override fun handlePermalinkClick(data: PermalinkData.RoomLink) {
|
||||||
callbacks.forEach { it.onPermalinkClick(data, pushToBackstack = !room.matches(data.roomIdOrAlias)) }
|
callback.handlePermalinkClick(data, pushToBackstack = !room.matches(data.roomIdOrAlias))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) {
|
override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) {
|
||||||
backstack.push(NavTarget.EventDebugInfo(eventId, debugInfo))
|
backstack.push(NavTarget.EventDebugInfo(eventId, debugInfo))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onForwardEventClick(eventId: EventId) {
|
override fun handleForwardEventClick(eventId: EventId) {
|
||||||
backstack.push(NavTarget.ForwardEvent(eventId = eventId, fromPinnedEvents = true))
|
backstack.push(NavTarget.ForwardEvent(eventId = eventId, fromPinnedEvents = true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -430,14 +440,14 @@ class MessagesFlowNode(
|
||||||
focusedEventId = navTarget.focusedEventId,
|
focusedEventId = navTarget.focusedEventId,
|
||||||
)
|
)
|
||||||
val callback = object : ThreadedMessagesNode.Callback {
|
val callback = object : ThreadedMessagesNode.Callback {
|
||||||
override fun onEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean {
|
override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean {
|
||||||
return processEventClick(
|
return processEventClick(
|
||||||
timelineMode = timelineMode,
|
timelineMode = timelineMode,
|
||||||
event = event,
|
event = event,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPreviewAttachments(attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?) {
|
override fun navigateToPreviewAttachments(attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?) {
|
||||||
backstack.push(
|
backstack.push(
|
||||||
NavTarget.AttachmentPreview(
|
NavTarget.AttachmentPreview(
|
||||||
attachment = attachments.first(),
|
attachment = attachments.first(),
|
||||||
|
|
@ -447,39 +457,39 @@ class MessagesFlowNode(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUserDataClick(userId: UserId) {
|
override fun navigateToRoomMemberDetails(userId: UserId) {
|
||||||
callbacks.forEach { it.onUserDataClick(userId) }
|
callback.navigateToRoomMemberDetails(userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPermalinkClick(data: PermalinkData) {
|
override fun handlePermalinkClick(data: PermalinkData) {
|
||||||
callbacks.forEach { it.onPermalinkClick(data, pushToBackstack = true) }
|
callback.handlePermalinkClick(data, pushToBackstack = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) {
|
override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) {
|
||||||
backstack.push(NavTarget.EventDebugInfo(eventId, debugInfo))
|
backstack.push(NavTarget.EventDebugInfo(eventId, debugInfo))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onForwardEventClick(eventId: EventId) {
|
override fun handleForwardEventClick(eventId: EventId) {
|
||||||
backstack.push(NavTarget.ForwardEvent(eventId, fromPinnedEvents = false))
|
backstack.push(NavTarget.ForwardEvent(eventId, fromPinnedEvents = false))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onReportMessage(eventId: EventId, senderId: UserId) {
|
override fun navigateToReportMessage(eventId: EventId, senderId: UserId) {
|
||||||
backstack.push(NavTarget.ReportMessage(eventId, senderId))
|
backstack.push(NavTarget.ReportMessage(eventId, senderId))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSendLocationClick() {
|
override fun navigateToSendLocation() {
|
||||||
backstack.push(NavTarget.SendLocation(Timeline.Mode.Thread(navTarget.threadRootId)))
|
backstack.push(NavTarget.SendLocation(Timeline.Mode.Thread(navTarget.threadRootId)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreatePollClick() {
|
override fun navigateToCreatePoll() {
|
||||||
backstack.push(NavTarget.CreatePoll(Timeline.Mode.Thread(navTarget.threadRootId)))
|
backstack.push(NavTarget.CreatePoll(Timeline.Mode.Thread(navTarget.threadRootId)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onEditPollClick(eventId: EventId) {
|
override fun navigateToEditPoll(eventId: EventId) {
|
||||||
backstack.push(NavTarget.EditPoll(Timeline.Mode.Thread(navTarget.threadRootId), eventId))
|
backstack.push(NavTarget.EditPoll(Timeline.Mode.Thread(navTarget.threadRootId), eventId))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onJoinCallClick(roomId: RoomId) {
|
override fun navigateToRoomCall(roomId: RoomId) {
|
||||||
val callType = CallType.RoomCall(
|
val callType = CallType.RoomCall(
|
||||||
sessionId = sessionId,
|
sessionId = sessionId,
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
|
|
@ -488,7 +498,7 @@ class MessagesFlowNode(
|
||||||
elementCallEntryPoint.startCall(callType)
|
elementCallEntryPoint.startCall(callType)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) {
|
override fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) {
|
||||||
backstack.push(NavTarget.Thread(threadRootId, focusedEventId))
|
backstack.push(NavTarget.Thread(threadRootId, focusedEventId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -502,11 +512,7 @@ class MessagesFlowNode(
|
||||||
roomIdOrAlias = room.roomId.toRoomIdOrAlias(),
|
roomIdOrAlias = room.roomId.toRoomIdOrAlias(),
|
||||||
eventId = eventId,
|
eventId = eventId,
|
||||||
)
|
)
|
||||||
callbacks.forEach { it.onPermalinkClick(permalinkData, pushToBackstack = false) }
|
callback.handlePermalinkClick(permalinkData, pushToBackstack = false)
|
||||||
}
|
|
||||||
|
|
||||||
private fun forwardEvent(eventId: EventId) {
|
|
||||||
callbacks.forEach { it.forwardEvent(eventId) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processEventClick(
|
private fun processEventClick(
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue