Merge pull request #2059 from element-hq/feature/fga/appyx_overlay

Appyx Overlay
This commit is contained in:
ganfra 2023-12-20 11:17:23 +01:00 committed by GitHub
commit 7d33409810
26 changed files with 404 additions and 216 deletions

View file

@ -25,7 +25,6 @@ import androidx.compose.ui.Modifier
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import com.bumble.appyx.core.composable.Children
import com.bumble.appyx.core.composable.PermanentChild import com.bumble.appyx.core.composable.PermanentChild
import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.modality.BuildContext
@ -57,8 +56,8 @@ import io.element.android.features.preferences.api.PreferencesEntryPoint
import io.element.android.features.roomlist.api.RoomListEntryPoint import io.element.android.features.roomlist.api.RoomListEntryPoint
import io.element.android.features.securebackup.api.SecureBackupEntryPoint import io.element.android.features.securebackup.api.SecureBackupEntryPoint
import io.element.android.features.verifysession.api.VerifySessionEntryPoint import io.element.android.features.verifysession.api.VerifySessionEntryPoint
import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.BaseFlowNode
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.deeplink.DeeplinkData import io.element.android.libraries.deeplink.DeeplinkData
@ -100,7 +99,7 @@ class LoggedInFlowNode @AssistedInject constructor(
private val lockScreenStateService: LockScreenService, private val lockScreenStateService: LockScreenService,
private val matrixClient: MatrixClient, private val matrixClient: MatrixClient,
snackbarDispatcher: SnackbarDispatcher, snackbarDispatcher: SnackbarDispatcher,
) : BackstackNode<LoggedInFlowNode.NavTarget>( ) : BaseFlowNode<LoggedInFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.RoomList, initialElement = NavTarget.RoomList,
savedStateMap = buildContext.savedStateMap, savedStateMap = buildContext.savedStateMap,
@ -384,12 +383,7 @@ class LoggedInFlowNode @AssistedInject constructor(
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
Box(modifier = modifier) { Box(modifier = modifier) {
val lockScreenState by lockScreenStateService.lockState.collectAsState() val lockScreenState by lockScreenStateService.lockState.collectAsState()
Children( BackstackView()
navModel = backstack,
modifier = Modifier,
// Animate navigation to settings and to a room
transitionHandler = rememberDefaultTransitionHandler(),
)
val isFtueDisplayed by ftueState.shouldDisplayFlow.collectAsState() val isFtueDisplayed by ftueState.shouldDisplayFlow.collectAsState()
if (!isFtueDisplayed) { if (!isFtueDisplayed) {
PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent) PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent)

View file

@ -20,7 +20,6 @@ import android.os.Parcelable
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import coil.Coil import coil.Coil
import com.bumble.appyx.core.composable.Children
import com.bumble.appyx.core.lifecycle.subscribe 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
@ -33,8 +32,8 @@ import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.login.api.LoginEntryPoint import io.element.android.features.login.api.LoginEntryPoint
import io.element.android.features.onboarding.api.OnBoardingEntryPoint import io.element.android.features.onboarding.api.OnBoardingEntryPoint
import io.element.android.features.preferences.api.ConfigureTracingEntryPoint import io.element.android.features.preferences.api.ConfigureTracingEntryPoint
import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.ui.media.NotLoggedInImageLoaderFactory import io.element.android.libraries.matrix.ui.media.NotLoggedInImageLoaderFactory
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@ -47,7 +46,7 @@ class NotLoggedInFlowNode @AssistedInject constructor(
private val configureTracingEntryPoint: ConfigureTracingEntryPoint, private val configureTracingEntryPoint: ConfigureTracingEntryPoint,
private val loginEntryPoint: LoginEntryPoint, private val loginEntryPoint: LoginEntryPoint,
private val notLoggedInImageLoaderFactory: NotLoggedInImageLoaderFactory, private val notLoggedInImageLoaderFactory: NotLoggedInImageLoaderFactory,
) : BackstackNode<NotLoggedInFlowNode.NavTarget>( ) : BaseFlowNode<NotLoggedInFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.OnBoarding, initialElement = NavTarget.OnBoarding,
savedStateMap = buildContext.savedStateMap savedStateMap = buildContext.savedStateMap
@ -111,11 +110,6 @@ class NotLoggedInFlowNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
Children( BackstackView()
navModel = backstack,
modifier = modifier,
// Animate navigation to login screen
transitionHandler = rememberDefaultTransitionHandler(),
)
} }
} }

View file

@ -24,7 +24,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.bumble.appyx.core.composable.Children
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.node.node import com.bumble.appyx.core.node.node
@ -46,8 +45,8 @@ import io.element.android.features.login.api.oidc.OidcAction
import io.element.android.features.login.api.oidc.OidcActionFlow import io.element.android.features.login.api.oidc.OidcActionFlow
import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint
import io.element.android.features.signedout.api.SignedOutEntryPoint import io.element.android.features.signedout.api.SignedOutEntryPoint
import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.BaseFlowNode
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.deeplink.DeeplinkData import io.element.android.libraries.deeplink.DeeplinkData
@ -74,7 +73,7 @@ class RootFlowNode @AssistedInject constructor(
private val signedOutEntryPoint: SignedOutEntryPoint, private val signedOutEntryPoint: SignedOutEntryPoint,
private val intentResolver: IntentResolver, private val intentResolver: IntentResolver,
private val oidcActionFlow: OidcActionFlow, private val oidcActionFlow: OidcActionFlow,
) : BackstackNode<RootFlowNode.NavTarget>( ) : BaseFlowNode<RootFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.SplashScreen, initialElement = NavTarget.SplashScreen,
savedStateMap = buildContext.savedStateMap, savedStateMap = buildContext.savedStateMap,
@ -172,11 +171,7 @@ class RootFlowNode @AssistedInject constructor(
modifier = modifier, modifier = modifier,
onOpenBugReport = this::onOpenBugReport, onOpenBugReport = this::onOpenBugReport,
) { ) {
Children( BackstackView()
navModel = backstack,
// Animate opening the bug report screen
transitionHandler = rememberDefaultTransitionHandler(),
)
} }
} }

View file

@ -25,7 +25,6 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.bumble.appyx.core.composable.Children
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.node.node import com.bumble.appyx.core.node.node
@ -38,7 +37,8 @@ import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkMonitor
import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.features.networkmonitor.api.NetworkStatus
import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.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
@ -57,7 +57,7 @@ class RoomFlowNode @AssistedInject constructor(
loadingRoomStateFlowFactory: LoadingRoomStateFlowFactory, loadingRoomStateFlowFactory: LoadingRoomStateFlowFactory,
private val networkMonitor: NetworkMonitor, private val networkMonitor: NetworkMonitor,
) : ) :
BackstackNode<RoomFlowNode.NavTarget>( BaseFlowNode<RoomFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.Loading, initialElement = NavTarget.Loading,
savedStateMap = buildContext.savedStateMap, savedStateMap = buildContext.savedStateMap,
@ -130,10 +130,7 @@ class RoomFlowNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
Children( BackstackView()
navModel = backstack,
modifier = modifier,
)
} }
} }

View file

@ -22,7 +22,6 @@ import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.bumble.appyx.core.composable.Children
import com.bumble.appyx.core.lifecycle.subscribe 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
@ -35,9 +34,9 @@ import io.element.android.anvilannotations.ContributesNode
import io.element.android.appnav.di.RoomComponentFactory import io.element.android.appnav.di.RoomComponentFactory
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.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
import io.element.android.libraries.architecture.inputs import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.DaggerComponentOwner import io.element.android.libraries.di.DaggerComponentOwner
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
@ -64,7 +63,7 @@ class RoomLoadedFlowNode @AssistedInject constructor(
private val appCoroutineScope: CoroutineScope, private val appCoroutineScope: CoroutineScope,
roomComponentFactory: RoomComponentFactory, roomComponentFactory: RoomComponentFactory,
roomMembershipObserver: RoomMembershipObserver, roomMembershipObserver: RoomMembershipObserver,
) : BackstackNode<RoomLoadedFlowNode.NavTarget>( ) : BaseFlowNode<RoomLoadedFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = plugins.filterIsInstance(Inputs::class.java).first().initialElement, initialElement = plugins.filterIsInstance(Inputs::class.java).first().initialElement,
savedStateMap = buildContext.savedStateMap, savedStateMap = buildContext.savedStateMap,
@ -202,10 +201,6 @@ class RoomLoadedFlowNode @AssistedInject constructor(
} }
} }
} }
Children( BackstackView()
navModel = backstack,
modifier = modifier,
transitionHandler = rememberDefaultTransitionHandler(),
)
} }
} }

View file

@ -19,7 +19,6 @@ package io.element.android.features.createroom.impl
import android.os.Parcelable import android.os.Parcelable
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.bumble.appyx.core.composable.Children
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
@ -32,8 +31,8 @@ import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.createroom.impl.addpeople.AddPeopleNode 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.features.createroom.impl.di.CreateRoomComponent import io.element.android.features.createroom.impl.di.CreateRoomComponent
import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.di.DaggerComponentOwner import io.element.android.libraries.di.DaggerComponentOwner
@ -45,7 +44,7 @@ class ConfigureRoomFlowNode @AssistedInject constructor(
@Assisted buildContext: BuildContext, @Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
) : DaggerComponentOwner, ) : DaggerComponentOwner,
BackstackNode<ConfigureRoomFlowNode.NavTarget>( BaseFlowNode<ConfigureRoomFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.Root, initialElement = NavTarget.Root,
savedStateMap = buildContext.savedStateMap, savedStateMap = buildContext.savedStateMap,
@ -88,10 +87,6 @@ class ConfigureRoomFlowNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
Children( BackstackView()
navModel = backstack,
modifier = modifier,
transitionHandler = rememberDefaultTransitionHandler()
)
} }
} }

View file

@ -19,7 +19,6 @@ package io.element.android.features.createroom.impl
import android.os.Parcelable import android.os.Parcelable
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.bumble.appyx.core.composable.Children
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
@ -32,8 +31,8 @@ import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.createroom.api.CreateRoomEntryPoint import io.element.android.features.createroom.api.CreateRoomEntryPoint
import io.element.android.features.createroom.impl.configureroom.ConfigureRoomNode import io.element.android.features.createroom.impl.configureroom.ConfigureRoomNode
import io.element.android.features.createroom.impl.root.CreateRoomRootNode import io.element.android.features.createroom.impl.root.CreateRoomRootNode
import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.BaseFlowNode
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
@ -43,7 +42,7 @@ import kotlinx.parcelize.Parcelize
class CreateRoomFlowNode @AssistedInject constructor( class CreateRoomFlowNode @AssistedInject constructor(
@Assisted buildContext: BuildContext, @Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
) : BackstackNode<CreateRoomFlowNode.NavTarget>( ) : BaseFlowNode<CreateRoomFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.Root, initialElement = NavTarget.Root,
savedStateMap = buildContext.savedStateMap, savedStateMap = buildContext.savedStateMap,
@ -87,10 +86,6 @@ class CreateRoomFlowNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
Children( BackstackView()
navModel = backstack,
modifier = modifier,
transitionHandler = rememberDefaultTransitionHandler()
)
} }
} }

View file

@ -20,7 +20,6 @@ import android.os.Parcelable
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.bumble.appyx.core.composable.Children
import com.bumble.appyx.core.lifecycle.subscribe 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.navigation.backpresshandlerstrategies.BaseBackPressHandlerStrategy import com.bumble.appyx.core.navigation.backpresshandlerstrategies.BaseBackPressHandlerStrategy
@ -40,8 +39,8 @@ import io.element.android.features.ftue.impl.state.DefaultFtueState
import io.element.android.features.ftue.impl.state.FtueStep import io.element.android.features.ftue.impl.state.FtueStep
import io.element.android.features.ftue.impl.welcome.WelcomeNode import io.element.android.features.ftue.impl.welcome.WelcomeNode
import io.element.android.features.lockscreen.api.LockScreenEntryPoint import io.element.android.features.lockscreen.api.LockScreenEntryPoint
import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
@ -62,7 +61,7 @@ class FtueFlowNode @AssistedInject constructor(
private val analyticsEntryPoint: AnalyticsEntryPoint, private val analyticsEntryPoint: AnalyticsEntryPoint,
private val analyticsService: AnalyticsService, private val analyticsService: AnalyticsService,
private val lockScreenEntryPoint: LockScreenEntryPoint, private val lockScreenEntryPoint: LockScreenEntryPoint,
) : BackstackNode<FtueFlowNode.NavTarget>( ) : BaseFlowNode<FtueFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.Placeholder, initialElement = NavTarget.Placeholder,
savedStateMap = buildContext.savedStateMap, savedStateMap = buildContext.savedStateMap,
@ -181,11 +180,7 @@ class FtueFlowNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
Children( BackstackView()
navModel = backstack,
modifier = modifier,
transitionHandler = rememberDefaultTransitionHandler(),
)
} }
@ContributesNode(AppScope::class) @ContributesNode(AppScope::class)

View file

@ -19,7 +19,6 @@ package io.element.android.features.lockscreen.impl
import android.os.Parcelable import android.os.Parcelable
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.bumble.appyx.core.composable.Children
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
@ -32,9 +31,9 @@ import io.element.android.features.lockscreen.api.LockScreenEntryPoint
import io.element.android.features.lockscreen.impl.settings.LockScreenSettingsFlowNode import io.element.android.features.lockscreen.impl.settings.LockScreenSettingsFlowNode
import io.element.android.features.lockscreen.impl.setup.LockScreenSetupFlowNode import io.element.android.features.lockscreen.impl.setup.LockScreenSetupFlowNode
import io.element.android.features.lockscreen.impl.unlock.PinUnlockNode import io.element.android.features.lockscreen.impl.unlock.PinUnlockNode
import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
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
@ -43,7 +42,7 @@ import kotlinx.parcelize.Parcelize
class LockScreenFlowNode @AssistedInject constructor( class LockScreenFlowNode @AssistedInject constructor(
@Assisted buildContext: BuildContext, @Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
) : BackstackNode<LockScreenFlowNode.NavTarget>( ) : BaseFlowNode<LockScreenFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = plugins.filterIsInstance(Inputs::class.java).first().initialNavTarget, initialElement = plugins.filterIsInstance(Inputs::class.java).first().initialNavTarget,
savedStateMap = buildContext.savedStateMap, savedStateMap = buildContext.savedStateMap,
@ -93,10 +92,6 @@ class LockScreenFlowNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
Children( BackstackView()
navModel = backstack,
modifier = modifier,
transitionHandler = rememberDefaultTransitionHandler(),
)
} }
} }

View file

@ -20,7 +20,6 @@ import android.os.Parcelable
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.bumble.appyx.core.composable.Children
import com.bumble.appyx.core.lifecycle.subscribe 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
@ -36,8 +35,8 @@ import io.element.android.features.lockscreen.impl.pin.DefaultPinCodeManagerCall
import io.element.android.features.lockscreen.impl.pin.PinCodeManager import io.element.android.features.lockscreen.impl.pin.PinCodeManager
import io.element.android.features.lockscreen.impl.setup.pin.SetupPinNode import io.element.android.features.lockscreen.impl.setup.pin.SetupPinNode
import io.element.android.features.lockscreen.impl.unlock.PinUnlockNode import io.element.android.features.lockscreen.impl.unlock.PinUnlockNode
import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.BaseFlowNode
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.coroutines.flow.first import kotlinx.coroutines.flow.first
@ -49,7 +48,7 @@ class LockScreenSettingsFlowNode @AssistedInject constructor(
@Assisted buildContext: BuildContext, @Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
private val pinCodeManager: PinCodeManager, private val pinCodeManager: PinCodeManager,
) : BackstackNode<LockScreenSettingsFlowNode.NavTarget>( ) : BaseFlowNode<LockScreenSettingsFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.Unknown, initialElement = NavTarget.Unknown,
savedStateMap = buildContext.savedStateMap, savedStateMap = buildContext.savedStateMap,
@ -132,10 +131,6 @@ class LockScreenSettingsFlowNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
Children( BackstackView()
navModel = backstack,
modifier = modifier,
transitionHandler = rememberDefaultTransitionHandler(),
)
} }
} }

View file

@ -19,7 +19,6 @@ package io.element.android.features.lockscreen.impl.setup
import android.os.Parcelable import android.os.Parcelable
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.bumble.appyx.core.composable.Children
import com.bumble.appyx.core.lifecycle.subscribe 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
@ -34,8 +33,8 @@ import io.element.android.features.lockscreen.impl.pin.DefaultPinCodeManagerCall
import io.element.android.features.lockscreen.impl.pin.PinCodeManager import io.element.android.features.lockscreen.impl.pin.PinCodeManager
import io.element.android.features.lockscreen.impl.setup.biometric.SetupBiometricNode import io.element.android.features.lockscreen.impl.setup.biometric.SetupBiometricNode
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.BackstackNode import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.BaseFlowNode
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
@ -45,7 +44,7 @@ class LockScreenSetupFlowNode @AssistedInject constructor(
@Assisted buildContext: BuildContext, @Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
private val pinCodeManager: PinCodeManager, private val pinCodeManager: PinCodeManager,
) : BackstackNode<LockScreenSetupFlowNode.NavTarget>( ) : BaseFlowNode<LockScreenSetupFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.Pin, initialElement = NavTarget.Pin,
savedStateMap = buildContext.savedStateMap, savedStateMap = buildContext.savedStateMap,
@ -106,10 +105,6 @@ class LockScreenSetupFlowNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
Children( BackstackView()
navModel = backstack,
modifier = modifier,
transitionHandler = rememberDefaultTransitionHandler(),
)
} }
} }

View file

@ -23,7 +23,6 @@ import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.bumble.appyx.core.composable.Children
import com.bumble.appyx.core.lifecycle.subscribe 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
@ -48,9 +47,9 @@ import io.element.android.features.login.impl.screens.loginpassword.LoginFormSta
import io.element.android.features.login.impl.screens.loginpassword.LoginPasswordNode import io.element.android.features.login.impl.screens.loginpassword.LoginPasswordNode
import io.element.android.features.login.impl.screens.searchaccountprovider.SearchAccountProviderNode import io.element.android.features.login.impl.screens.searchaccountprovider.SearchAccountProviderNode
import io.element.android.features.login.impl.screens.waitlistscreen.WaitListNode import io.element.android.features.login.impl.screens.waitlistscreen.WaitListNode
import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
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.AppScope import io.element.android.libraries.di.AppScope
@ -68,7 +67,7 @@ class LoginFlowNode @AssistedInject constructor(
private val accountProviderDataSource: AccountProviderDataSource, private val accountProviderDataSource: AccountProviderDataSource,
private val defaultLoginUserStory: DefaultLoginUserStory, private val defaultLoginUserStory: DefaultLoginUserStory,
private val oidcActionFlow: OidcActionFlow, private val oidcActionFlow: OidcActionFlow,
) : BackstackNode<LoginFlowNode.NavTarget>( ) : BaseFlowNode<LoginFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.ConfirmAccountProvider, initialElement = NavTarget.ConfirmAccountProvider,
savedStateMap = buildContext.savedStateMap, savedStateMap = buildContext.savedStateMap,
@ -217,11 +216,6 @@ class LoginFlowNode @AssistedInject constructor(
accountProviderDataSource.reset() accountProviderDataSource.reset()
} }
} }
Children( BackstackView()
navModel = backstack,
modifier = modifier,
// Animate transition to change server screen
transitionHandler = rememberDefaultTransitionHandler(),
)
} }
} }

View file

@ -20,9 +20,9 @@ import android.content.Context
import android.os.Parcelable import android.os.Parcelable
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.bumble.appyx.core.composable.Children
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.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.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
@ -49,9 +49,11 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
import io.element.android.features.poll.api.create.CreatePollEntryPoint 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.BackstackNode import io.element.android.libraries.architecture.BackstackWithOverlayBox
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.BaseFlowNode
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.operation.show
import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
@ -74,16 +76,23 @@ class MessagesFlowNode @AssistedInject constructor(
private val sendLocationEntryPoint: SendLocationEntryPoint, private val sendLocationEntryPoint: SendLocationEntryPoint,
private val showLocationEntryPoint: ShowLocationEntryPoint, private val showLocationEntryPoint: ShowLocationEntryPoint,
private val createPollEntryPoint: CreatePollEntryPoint, private val createPollEntryPoint: CreatePollEntryPoint,
) : BackstackNode<MessagesFlowNode.NavTarget>( ) : BaseFlowNode<MessagesFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.Messages, initialElement = NavTarget.Messages,
savedStateMap = buildContext.savedStateMap, savedStateMap = buildContext.savedStateMap,
), ),
overlay = Overlay(
savedStateMap = buildContext.savedStateMap,
),
buildContext = buildContext, buildContext = buildContext,
plugins = plugins plugins = plugins
) { ) {
sealed interface NavTarget : Parcelable { sealed interface NavTarget : Parcelable {
@Parcelize
data object Empty : NavTarget
@Parcelize @Parcelize
data object Messages : NavTarget data object Messages : NavTarget
@ -223,6 +232,9 @@ class MessagesFlowNode @AssistedInject constructor(
.params(CreatePollEntryPoint.Params(mode = CreatePollMode.EditPoll(eventId = navTarget.eventId))) .params(CreatePollEntryPoint.Params(mode = CreatePollMode.EditPoll(eventId = navTarget.eventId)))
.build() .build()
} }
NavTarget.Empty -> {
node(buildContext) {}
}
} }
} }
@ -239,7 +251,7 @@ class MessagesFlowNode @AssistedInject constructor(
mediaSource = event.content.mediaSource, mediaSource = event.content.mediaSource,
thumbnailSource = event.content.thumbnailSource, thumbnailSource = event.content.thumbnailSource,
) )
backstack.push(navTarget) overlay.show(navTarget)
} }
is TimelineItemVideoContent -> { is TimelineItemVideoContent -> {
val navTarget = NavTarget.MediaViewer( val navTarget = NavTarget.MediaViewer(
@ -252,7 +264,7 @@ class MessagesFlowNode @AssistedInject constructor(
mediaSource = event.content.videoSource, mediaSource = event.content.videoSource,
thumbnailSource = event.content.thumbnailSource, thumbnailSource = event.content.thumbnailSource,
) )
backstack.push(navTarget) overlay.show(navTarget)
} }
is TimelineItemFileContent -> { is TimelineItemFileContent -> {
val navTarget = NavTarget.MediaViewer( val navTarget = NavTarget.MediaViewer(
@ -265,7 +277,7 @@ class MessagesFlowNode @AssistedInject constructor(
mediaSource = event.content.fileSource, mediaSource = event.content.fileSource,
thumbnailSource = event.content.thumbnailSource, thumbnailSource = event.content.thumbnailSource,
) )
backstack.push(navTarget) overlay.show(navTarget)
} }
is TimelineItemAudioContent -> { is TimelineItemAudioContent -> {
val navTarget = NavTarget.MediaViewer( val navTarget = NavTarget.MediaViewer(
@ -278,14 +290,14 @@ class MessagesFlowNode @AssistedInject constructor(
mediaSource = event.content.mediaSource, mediaSource = event.content.mediaSource,
thumbnailSource = null, thumbnailSource = null,
) )
backstack.push(navTarget) overlay.show(navTarget)
} }
is TimelineItemLocationContent -> { is TimelineItemLocationContent -> {
val navTarget = NavTarget.LocationViewer( val navTarget = NavTarget.LocationViewer(
location = event.content.location, location = event.content.location,
description = event.content.description, description = event.content.description,
) )
backstack.push(navTarget) overlay.show(navTarget)
} }
else -> Unit else -> Unit
} }
@ -293,10 +305,6 @@ class MessagesFlowNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
Children( BackstackWithOverlayBox(modifier)
navModel = backstack,
modifier = modifier,
transitionHandler = rememberDefaultTransitionHandler(),
)
} }
} }

View file

@ -19,7 +19,6 @@ package io.element.android.features.poll.impl.history
import android.os.Parcelable import android.os.Parcelable
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.bumble.appyx.core.composable.Children
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
@ -30,8 +29,8 @@ import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.poll.api.create.CreatePollEntryPoint 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.BackstackNode import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
@ -42,7 +41,7 @@ class PollHistoryFlowNode @AssistedInject constructor(
@Assisted buildContext: BuildContext, @Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
private val createPollEntryPoint: CreatePollEntryPoint, private val createPollEntryPoint: CreatePollEntryPoint,
) : BackstackNode<PollHistoryFlowNode.NavTarget>( ) : BaseFlowNode<PollHistoryFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.Root, initialElement = NavTarget.Root,
savedStateMap = buildContext.savedStateMap, savedStateMap = buildContext.savedStateMap,
@ -82,11 +81,7 @@ class PollHistoryFlowNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
Children( BackstackView()
navModel = backstack,
modifier = modifier,
transitionHandler = rememberDefaultTransitionHandler()
)
} }
} }

View file

@ -19,7 +19,6 @@ package io.element.android.features.preferences.impl
import android.os.Parcelable import android.os.Parcelable
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.bumble.appyx.core.composable.Children
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
@ -41,8 +40,8 @@ import io.element.android.features.preferences.impl.notifications.NotificationSe
import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingNode import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingNode
import io.element.android.features.preferences.impl.root.PreferencesRootNode import io.element.android.features.preferences.impl.root.PreferencesRootNode
import io.element.android.features.preferences.impl.user.editprofile.EditUserProfileNode import io.element.android.features.preferences.impl.user.editprofile.EditUserProfileNode
import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.BaseFlowNode
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
@ -55,7 +54,7 @@ class PreferencesFlowNode @AssistedInject constructor(
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
private val lockScreenEntryPoint: LockScreenEntryPoint, private val lockScreenEntryPoint: LockScreenEntryPoint,
private val logoutEntryPoint: LogoutEntryPoint, private val logoutEntryPoint: LogoutEntryPoint,
) : BackstackNode<PreferencesFlowNode.NavTarget>( ) : BaseFlowNode<PreferencesFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = plugins.filterIsInstance<PreferencesEntryPoint.Params>().first().initialElement.toNavTarget(), initialElement = plugins.filterIsInstance<PreferencesEntryPoint.Params>().first().initialElement.toNavTarget(),
savedStateMap = buildContext.savedStateMap, savedStateMap = buildContext.savedStateMap,
@ -210,10 +209,6 @@ class PreferencesFlowNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
Children( BackstackView()
navModel = backstack,
modifier = modifier,
transitionHandler = rememberDefaultTransitionHandler()
)
} }
} }

View file

@ -19,7 +19,6 @@ package io.element.android.features.roomdetails.impl
import android.os.Parcelable import android.os.Parcelable
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.bumble.appyx.core.composable.Children
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
@ -37,8 +36,8 @@ import io.element.android.features.roomdetails.impl.members.RoomMemberListNode
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsNode import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsNode
import io.element.android.features.roomdetails.impl.members.details.avatar.AvatarPreviewNode import io.element.android.features.roomdetails.impl.members.details.avatar.AvatarPreviewNode
import io.element.android.features.roomdetails.impl.notificationsettings.RoomNotificationSettingsNode import io.element.android.features.roomdetails.impl.notificationsettings.RoomNotificationSettingsNode
import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.RoomScope
@ -54,7 +53,7 @@ class RoomDetailsFlowNode @AssistedInject constructor(
@Assisted buildContext: BuildContext, @Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
private val pollHistoryEntryPoint: PollHistoryEntryPoint, private val pollHistoryEntryPoint: PollHistoryEntryPoint,
) : BackstackNode<RoomDetailsFlowNode.NavTarget>( ) : BaseFlowNode<RoomDetailsFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = plugins.filterIsInstance<RoomDetailsEntryPoint.Params>().first().initialElement.toNavTarget(), initialElement = plugins.filterIsInstance<RoomDetailsEntryPoint.Params>().first().initialElement.toNavTarget(),
savedStateMap = buildContext.savedStateMap, savedStateMap = buildContext.savedStateMap,
@ -196,10 +195,6 @@ class RoomDetailsFlowNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
Children( BackstackView()
navModel = backstack,
modifier = modifier,
transitionHandler = rememberDefaultTransitionHandler(),
)
} }
} }

View file

@ -19,7 +19,6 @@ package io.element.android.features.securebackup.impl
import android.os.Parcelable import android.os.Parcelable
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.bumble.appyx.core.composable.Children
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
@ -33,8 +32,8 @@ import io.element.android.features.securebackup.impl.enable.SecureBackupEnableNo
import io.element.android.features.securebackup.impl.enter.SecureBackupEnterRecoveryKeyNode import io.element.android.features.securebackup.impl.enter.SecureBackupEnterRecoveryKeyNode
import io.element.android.features.securebackup.impl.root.SecureBackupRootNode import io.element.android.features.securebackup.impl.root.SecureBackupRootNode
import io.element.android.features.securebackup.impl.setup.SecureBackupSetupNode import io.element.android.features.securebackup.impl.setup.SecureBackupSetupNode
import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.BaseFlowNode
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
@ -43,7 +42,7 @@ import kotlinx.parcelize.Parcelize
class SecureBackupFlowNode @AssistedInject constructor( class SecureBackupFlowNode @AssistedInject constructor(
@Assisted buildContext: BuildContext, @Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
) : BackstackNode<SecureBackupFlowNode.NavTarget>( ) : BaseFlowNode<SecureBackupFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.Root, initialElement = NavTarget.Root,
savedStateMap = buildContext.savedStateMap, savedStateMap = buildContext.savedStateMap,
@ -124,10 +123,6 @@ class SecureBackupFlowNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
Children( BackstackView()
navModel = backstack,
modifier = modifier,
transitionHandler = rememberDefaultTransitionHandler()
)
} }
} }

View file

@ -15,6 +15,7 @@
*/ */
plugins { plugins {
id("io.element.android-compose-library") id("io.element.android-compose-library")
id("kotlin-parcelize")
} }
android { android {

View file

@ -1,57 +0,0 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.architecture
import androidx.compose.runtime.Stable
import com.bumble.appyx.core.children.ChildEntry
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.navigation.model.combined.plus
import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.node.ParentNode
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.navmodel.backstack.BackStack
/**
* This class is just an helper for configuring a backstack directly in the constructor.
* With this we can more easily use constructor injection without having a secondary constructor to create the [BackStack] instance.
* Can be used instead of [ParentNode] in flow nodes.
*/
@Stable
abstract class BackstackNode<NavTarget : Any>(
val backstack: BackStack<NavTarget>,
buildContext: BuildContext,
plugins: List<Plugin>,
val permanentNavModel: PermanentNavModel<NavTarget> = PermanentNavModel(emptySet(), null),
childKeepMode: ChildEntry.KeepMode = ChildEntry.KeepMode.KEEP,
) : ParentNode<NavTarget>(
navModel = backstack + permanentNavModel,
buildContext = buildContext,
plugins = plugins,
childKeepMode = childKeepMode,
) {
override fun onBuilt() {
super.onBuilt()
lifecycle.logLifecycle(this::class.java.simpleName)
whenChildAttached<Node> { _, child ->
// BackstackNode will be logged by their parent.
if (child !is BackstackNode<*>) {
child.lifecycle.logLifecycle(child::class.java.simpleName)
}
}
}
}

View file

@ -0,0 +1,106 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.architecture
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
import com.bumble.appyx.core.children.ChildEntry
import com.bumble.appyx.core.composable.Children
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.navigation.model.combined.plus
import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel
import com.bumble.appyx.core.navigation.transition.TransitionHandler
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.node.ParentNode
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.transitionhandler.rememberBackstackFader
import com.bumble.appyx.navmodel.backstack.transitionhandler.rememberBackstackSlider
import io.element.android.libraries.architecture.overlay.Overlay
/**
* This class is a [ParentNode] that contains a [BackStack] and an [Overlay]. It is used to represent a flow in the app.
* Should be used instead of [ParentNode] in flow nodes.
*/
@Stable
abstract class BaseFlowNode<NavTarget : Any>(
val backstack: BackStack<NavTarget>,
buildContext: BuildContext,
plugins: List<Plugin>,
val overlay: Overlay<NavTarget> = Overlay(null),
val permanentNavModel: PermanentNavModel<NavTarget> = PermanentNavModel(emptySet(), null),
childKeepMode: ChildEntry.KeepMode = ChildEntry.KeepMode.KEEP,
) : ParentNode<NavTarget>(
navModel = overlay + backstack + permanentNavModel,
buildContext = buildContext,
plugins = plugins,
childKeepMode = childKeepMode,
) {
override fun onBuilt() {
super.onBuilt()
lifecycle.logLifecycle(this::class.java.simpleName)
whenChildAttached<Node> { _, child ->
// BackstackNode will be logged by their parent.
if (child !is BaseFlowNode<*>) {
child.lifecycle.logLifecycle(child::class.java.simpleName)
}
}
}
}
@Composable
inline fun <reified NavTarget : Any> BaseFlowNode<NavTarget>.BackstackView(
modifier: Modifier = Modifier,
transitionHandler: TransitionHandler<NavTarget, BackStack.State> = rememberBackstackSlider(
transitionSpec = { spring(stiffness = Spring.StiffnessMediumLow) },
),
) {
Children(
modifier = modifier,
navModel = backstack,
transitionHandler = transitionHandler,
)
}
@Composable
inline fun <reified NavTarget : Any> BaseFlowNode<NavTarget>.OverlayView(
modifier: Modifier = Modifier,
transitionHandler: TransitionHandler<NavTarget, BackStack.State> = rememberBackstackFader(),
) {
Children(
modifier = modifier,
navModel = overlay,
transitionHandler = transitionHandler,
)
}
@Composable
inline fun <reified NavTarget : Any> BaseFlowNode<NavTarget>.BackstackWithOverlayBox(
modifier: Modifier = Modifier,
content: @Composable BoxScope.() -> Unit = {},
) {
Box(modifier = modifier) {
BackstackView()
OverlayView()
content()
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.architecture.overlay
import com.bumble.appyx.core.navigation.backpresshandlerstrategies.BaseBackPressHandlerStrategy
import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.BackStackElements
import io.element.android.libraries.architecture.overlay.operation.Hide
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
class HideOverlayBackPressHandler<NavTarget : Any>
: BaseBackPressHandlerStrategy<NavTarget, BackStack.State>() {
override val canHandleBackPressFlow: Flow<Boolean> by lazy {
navModel.elements.map(::areThereElements)
}
private fun areThereElements(elements: BackStackElements<NavTarget>) =
elements.isNotEmpty()
override fun onBackPressed() {
navModel.accept(Hide())
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.architecture.overlay
import com.bumble.appyx.core.navigation.BaseNavModel
import com.bumble.appyx.core.navigation.NavElements
import com.bumble.appyx.core.navigation.backpresshandlerstrategies.BackPressHandlerStrategy
import com.bumble.appyx.core.navigation.onscreen.OnScreenStateResolver
import com.bumble.appyx.core.navigation.operationstrategies.ExecuteImmediately
import com.bumble.appyx.core.navigation.operationstrategies.OperationStrategy
import com.bumble.appyx.core.state.SavedStateMap
import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.BackStackOnScreenResolver
class Overlay<NavTarget : Any>(
savedStateMap: SavedStateMap?,
key: String = requireNotNull(Overlay::class.qualifiedName),
backPressHandler: BackPressHandlerStrategy<NavTarget, BackStack.State> = HideOverlayBackPressHandler(),
operationStrategy: OperationStrategy<NavTarget, BackStack.State> = ExecuteImmediately(),
screenResolver: OnScreenStateResolver<BackStack.State> = BackStackOnScreenResolver,
) : BaseNavModel<NavTarget, BackStack.State>(
backPressHandler = backPressHandler,
screenResolver = screenResolver,
operationStrategy = operationStrategy,
finalState = BackStack.State.DESTROYED,
savedStateMap = savedStateMap,
key = key,
) {
override val initialElements: NavElements<NavTarget, BackStack.State>
get() = emptyList()
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.architecture.overlay.operation
import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.BackStackElements
import com.bumble.appyx.navmodel.backstack.activeIndex
import io.element.android.libraries.architecture.overlay.Overlay
import kotlinx.parcelize.Parcelize
@Parcelize
class Hide<T : Any> : OverlayOperation<T> {
override fun isApplicable(elements: BackStackElements<T>): Boolean =
elements.any { it.targetState == BackStack.State.ACTIVE }
override fun invoke(
elements: BackStackElements<T>
): BackStackElements<T> {
val hideIndex = elements.activeIndex
require(hideIndex != -1) { "Nothing to hide, state=$elements" }
return elements.mapIndexed { index, element ->
when (index) {
hideIndex -> element.transitionTo(
newTargetState = BackStack.State.DESTROYED,
operation = this
)
else -> element
}
}
}
override fun equals(other: Any?): Boolean = this.javaClass == other?.javaClass
override fun hashCode(): Int = this.javaClass.hashCode()
}
fun <T : Any> Overlay<T>.hide() {
accept(Hide())
}

View file

@ -0,0 +1,22 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.architecture.overlay.operation
import com.bumble.appyx.core.navigation.Operation
import com.bumble.appyx.navmodel.backstack.BackStack
interface OverlayOperation<T> : Operation<T, BackStack.State>

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.architecture.overlay.operation
import com.bumble.appyx.core.navigation.NavKey
import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.BackStackElement
import com.bumble.appyx.navmodel.backstack.BackStackElements
import com.bumble.appyx.navmodel.backstack.activeElement
import io.element.android.libraries.architecture.overlay.Overlay
import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.RawValue
@Parcelize
data class Show<T : Any>(
private val element: @RawValue T
) : OverlayOperation<T> {
override fun isApplicable(elements: BackStackElements<T>): Boolean =
element != elements.activeElement
override fun invoke(elements: BackStackElements<T>): BackStackElements<T> = listOf(
BackStackElement(
key = NavKey(element),
fromState = BackStack.State.CREATED,
targetState = BackStack.State.ACTIVE,
operation = this
)
)
}
fun <T : Any> Overlay<T>.show(element: T) {
accept(Show(element))
}

View file

@ -24,6 +24,7 @@ import com.lemonappdev.konsist.api.ext.list.withAllAnnotationsOf
import com.lemonappdev.konsist.api.ext.list.withTopLevel import com.lemonappdev.konsist.api.ext.list.withTopLevel
import com.lemonappdev.konsist.api.ext.list.withoutName import com.lemonappdev.konsist.api.ext.list.withoutName
import com.lemonappdev.konsist.api.ext.list.withoutNameEndingWith import com.lemonappdev.konsist.api.ext.list.withoutNameEndingWith
import com.lemonappdev.konsist.api.ext.list.withoutReceiverType
import com.lemonappdev.konsist.api.verify.assertTrue import com.lemonappdev.konsist.api.verify.assertTrue
import org.junit.Test import org.junit.Test
@ -37,6 +38,7 @@ class KonsistComposableTest {
.withoutModifier(KoModifier.PRIVATE) .withoutModifier(KoModifier.PRIVATE)
.withoutNameEndingWith("Preview") .withoutNameEndingWith("Preview")
.withAllAnnotationsOf(Composable::class) .withAllAnnotationsOf(Composable::class)
.withoutReceiverType()
.withoutName( .withoutName(
// Add some exceptions... // Add some exceptions...
"OutlinedButton", "OutlinedButton",