[Architecture] Introduce EntryPoint and use createNode for ParentNode too

This commit is contained in:
ganfra
2023-03-02 12:33:18 +01:00
parent 1ec629c304
commit da92113e43
54 changed files with 674 additions and 164 deletions

View File

@@ -26,8 +26,8 @@ import androidx.core.view.WindowCompat
import com.bumble.appyx.core.integration.NodeHost
import com.bumble.appyx.core.integrationpoint.NodeComponentActivity
import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.designsystem.theme.ElementTheme
import io.element.android.libraries.di.DaggerComponentOwner
import io.element.android.x.di.AppBindings
import io.element.android.x.node.RootFlowNode
@@ -45,13 +45,7 @@ class MainActivity : NodeComponentActivity() {
modifier = Modifier.fillMaxSize(),
) {
NodeHost(integrationPoint = appyxIntegrationPoint) {
RootFlowNode(
buildContext = it,
appComponentOwner = applicationContext as DaggerComponentOwner,
authenticationService = appBindings.authenticationService(),
presenter = appBindings.rootPresenter(),
matrixClientsHolder = appBindings.matrixClientsHolder()
)
createNode<RootFlowNode>(it)
}
}
}

View File

@@ -29,38 +29,69 @@ import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.node.ParentNode
import com.bumble.appyx.core.node.node
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.push
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.preferences.PreferencesFlowNode
import io.element.android.features.roomlist.RoomListNode
import io.element.android.features.roomlist.api.RoomListEntryPoint
import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.architecture.nodeInputs
import io.element.android.libraries.architecture.nodeInputsProvider
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.DaggerComponentOwner
import io.element.android.libraries.matrix.MatrixClient
import io.element.android.libraries.matrix.core.RoomId
import io.element.android.libraries.matrix.core.SessionId
import io.element.android.libraries.matrix.ui.di.MatrixUIBindings
import io.element.android.x.di.SessionComponent
import kotlinx.parcelize.Parcelize
@ContributesNode(AppScope::class)
class LoggedInFlowNode(
buildContext: BuildContext,
val sessionId: SessionId,
private val matrixClient: MatrixClient,
private val onOpenBugReport: () -> Unit,
private val backstack: BackStack<NavTarget> = BackStack(
initialElement = NavTarget.RoomList,
savedStateMap = buildContext.savedStateMap,
),
plugins: List<Plugin>,
private val backstack: BackStack<NavTarget>,
private val roomListEntryPoint: RoomListEntryPoint,
) : ParentNode<LoggedInFlowNode.NavTarget>(
navModel = backstack,
buildContext = buildContext
buildContext = buildContext,
plugins = plugins
), DaggerComponentOwner {
@AssistedInject
constructor(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
roomListEntryPoint: RoomListEntryPoint,
) : this(
buildContext = buildContext,
plugins = plugins,
roomListEntryPoint = roomListEntryPoint,
backstack = BackStack(
initialElement = NavTarget.RoomList,
savedStateMap = buildContext.savedStateMap,
)
)
interface Callback : Plugin {
fun onOpenBugReport()
}
data class Inputs(
val matrixClient: MatrixClient
) : NodeInputs
private val inputs: Inputs by nodeInputs()
override val daggerComponent: Any by lazy {
parent!!.bindings<SessionComponent.ParentBindings>().sessionComponentBuilder().client(matrixClient).build()
parent!!.bindings<SessionComponent.ParentBindings>().sessionComponentBuilder().client(inputs.matrixClient).build()
}
override fun onBuilt() {
@@ -69,7 +100,7 @@ class LoggedInFlowNode(
onCreate = {
val imageLoaderFactory = bindings<MatrixUIBindings>().loggedInImageLoaderFactory()
Coil.setImageLoader(imageLoaderFactory)
matrixClient.startSync()
inputs.matrixClient.startSync()
},
onDestroy = {
val imageLoaderFactory = bindings<MatrixUIBindings>().notLoggedInImageLoaderFactory()
@@ -78,16 +109,6 @@ class LoggedInFlowNode(
)
}
private val roomListCallback = object : RoomListNode.Callback {
override fun onRoomClicked(roomId: RoomId) {
backstack.push(NavTarget.Room(roomId))
}
override fun onSettingsClicked() {
backstack.push(NavTarget.Settings)
}
}
sealed interface NavTarget : Parcelable {
@Parcelize
object RoomList : NavTarget
@@ -102,10 +123,19 @@ class LoggedInFlowNode(
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
return when (navTarget) {
NavTarget.RoomList -> {
createNode<RoomListNode>(buildContext, plugins = listOf(roomListCallback))
val callback = object : RoomListEntryPoint.Callback {
override fun onRoomClicked(roomId: RoomId) {
backstack.push(NavTarget.Room(roomId))
}
override fun onSettingsClicked() {
backstack.push(NavTarget.Settings)
}
}
roomListEntryPoint.node(this, buildContext, plugins = listOf(callback))
}
is NavTarget.Room -> {
val room = matrixClient.getRoom(roomId = navTarget.roomId)
val room = inputs.matrixClient.getRoom(roomId = navTarget.roomId)
if (room == null) {
// TODO CREATE UNKNOWN ROOM NODE
node(buildContext) {
@@ -114,11 +144,17 @@ class LoggedInFlowNode(
}
}
} else {
RoomFlowNode(buildContext, room)
val inputsProvider = nodeInputsProvider(RoomFlowNode.Inputs(room))
createNode<RoomFlowNode>(buildContext, plugins = listOf(inputsProvider))
}
}
NavTarget.Settings -> {
PreferencesFlowNode(buildContext, onOpenBugReport)
val callback = object : PreferencesFlowNode.Callback {
override fun onOpenBugReport() {
plugins<Callback>().forEach { it.onOpenBugReport() }
}
}
createNode<PreferencesFlowNode>(buildContext, plugins = listOf(callback))
}
}
}

View File

@@ -24,25 +24,45 @@ import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.node.ParentNode
import com.bumble.appyx.core.node.node
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.push
import io.element.android.features.login.LoginFlowNode
import io.element.android.features.onboarding.OnBoardingScreen
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.login.api.LoginEntryPoint
import io.element.android.features.onboarding.api.OnBoardingEntryPoint
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
import io.element.android.libraries.di.AppScope
import kotlinx.parcelize.Parcelize
import timber.log.Timber
class NotLoggedInFlowNode(
@ContributesNode(AppScope::class)
class NotLoggedInFlowNode private constructor(
buildContext: BuildContext,
private val backstack: BackStack<NavTarget> = BackStack(
initialElement = NavTarget.OnBoarding,
savedStateMap = buildContext.savedStateMap,
),
plugins: List<Plugin>,
private val onBoardingEntryPoint: OnBoardingEntryPoint,
private val loginEntryPoint: LoginEntryPoint,
private val backstack: BackStack<NavTarget>,
) : ParentNode<NotLoggedInFlowNode.NavTarget>(
navModel = backstack,
buildContext = buildContext
buildContext = buildContext,
plugins = plugins,
) {
@AssistedInject
constructor(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
onBoardingEntryPoint: OnBoardingEntryPoint,
loginEntryPoint: LoginEntryPoint,
)
: this(
buildContext = buildContext,
plugins = plugins,
onBoardingEntryPoint = onBoardingEntryPoint,
loginEntryPoint = loginEntryPoint,
backstack = BackStack(initialElement = NavTarget.OnBoarding, savedStateMap = buildContext.savedStateMap),
)
init {
lifecycle.subscribe(
@@ -61,12 +81,21 @@ class NotLoggedInFlowNode(
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
return when (navTarget) {
NavTarget.OnBoarding -> node(buildContext) {
OnBoardingScreen(
onSignIn = { backstack.push(NavTarget.LoginFlow) }
)
NavTarget.OnBoarding -> {
val callback = object : OnBoardingEntryPoint.Callback {
override fun onSignUp() {
//NOOP
}
override fun onSignIn() {
backstack.push(NavTarget.LoginFlow)
}
}
onBoardingEntryPoint.node(this, buildContext, plugins = listOf(callback))
}
NavTarget.LoginFlow -> {
loginEntryPoint.node(this, buildContext)
}
NavTarget.LoginFlow -> LoginFlowNode(buildContext)
}
}

View File

@@ -24,30 +24,52 @@ import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.node.ParentNode
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.navmodel.backstack.BackStack
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.messages.MessagesNode
import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.architecture.nodeInputs
import io.element.android.libraries.di.DaggerComponentOwner
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.room.MatrixRoom
import io.element.android.x.di.RoomComponent
import kotlinx.parcelize.Parcelize
import timber.log.Timber
@ContributesNode(SessionScope::class)
class RoomFlowNode(
buildContext: BuildContext,
private val room: MatrixRoom,
private val backstack: BackStack<NavTarget> = BackStack(
initialElement = NavTarget.Messages,
savedStateMap = buildContext.savedStateMap,
),
plugins: List<Plugin>,
private val backstack: BackStack<NavTarget>,
) : ParentNode<RoomFlowNode.NavTarget>(
navModel = backstack,
buildContext = buildContext
buildContext = buildContext,
plugins = plugins,
), DaggerComponentOwner {
data class Inputs(
val room: MatrixRoom,
) : NodeInputs
@AssistedInject
constructor(@Assisted buildContext: BuildContext, @Assisted plugins: List<Plugin>) : this(
buildContext = buildContext,
plugins = plugins,
backstack = BackStack(
initialElement = NavTarget.Messages,
savedStateMap = buildContext.savedStateMap,
),
)
private val inputs: Inputs by nodeInputs()
override val daggerComponent: Any by lazy {
parent!!.bindings<RoomComponent.ParentBindings>().roomComponentBuilder().room(room).build()
parent!!.bindings<RoomComponent.ParentBindings>().roomComponentBuilder().room(inputs.room).build()
}
init {

View File

@@ -16,6 +16,7 @@
package io.element.android.x.node
import android.content.Context
import android.os.Parcelable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
@@ -28,14 +29,21 @@ import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.node.ParentNode
import com.bumble.appyx.core.node.node
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.newRoot
import com.bumble.appyx.navmodel.backstack.operation.pop
import com.bumble.appyx.navmodel.backstack.operation.push
import io.element.android.features.rageshake.bugreport.BugReportNode
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.rageshake.bugreport.BugReportEntryPoint
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.architecture.nodeInputsProvider
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.DaggerComponentOwner
import io.element.android.libraries.matrix.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.core.SessionId
@@ -48,7 +56,8 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.parcelize.Parcelize
import timber.log.Timber
class RootFlowNode(
@ContributesNode(AppScope::class)
class RootFlowNode private constructor(
private val buildContext: BuildContext,
private val backstack: BackStack<NavTarget> = BackStack(
initialElement = NavTarget.SplashScreen,
@@ -57,7 +66,8 @@ class RootFlowNode(
private val appComponentOwner: DaggerComponentOwner,
private val authenticationService: MatrixAuthenticationService,
private val matrixClientsHolder: MatrixClientsHolder,
private val presenter: RootPresenter
private val presenter: RootPresenter,
private val bugReportEntryPoint: BugReportEntryPoint,
) :
ParentNode<RootFlowNode.NavTarget>(
navModel = backstack,
@@ -66,6 +76,28 @@ class RootFlowNode(
DaggerComponentOwner by appComponentOwner {
@AssistedInject
constructor(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
@ApplicationContext context: Context,
authenticationService: MatrixAuthenticationService,
matrixClientsHolder: MatrixClientsHolder,
presenter: RootPresenter,
bugReportEntryPoint: BugReportEntryPoint,
) : this(
buildContext = buildContext,
backstack = BackStack(
initialElement = NavTarget.SplashScreen,
savedStateMap = buildContext.savedStateMap,
),
appComponentOwner = context.applicationContext as DaggerComponentOwner,
authenticationService = authenticationService,
matrixClientsHolder = matrixClientsHolder,
presenter = presenter,
bugReportEntryPoint = bugReportEntryPoint,
)
override fun onBuilt() {
super.onBuilt()
observeLoggedInState()
@@ -140,12 +172,6 @@ class RootFlowNode(
}
}
private val bugReportNodeCallback = object : BugReportNode.Callback {
override fun onBugReportSent() {
backstack.pop()
}
}
sealed interface NavTarget : Parcelable {
@Parcelize
object SplashScreen : NavTarget
@@ -167,16 +193,24 @@ class RootFlowNode(
Timber.w("Couldn't find any session, go through SplashScreen")
backstack.newRoot(NavTarget.SplashScreen)
}
LoggedInFlowNode(
buildContext = buildContext,
sessionId = navTarget.sessionId,
matrixClient = matrixClient,
onOpenBugReport = this::onOpenBugReport
)
val inputsProvider = nodeInputsProvider(LoggedInFlowNode.Inputs(matrixClient))
val callback = object : LoggedInFlowNode.Callback {
override fun onOpenBugReport() {
backstack.push(NavTarget.BugReport)
}
}
createNode<LoggedInFlowNode>(buildContext, plugins = listOf(inputsProvider, callback))
}
NavTarget.NotLoggedInFlow -> NotLoggedInFlowNode(buildContext)
NavTarget.NotLoggedInFlow -> createNode<NotLoggedInFlowNode>(buildContext)
NavTarget.SplashScreen -> splashNode(buildContext)
NavTarget.BugReport -> createNode<BugReportNode>(buildContext, plugins = listOf(bugReportNodeCallback))
NavTarget.BugReport -> {
val callback = object : BugReportEntryPoint.Callback {
override fun onBugReportSent() {
backstack.pop()
}
}
bugReportEntryPoint.node(this, buildContext, plugins = listOf(callback))
}
}
}

View File

@@ -0,0 +1,21 @@
/*
* 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.features.login.api
import io.element.android.libraries.architecture.EntryPoint
interface LoginEntryPoint : EntryPoint

View File

@@ -0,0 +1,33 @@
/*
* 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.features.login.implementation
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.login.api.LoginEntryPoint
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.di.AppScope
import javax.inject.Inject
@ContributesBinding(AppScope::class)
class DefaultLoginEntryPoint @Inject constructor() : LoginEntryPoint {
override fun node(parentNode: Node, buildContext: BuildContext, plugins: List<Plugin>): Node {
return parentNode.createNode<LoginFlowNode>(buildContext, plugins)
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.login
package io.element.android.features.login.implementation
import android.os.Parcelable
import androidx.compose.runtime.Composable
@@ -23,30 +23,39 @@ import com.bumble.appyx.core.composable.Children
import com.bumble.appyx.core.modality.BuildContext
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.operation.push
import io.element.android.features.login.changeserver.ChangeServerNode
import io.element.android.features.login.root.LoginRootNode
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.login.implementation.changeserver.ChangeServerNode
import io.element.android.features.login.implementation.root.LoginRootNode
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.di.AppScope
import kotlinx.parcelize.Parcelize
@ContributesNode(AppScope::class)
class LoginFlowNode(
buildContext: BuildContext,
private val backstack: BackStack<NavTarget> = BackStack(
initialElement = NavTarget.Root,
savedStateMap = buildContext.savedStateMap,
),
plugins: List<Plugin>,
private val backstack: BackStack<NavTarget>,
) : ParentNode<LoginFlowNode.NavTarget>(
navModel = backstack,
buildContext = buildContext
buildContext = buildContext,
plugins = plugins,
) {
private val loginRootCallback = object : LoginRootNode.Callback {
override fun onChangeHomeServer() {
backstack.push(NavTarget.ChangeServer)
}
}
@AssistedInject
constructor(@Assisted buildContext: BuildContext, @Assisted plugins: List<Plugin>) : this(
buildContext = buildContext,
plugins = plugins,
backstack = BackStack(
initialElement = NavTarget.Root,
savedStateMap = buildContext.savedStateMap,
),
)
sealed interface NavTarget : Parcelable {
@Parcelize
@@ -58,7 +67,14 @@ class LoginFlowNode(
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
return when (navTarget) {
NavTarget.Root -> createNode<LoginRootNode>(buildContext, plugins = listOf(loginRootCallback))
NavTarget.Root -> {
val callback = object : LoginRootNode.Callback {
override fun onChangeHomeServer() {
backstack.push(NavTarget.ChangeServer)
}
}
createNode<LoginRootNode>(buildContext, plugins = listOf(callback))
}
NavTarget.ChangeServer -> createNode<ChangeServerNode>(buildContext)
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.login.changeserver
package io.element.android.features.login.implementation.changeserver
sealed interface ChangeServerEvents {
data class SetServer(val server: String) : ChangeServerEvents

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.login.changeserver
package io.element.android.features.login.implementation.changeserver
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.login.changeserver
package io.element.android.features.login.implementation.changeserver
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.login.changeserver
package io.element.android.features.login.implementation.changeserver
import io.element.android.libraries.architecture.Async

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.login.changeserver
package io.element.android.features.login.implementation.changeserver
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.architecture.Async

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.login.changeserver
package io.element.android.features.login.implementation.changeserver
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
@@ -46,7 +46,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.features.login.R
import io.element.android.features.login.error.changeServerError
import io.element.android.features.login.implementation.error.changeServerError
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.components.form.textFieldState
import io.element.android.libraries.designsystem.preview.ElementPreviewDark

View File

@@ -14,11 +14,11 @@
* limitations under the License.
*/
package io.element.android.features.login.error
package io.element.android.features.login.implementation.error
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import io.element.android.features.login.root.LoginFormState
import io.element.android.features.login.implementation.root.LoginFormState
import io.element.android.libraries.core.uri.isValidUrl
import io.element.android.libraries.ui.strings.R as StringR

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.login.root
package io.element.android.features.login.implementation.root
sealed interface LoginRootEvents {
object RefreshHomeServer : LoginRootEvents

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.login.root
package io.element.android.features.login.implementation.root
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.login.root
package io.element.android.features.login.implementation.root
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.login.root
package io.element.android.features.login.implementation.root
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -48,7 +48,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.features.login.error.loginError
import io.element.android.features.login.implementation.error.loginError
import io.element.android.libraries.designsystem.components.form.textFieldState
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.login.root
package io.element.android.features.login.implementation.root
import android.os.Parcelable
import io.element.android.libraries.matrix.core.SessionId

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.login.root
package io.element.android.features.login.implementation.root
fun aLoginRootState() = LoginRootState(
homeserver = "",

View File

@@ -22,6 +22,8 @@ import app.cash.molecule.RecompositionClock
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.login.implementation.changeserver.ChangeServerEvents
import io.element.android.features.login.implementation.changeserver.ChangeServerPresenter
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.matrixtest.A_HOMESERVER
import io.element.android.libraries.matrixtest.auth.FakeAuthenticationService

View File

@@ -22,6 +22,10 @@ import app.cash.molecule.RecompositionClock
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.login.implementation.root.LoggedInState
import io.element.android.features.login.implementation.root.LoginFormState
import io.element.android.features.login.implementation.root.LoginRootEvents
import io.element.android.features.login.implementation.root.LoginRootPresenter
import io.element.android.libraries.matrix.core.SessionId
import io.element.android.libraries.matrixtest.A_HOMESERVER
import io.element.android.libraries.matrixtest.A_HOMESERVER_2

View File

@@ -19,15 +19,23 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.ksp)
alias(libs.plugins.anvil)
}
android {
namespace = "io.element.android.features.onboarding"
}
anvil {
generateDaggerFactories.set(true)
}
dependencies {
implementation(projects.libraries.core)
implementation(projects.libraries.elementresources)
implementation(projects.anvilannotations)
anvil(projects.anvilcodegen)
implementation(projects.libraries.uiStrings)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.architecture)

View File

@@ -0,0 +1,27 @@
/*
* 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.features.onboarding.api
import com.bumble.appyx.core.plugin.Plugin
import io.element.android.libraries.architecture.EntryPoint
interface OnBoardingEntryPoint : EntryPoint {
interface Callback : Plugin {
fun onSignUp()
fun onSignIn()
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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.features.onboarding.implementation
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.onboarding.api.OnBoardingEntryPoint
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.di.AppScope
import javax.inject.Inject
@ContributesBinding(AppScope::class)
class DefaultOnBoardingEntryPoint @Inject constructor() : OnBoardingEntryPoint {
override fun node(parentNode: Node, buildContext: BuildContext, plugins: List<Plugin>): Node {
return parentNode.createNode<OnBoardingNode>(buildContext, plugins)
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.features.onboarding.implementation
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.onboarding.api.OnBoardingEntryPoint
import io.element.android.libraries.di.AppScope
@ContributesNode(AppScope::class)
class OnBoardingNode @AssistedInject constructor(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
) : Node(
buildContext = buildContext,
plugins = plugins
) {
private fun onSignIn() {
plugins<OnBoardingEntryPoint.Callback>().forEach { it.onSignIn() }
}
private fun onSignUp() {
plugins<OnBoardingEntryPoint.Callback>().forEach { it.onSignUp() }
}
@Composable
override fun View(modifier: Modifier) {
OnBoardingScreen(
modifier = modifier,
onSignIn = this::onSignIn,
onSignUp = this::onSignUp
)
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.onboarding
package io.element.android.features.onboarding.implementation
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.onboarding
package io.element.android.features.onboarding.implementation
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* 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.
@@ -14,9 +14,10 @@
* limitations under the License.
*/
package io.element.android.features.onboarding
package io.element.android.features.onboarding.implementation
import androidx.annotation.DrawableRes
import io.element.android.features.onboarding.R
import io.element.android.libraries.ui.strings.R as StringR
class SplashCarouselDataFactory {

View File

@@ -23,27 +23,46 @@ import com.bumble.appyx.core.composable.Children
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.node.ParentNode
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.preferences.root.PreferencesRootNode
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.di.SessionScope
import kotlinx.parcelize.Parcelize
@ContributesNode(SessionScope::class)
class PreferencesFlowNode(
buildContext: BuildContext,
private val onOpenBugReport: () -> Unit,
private val backstack: BackStack<NavTarget> = BackStack(
initialElement = NavTarget.Root,
savedStateMap = buildContext.savedStateMap,
),
plugins: List<Plugin>,
private val backstack: BackStack<NavTarget>,
) : ParentNode<PreferencesFlowNode.NavTarget>(
navModel = backstack,
buildContext = buildContext
buildContext = buildContext,
plugins = plugins
) {
@AssistedInject
constructor(@Assisted buildContext: BuildContext, @Assisted plugins: List<Plugin>) : this(
buildContext = buildContext,
plugins = plugins,
backstack = BackStack(
initialElement = NavTarget.Root,
savedStateMap = buildContext.savedStateMap,
)
)
interface Callback : Plugin {
fun onOpenBugReport()
}
private val preferencesRootNodeCallback = object : PreferencesRootNode.Callback {
override fun onOpenBugReport() {
onOpenBugReport.invoke()
plugins<Callback>().forEach { it.onOpenBugReport() }
}
}

View File

@@ -0,0 +1,26 @@
/*
* 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.features.rageshake.bugreport
import com.bumble.appyx.core.plugin.Plugin
import io.element.android.libraries.architecture.EntryPoint
interface BugReportEntryPoint : EntryPoint {
interface Callback : Plugin {
fun onBugReportSent()
}
}

View File

@@ -34,10 +34,6 @@ class BugReportNode @AssistedInject constructor(
private val presenter: BugReportPresenter,
) : Node(buildContext, plugins = plugins) {
interface Callback : Plugin {
fun onBugReportSent()
}
@Composable
override fun View(modifier: Modifier) {
val state = presenter.present()
@@ -49,6 +45,6 @@ class BugReportNode @AssistedInject constructor(
}
private fun onDone() {
plugins<Callback>().forEach { it.onBugReportSent() }
plugins<BugReportEntryPoint.Callback>().forEach { it.onBugReportSent() }
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.features.rageshake.bugreport
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.di.AppScope
import javax.inject.Inject
@ContributesBinding(AppScope::class)
class DefaultBugReportEntryPoint @Inject constructor() : BugReportEntryPoint {
override fun node(parentNode: Node, buildContext: BuildContext, plugins: List<Plugin>): Node {
return parentNode.createNode<BugReportNode>(buildContext, plugins)
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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.features.roomlist.api
import com.bumble.appyx.core.plugin.Plugin
import io.element.android.libraries.architecture.EntryPoint
import io.element.android.libraries.matrix.core.RoomId
interface RoomListEntryPoint : EntryPoint {
interface Callback : Plugin {
fun onRoomClicked(roomId: RoomId)
fun onSettingsClicked()
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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.features.roomlist.impl
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.roomlist.api.RoomListEntryPoint
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.di.AppScope
import javax.inject.Inject
@ContributesBinding(AppScope::class)
class DefaultRoomListEntryPoint @Inject constructor() : RoomListEntryPoint {
override fun node(parentNode: Node, buildContext: BuildContext, plugins: List<Plugin>): Node {
return parentNode.createNode<RoomListNode>(buildContext, plugins)
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.roomlist.model
package io.element.android.features.roomlist.impl
sealed interface RoomListEvents {
data class UpdateFilter(val newFilter: String) : RoomListEvents

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.roomlist
package io.element.android.features.roomlist.impl
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -25,6 +25,7 @@ import com.bumble.appyx.core.plugin.plugins
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.roomlist.api.RoomListEntryPoint
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.core.RoomId
@@ -35,17 +36,12 @@ class RoomListNode @AssistedInject constructor(
private val presenter: RoomListPresenter,
) : Node(buildContext, plugins = plugins) {
interface Callback : Plugin {
fun onRoomClicked(roomId: RoomId)
fun onSettingsClicked()
}
private fun onRoomClicked(roomId: RoomId) {
plugins<Callback>().forEach { it.onRoomClicked(roomId) }
plugins<RoomListEntryPoint.Callback>().forEach { it.onRoomClicked(roomId) }
}
private fun onOpenSettings() {
plugins<Callback>().forEach { it.onSettingsClicked() }
plugins<RoomListEntryPoint.Callback>().forEach { it.onSettingsClicked() }
}
@Composable

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.roomlist
package io.element.android.features.roomlist.impl
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -25,10 +25,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import io.element.android.features.roomlist.model.RoomListEvents
import io.element.android.features.roomlist.model.RoomListRoomSummary
import io.element.android.features.roomlist.model.RoomListRoomSummaryPlaceholders
import io.element.android.features.roomlist.model.RoomListState
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.coroutine.parallelMap
import io.element.android.libraries.dateformatter.LastMessageFormatter

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.roomlist.model
package io.element.android.features.roomlist.impl
import androidx.compose.runtime.Immutable
import io.element.android.libraries.designsystem.components.avatar.AvatarData

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.roomlist.model
package io.element.android.features.roomlist.impl
import io.element.android.libraries.designsystem.components.avatar.AvatarData

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.roomlist.model
package io.element.android.features.roomlist.impl
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.designsystem.components.avatar.AvatarData

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.roomlist.model
package io.element.android.features.roomlist.impl
import androidx.compose.runtime.Immutable
import io.element.android.libraries.matrix.ui.model.MatrixUser

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.roomlist.model
package io.element.android.features.roomlist.impl
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.designsystem.components.avatar.AvatarData

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.roomlist
package io.element.android.features.roomlist.impl
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
@@ -34,12 +34,8 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.Velocity
import io.element.android.features.roomlist.components.RoomListTopBar
import io.element.android.features.roomlist.components.RoomSummaryRow
import io.element.android.features.roomlist.model.RoomListEvents
import io.element.android.features.roomlist.model.RoomListRoomSummary
import io.element.android.features.roomlist.model.RoomListState
import io.element.android.features.roomlist.model.RoomListStateProvider
import io.element.android.features.roomlist.impl.components.RoomListTopBar
import io.element.android.features.roomlist.impl.components.RoomSummaryRow
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Scaffold

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* 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.
@@ -16,7 +16,7 @@
@file:OptIn(ExperimentalMaterial3Api::class)
package io.element.android.features.roomlist.components
package io.element.android.features.roomlist.impl.components
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.fillMaxWidth

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.roomlist.components
package io.element.android.features.roomlist.impl.components
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
@@ -53,8 +53,8 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.google.accompanist.placeholder.material.placeholder
import io.element.android.features.roomlist.model.RoomListRoomSummary
import io.element.android.features.roomlist.model.RoomListRoomSummaryProvider
import io.element.android.features.roomlist.impl.RoomListRoomSummary
import io.element.android.features.roomlist.impl.RoomListRoomSummaryProvider
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight

View File

@@ -22,8 +22,9 @@ import app.cash.molecule.RecompositionClock
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.roomlist.model.RoomListEvents
import io.element.android.features.roomlist.model.RoomListRoomSummary
import io.element.android.features.roomlist.impl.RoomListPresenter
import io.element.android.features.roomlist.impl.RoomListEvents
import io.element.android.features.roomlist.impl.RoomListRoomSummary
import io.element.android.libraries.dateformatter.LastMessageFormatter
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.matrix.core.SessionId

View File

@@ -0,0 +1,25 @@
/*
* 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 com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
interface EntryPoint {
fun node(parentNode: Node, buildContext: BuildContext, plugins: List<Plugin> = emptyList()): Node
}

View File

@@ -16,14 +16,24 @@
package io.element.android.libraries.architecture
import android.content.Context
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
inline fun <reified NODE : Node> Node.createNode(context: BuildContext, plugins: List<Plugin> = emptyList()): NODE {
val nodeClass = NODE::class.java
val bindings: NodeFactoriesBindings = bindings()
val nodeFactoryMap = bindings.nodeFactories()
return bindings.createNode(context, plugins)
}
inline fun <reified NODE : Node> Context.createNode(context: BuildContext, plugins: List<Plugin> = emptyList()): NODE {
val bindings: NodeFactoriesBindings = bindings()
return bindings.createNode(context, plugins)
}
inline fun <reified NODE : Node> NodeFactoriesBindings.createNode(context: BuildContext, plugins: List<Plugin> = emptyList()): NODE {
val nodeClass = NODE::class.java
val nodeFactoryMap = nodeFactories()
val nodeFactory = nodeFactoryMap[nodeClass] ?: error("Cannot find NodeFactory for ${nodeClass.name}.")
@Suppress("UNCHECKED_CAST")

View File

@@ -0,0 +1,36 @@
/*
* 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 com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
import kotlin.properties.ReadOnlyProperty
interface NodeInputs
interface NodeInputsProvider<I : NodeInputs> : Plugin {
fun inputs(): I
}
inline fun <reified I : NodeInputs> nodeInputsProvider(inputs: I) = object : NodeInputsProvider<I> {
override fun inputs() = inputs
}
fun <I : NodeInputs> nodeInputs() = ReadOnlyProperty<Node, I> { thisRef, _ ->
thisRef.plugins<NodeInputsProvider<I>>().first().inputs()
}

View File

@@ -57,7 +57,7 @@ fun ElementTheme(
) {
val systemUiController = rememberSystemUiController()
val useDarkIcons = !darkTheme
val currentColor = remember { if (darkTheme) darkColors else lightColors }
val currentColor = remember(darkTheme) { if (darkTheme) darkColors else lightColors }
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current

View File

@@ -19,8 +19,8 @@ package io.element.android.samples.minimal
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import io.element.android.features.login.root.LoginRootPresenter
import io.element.android.features.login.root.LoginRootScreen
import io.element.android.features.login.implementation.root.LoginRootPresenter
import io.element.android.features.login.implementation.root.LoginRootScreen
import io.element.android.libraries.matrix.auth.MatrixAuthenticationService
class LoginScreen(private val authenticationService: MatrixAuthenticationService) {

View File

@@ -19,8 +19,8 @@ package io.element.android.samples.minimal
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.Modifier
import io.element.android.features.roomlist.RoomListPresenter
import io.element.android.features.roomlist.RoomListView
import io.element.android.features.roomlist.impl.RoomListPresenter
import io.element.android.features.roomlist.impl.RoomListView
import io.element.android.libraries.dateformatter.impl.DateFormatters
import io.element.android.libraries.dateformatter.impl.DefaultLastMessageFormatter
import io.element.android.libraries.dateformatter.impl.LocalDateTimeProvider