Merge pull request #173 from vector-im/feature/fga/improve_node_architecture
Feature/fga/improve node architecture
This commit is contained in:
commit
08f5f6edaa
557 changed files with 2189 additions and 1743 deletions
|
|
@ -17,7 +17,7 @@
|
||||||
@file:Suppress("UnstableApiUsage")
|
@file:Suppress("UnstableApiUsage")
|
||||||
|
|
||||||
import com.android.build.api.variant.FilterConfiguration.FilterType.ABI
|
import com.android.build.api.variant.FilterConfiguration.FilterType.ABI
|
||||||
import extension.allFeatures
|
import extension.allFeaturesImpl
|
||||||
import extension.allLibraries
|
import extension.allLibraries
|
||||||
|
|
||||||
// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed
|
// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed
|
||||||
|
|
@ -26,8 +26,8 @@ plugins {
|
||||||
id("io.element.android-compose-application")
|
id("io.element.android-compose-application")
|
||||||
alias(libs.plugins.stem)
|
alias(libs.plugins.stem)
|
||||||
alias(libs.plugins.kotlin.android)
|
alias(libs.plugins.kotlin.android)
|
||||||
alias(libs.plugins.ksp)
|
|
||||||
alias(libs.plugins.anvil)
|
alias(libs.plugins.anvil)
|
||||||
|
alias(libs.plugins.ksp)
|
||||||
alias(libs.plugins.kapt)
|
alias(libs.plugins.kapt)
|
||||||
id("com.google.firebase.appdistribution") version "4.0.0"
|
id("com.google.firebase.appdistribution") version "4.0.0"
|
||||||
id("org.jetbrains.kotlinx.knit") version "0.4.0"
|
id("org.jetbrains.kotlinx.knit") version "0.4.0"
|
||||||
|
|
@ -201,12 +201,13 @@ knit {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
allLibraries()
|
allLibraries()
|
||||||
allFeatures()
|
allFeaturesImpl()
|
||||||
implementation(projects.libraries.matrix.impl)
|
implementation(projects.libraries.matrix.impl)
|
||||||
implementation(projects.libraries.dateformatter.impl)
|
implementation(projects.libraries.dateformatter.impl)
|
||||||
implementation(projects.libraries.sessionStorage.impl)
|
implementation(projects.libraries.sessionStorage.impl)
|
||||||
implementation(projects.tests.uitests)
|
implementation(projects.tests.uitests)
|
||||||
implementation(projects.anvilannotations)
|
implementation(projects.anvilannotations)
|
||||||
|
implementation(projects.appnav)
|
||||||
anvil(projects.anvilcodegen)
|
anvil(projects.anvilcodegen)
|
||||||
|
|
||||||
// https://developer.android.com/studio/write/java8-support#library-desugaring-versions
|
// https://developer.android.com/studio/write/java8-support#library-desugaring-versions
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ package io.element.android.x
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.startup.AppInitializer
|
import androidx.startup.AppInitializer
|
||||||
import io.element.android.libraries.di.DaggerComponentOwner
|
|
||||||
import io.element.android.x.di.AppComponent
|
import io.element.android.x.di.AppComponent
|
||||||
import io.element.android.x.di.DaggerAppComponent
|
import io.element.android.x.di.DaggerAppComponent
|
||||||
|
import io.element.android.libraries.di.DaggerComponentOwner
|
||||||
import io.element.android.x.info.logApplicationInfo
|
import io.element.android.x.info.logApplicationInfo
|
||||||
import io.element.android.x.initializer.CrashInitializer
|
import io.element.android.x.initializer.CrashInitializer
|
||||||
import io.element.android.x.initializer.MatrixInitializer
|
import io.element.android.x.initializer.MatrixInitializer
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,7 @@ import com.bumble.appyx.core.integration.NodeHost
|
||||||
import com.bumble.appyx.core.integrationpoint.NodeComponentActivity
|
import com.bumble.appyx.core.integrationpoint.NodeComponentActivity
|
||||||
import io.element.android.libraries.architecture.bindings
|
import io.element.android.libraries.architecture.bindings
|
||||||
import io.element.android.libraries.designsystem.theme.ElementTheme
|
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.di.AppBindings
|
||||||
import io.element.android.x.node.RootFlowNode
|
|
||||||
|
|
||||||
class MainActivity : NodeComponentActivity() {
|
class MainActivity : NodeComponentActivity() {
|
||||||
|
|
||||||
|
|
@ -47,13 +45,7 @@ class MainActivity : NodeComponentActivity() {
|
||||||
modifier = Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background),
|
modifier = Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background),
|
||||||
) {
|
) {
|
||||||
NodeHost(integrationPoint = appyxIntegrationPoint) {
|
NodeHost(integrationPoint = appyxIntegrationPoint) {
|
||||||
RootFlowNode(
|
MainNode(it, appBindings.mainDaggerComponentOwner())
|
||||||
buildContext = it,
|
|
||||||
appComponentOwner = applicationContext as DaggerComponentOwner,
|
|
||||||
authenticationService = appBindings.authenticationService(),
|
|
||||||
presenter = appBindings.rootPresenter(),
|
|
||||||
matrixClientsHolder = appBindings.matrixClientsHolder()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
86
app/src/main/kotlin/io/element/android/x/MainNode.kt
Normal file
86
app/src/main/kotlin/io/element/android/x/MainNode.kt
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* 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.x
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import com.bumble.appyx.core.composable.Children
|
||||||
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel
|
||||||
|
import com.bumble.appyx.core.node.Node
|
||||||
|
import com.bumble.appyx.core.node.ParentNode
|
||||||
|
import io.element.android.appnav.LoggedInFlowNode
|
||||||
|
import io.element.android.appnav.RoomFlowNode
|
||||||
|
import io.element.android.appnav.RootFlowNode
|
||||||
|
import io.element.android.libraries.architecture.bindings
|
||||||
|
import io.element.android.libraries.architecture.createNode
|
||||||
|
import io.element.android.libraries.di.DaggerComponentOwner
|
||||||
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
|
import io.element.android.x.di.MainDaggerComponentsOwner
|
||||||
|
import io.element.android.x.di.RoomComponent
|
||||||
|
import io.element.android.x.di.SessionComponent
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
class MainNode(
|
||||||
|
buildContext: BuildContext,
|
||||||
|
private val mainDaggerComponentOwner: MainDaggerComponentsOwner,
|
||||||
|
) :
|
||||||
|
ParentNode<MainNode.RootNavTarget>(
|
||||||
|
navModel = PermanentNavModel(
|
||||||
|
navTargets = setOf(RootNavTarget),
|
||||||
|
savedStateMap = buildContext.savedStateMap,
|
||||||
|
),
|
||||||
|
buildContext = buildContext,
|
||||||
|
),
|
||||||
|
DaggerComponentOwner by mainDaggerComponentOwner {
|
||||||
|
|
||||||
|
private val loggedInFlowNodeCallback = object : LoggedInFlowNode.LifecycleCallback {
|
||||||
|
override fun onFlowCreated(client: MatrixClient) {
|
||||||
|
val component = bindings<SessionComponent.ParentBindings>().sessionComponentBuilder().client(client).build()
|
||||||
|
mainDaggerComponentOwner.addComponent(client.sessionId.value, component)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFlowReleased(client: MatrixClient) {
|
||||||
|
mainDaggerComponentOwner.removeComponent(client.sessionId.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val roomFlowNodeCallback = object : RoomFlowNode.LifecycleCallback {
|
||||||
|
override fun onFlowCreated(room: MatrixRoom) {
|
||||||
|
val component = bindings<RoomComponent.ParentBindings>().roomComponentBuilder().room(room).build()
|
||||||
|
mainDaggerComponentOwner.addComponent(room.roomId.value, component)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFlowReleased(room: MatrixRoom) {
|
||||||
|
mainDaggerComponentOwner.removeComponent(room.roomId.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resolve(navTarget: RootNavTarget, buildContext: BuildContext): Node {
|
||||||
|
return createNode<RootFlowNode>(buildContext, plugins = listOf(loggedInFlowNodeCallback, roomFlowNodeCallback))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun View(modifier: Modifier) {
|
||||||
|
Children(navModel = navModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
object RootNavTarget : Parcelable
|
||||||
|
}
|
||||||
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -17,13 +17,11 @@
|
||||||
package io.element.android.x.di
|
package io.element.android.x.di
|
||||||
|
|
||||||
import com.squareup.anvil.annotations.ContributesTo
|
import com.squareup.anvil.annotations.ContributesTo
|
||||||
|
import io.element.android.appnav.di.MatrixClientsHolder
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
|
||||||
import io.element.android.x.root.RootPresenter
|
|
||||||
|
|
||||||
@ContributesTo(AppScope::class)
|
@ContributesTo(AppScope::class)
|
||||||
interface AppBindings {
|
interface AppBindings {
|
||||||
fun rootPresenter(): RootPresenter
|
|
||||||
fun authenticationService(): MatrixAuthenticationService
|
|
||||||
fun matrixClientsHolder(): MatrixClientsHolder
|
fun matrixClientsHolder(): MatrixClientsHolder
|
||||||
|
fun mainDaggerComponentOwner(): MainDaggerComponentsOwner
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
|
||||||
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -20,8 +20,6 @@ import android.content.Context
|
||||||
import com.squareup.anvil.annotations.ContributesTo
|
import com.squareup.anvil.annotations.ContributesTo
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import io.element.android.features.rageshake.reporter.BugReporter
|
|
||||||
import io.element.android.features.rageshake.reporter.DefaultBugReporter
|
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
import io.element.android.libraries.di.ApplicationContext
|
import io.element.android.libraries.di.ApplicationContext
|
||||||
|
|
@ -60,7 +58,4 @@ object AppModule {
|
||||||
diffUpdateDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
diffUpdateDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
fun providesBugReporter(bugReporter: DefaultBugReporter): BugReporter = bugReporter
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* 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.x.di
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
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.di.SingleIn
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@SingleIn(AppScope::class)
|
||||||
|
class MainDaggerComponentsOwner @Inject constructor(@ApplicationContext context: Context) : DaggerComponentOwner {
|
||||||
|
|
||||||
|
private val daggerComponents = LinkedHashMap<String, Any>().apply {
|
||||||
|
put("app", (context as DaggerComponentOwner).daggerComponent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addComponent(identifier: String, component: Any) {
|
||||||
|
daggerComponents[identifier] = component
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeComponent(identifier: String) {
|
||||||
|
daggerComponents.remove(identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val daggerComponent: Any
|
||||||
|
get() = daggerComponents.values.reversed()
|
||||||
|
}
|
||||||
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
|
||||||
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -28,7 +28,7 @@ import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
|
|
||||||
@SingleIn(SessionScope::class)
|
@SingleIn(SessionScope::class)
|
||||||
@MergeSubcomponent(SessionScope::class)
|
@MergeSubcomponent(SessionScope::class)
|
||||||
interface SessionComponent : NodeFactoriesBindings, RoomComponent.ParentBindings {
|
interface SessionComponent : NodeFactoriesBindings {
|
||||||
|
|
||||||
@Subcomponent.Builder
|
@Subcomponent.Builder
|
||||||
interface Builder {
|
interface Builder {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ package io.element.android.x.initializer
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.startup.Initializer
|
import androidx.startup.Initializer
|
||||||
import io.element.android.features.rageshake.crash.VectorUncaughtExceptionHandler
|
import io.element.android.features.rageshake.impl.crash.VectorUncaughtExceptionHandler
|
||||||
|
|
||||||
class CrashInitializer : Initializer<Unit> {
|
class CrashInitializer : Initializer<Unit> {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ package io.element.android.x.initializer
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.startup.Initializer
|
import androidx.startup.Initializer
|
||||||
import io.element.android.features.rageshake.logs.VectorFileLogger
|
import io.element.android.features.rageshake.impl.logs.VectorFileLogger
|
||||||
import io.element.android.x.BuildConfig
|
import io.element.android.x.BuildConfig
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,70 +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.x.root
|
|
||||||
|
|
||||||
import io.element.android.features.rageshake.reporter.BugReporter
|
|
||||||
import io.element.android.features.rageshake.reporter.BugReporterListener
|
|
||||||
import io.element.android.features.rageshake.reporter.ReportType
|
|
||||||
import io.element.android.libraries.matrix.test.A_FAILURE_REASON
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
// TODO Remove this duplicated class when we will rework modules.
|
|
||||||
class FakeBugReporter(val mode: FakeBugReporterMode = FakeBugReporterMode.Success) : BugReporter {
|
|
||||||
override fun sendBugReport(
|
|
||||||
coroutineScope: CoroutineScope,
|
|
||||||
reportType: ReportType,
|
|
||||||
withDevicesLogs: Boolean,
|
|
||||||
withCrashLogs: Boolean,
|
|
||||||
withKeyRequestHistory: Boolean,
|
|
||||||
withScreenshot: Boolean,
|
|
||||||
theBugDescription: String,
|
|
||||||
serverVersion: String,
|
|
||||||
canContact: Boolean,
|
|
||||||
customFields: Map<String, String>?,
|
|
||||||
listener: BugReporterListener?,
|
|
||||||
) {
|
|
||||||
coroutineScope.launch {
|
|
||||||
delay(100)
|
|
||||||
listener?.onProgress(0)
|
|
||||||
delay(100)
|
|
||||||
listener?.onProgress(50)
|
|
||||||
delay(100)
|
|
||||||
when (mode) {
|
|
||||||
FakeBugReporterMode.Success -> Unit
|
|
||||||
FakeBugReporterMode.Failure -> {
|
|
||||||
listener?.onUploadFailed(A_FAILURE_REASON)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
FakeBugReporterMode.Cancel -> {
|
|
||||||
listener?.onUploadCancelled()
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
listener?.onProgress(100)
|
|
||||||
delay(100)
|
|
||||||
listener?.onUploadSucceed(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class FakeBugReporterMode {
|
|
||||||
Success,
|
|
||||||
Failure,
|
|
||||||
Cancel
|
|
||||||
}
|
|
||||||
|
|
@ -1,50 +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.x.root
|
|
||||||
|
|
||||||
import io.element.android.features.rageshake.crash.CrashDataStore
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
|
|
||||||
const val A_CRASH_DATA = "Some crash data"
|
|
||||||
|
|
||||||
// TODO Remove this duplicated class when we will rework modules.
|
|
||||||
|
|
||||||
class FakeCrashDataStore(
|
|
||||||
crashData: String = "",
|
|
||||||
appHasCrashed: Boolean = false,
|
|
||||||
) : CrashDataStore {
|
|
||||||
private val appHasCrashedFlow = MutableStateFlow(appHasCrashed)
|
|
||||||
private val crashDataFlow = MutableStateFlow(crashData)
|
|
||||||
|
|
||||||
override fun setCrashData(crashData: String) {
|
|
||||||
crashDataFlow.value = crashData
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun resetAppHasCrashed() {
|
|
||||||
appHasCrashedFlow.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun appHasCrashed(): Flow<Boolean> = appHasCrashedFlow
|
|
||||||
|
|
||||||
override fun crashInfo(): Flow<String> = crashDataFlow
|
|
||||||
|
|
||||||
override suspend fun reset() {
|
|
||||||
appHasCrashedFlow.value = false
|
|
||||||
crashDataFlow.value = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,44 +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.x.root
|
|
||||||
|
|
||||||
import io.element.android.features.rageshake.rageshake.RageShake
|
|
||||||
|
|
||||||
// TODO Remove this duplicated class when we will rework modules.
|
|
||||||
class FakeRageShake(
|
|
||||||
private var isAvailableValue: Boolean = true
|
|
||||||
) : RageShake {
|
|
||||||
|
|
||||||
private var interceptor: (() -> Unit)? = null
|
|
||||||
|
|
||||||
override fun isAvailable() = isAvailableValue
|
|
||||||
|
|
||||||
override fun start(sensitivity: Float) {
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun stop() {
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setSensitivity(sensitivity: Float) {
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setInterceptor(interceptor: (() -> Unit)?) {
|
|
||||||
this.interceptor = interceptor
|
|
||||||
}
|
|
||||||
|
|
||||||
fun triggerPhoneRageshake() = interceptor?.invoke()
|
|
||||||
}
|
|
||||||
|
|
@ -1,46 +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.x.root
|
|
||||||
|
|
||||||
import io.element.android.features.rageshake.rageshake.RageshakeDataStore
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
|
|
||||||
const val A_SENSITIVITY = 1f
|
|
||||||
|
|
||||||
// TODO Remove this duplicated class when we will rework modules.
|
|
||||||
class FakeRageshakeDataStore(
|
|
||||||
isEnabled: Boolean = true,
|
|
||||||
sensitivity: Float = A_SENSITIVITY,
|
|
||||||
) : RageshakeDataStore {
|
|
||||||
|
|
||||||
private val isEnabledFlow = MutableStateFlow(isEnabled)
|
|
||||||
override fun isEnabled(): Flow<Boolean> = isEnabledFlow
|
|
||||||
|
|
||||||
override suspend fun setIsEnabled(isEnabled: Boolean) {
|
|
||||||
isEnabledFlow.value = isEnabled
|
|
||||||
}
|
|
||||||
|
|
||||||
private val sensitivityFlow = MutableStateFlow(sensitivity)
|
|
||||||
override fun sensitivity(): Flow<Float> = sensitivityFlow
|
|
||||||
|
|
||||||
override suspend fun setSensitivity(sensitivity: Float) {
|
|
||||||
sensitivityFlow.value = sensitivity
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun reset() = Unit
|
|
||||||
}
|
|
||||||
59
appnav/build.gradle.kts
Normal file
59
appnav/build.gradle.kts
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("UnstableApiUsage")
|
||||||
|
|
||||||
|
import extension.allFeaturesApi
|
||||||
|
|
||||||
|
// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed
|
||||||
|
@Suppress("DSL_SCOPE_VIOLATION")
|
||||||
|
plugins {
|
||||||
|
id("io.element.android-compose-library")
|
||||||
|
alias(libs.plugins.anvil)
|
||||||
|
alias(libs.plugins.ksp)
|
||||||
|
alias(libs.plugins.kapt)
|
||||||
|
id("kotlin-parcelize")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "io.element.android.appnav"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(projects.anvilannotations)
|
||||||
|
anvil(projects.anvilcodegen)
|
||||||
|
implementation(libs.dagger)
|
||||||
|
kapt(libs.dagger.compiler)
|
||||||
|
|
||||||
|
allFeaturesApi()
|
||||||
|
|
||||||
|
implementation(projects.libraries.core)
|
||||||
|
implementation(projects.libraries.architecture)
|
||||||
|
implementation(projects.libraries.matrix.api)
|
||||||
|
implementation(projects.libraries.designsystem)
|
||||||
|
implementation(projects.libraries.matrixui)
|
||||||
|
implementation(projects.tests.uitests)
|
||||||
|
implementation(libs.coil)
|
||||||
|
|
||||||
|
testImplementation(libs.test.junit)
|
||||||
|
testImplementation(libs.coroutines.test)
|
||||||
|
testImplementation(libs.molecule.runtime)
|
||||||
|
testImplementation(libs.test.truth)
|
||||||
|
testImplementation(libs.test.turbine)
|
||||||
|
testImplementation(projects.libraries.matrix.test)
|
||||||
|
testImplementation(projects.features.rageshake.test)
|
||||||
|
testImplementation(projects.features.rageshake.impl)
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.x.node
|
package io.element.android.appnav
|
||||||
|
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.NewRoot
|
import com.bumble.appyx.navmodel.backstack.operation.NewRoot
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.x.node
|
package io.element.android.appnav
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
|
@ -27,72 +27,79 @@ 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
|
||||||
import com.bumble.appyx.core.node.ParentNode
|
|
||||||
import com.bumble.appyx.core.node.node
|
import com.bumble.appyx.core.node.node
|
||||||
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||||
import io.element.android.features.createroom.CreateRoomFlowNode
|
import dagger.assisted.Assisted
|
||||||
import io.element.android.features.preferences.PreferencesFlowNode
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.features.roomlist.RoomListNode
|
import io.element.android.anvilannotations.ContributesNode
|
||||||
|
import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
||||||
|
import io.element.android.features.preferences.api.PreferencesEntryPoint
|
||||||
|
import io.element.android.features.roomlist.api.RoomListEntryPoint
|
||||||
|
import io.element.android.libraries.architecture.BackstackNode
|
||||||
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
|
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
|
||||||
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.architecture.inputs
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.di.DaggerComponentOwner
|
import io.element.android.libraries.di.AppScope
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
|
||||||
import io.element.android.libraries.matrix.ui.di.MatrixUIBindings
|
import io.element.android.libraries.matrix.ui.di.MatrixUIBindings
|
||||||
import io.element.android.x.di.SessionComponent
|
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
class LoggedInFlowNode(
|
@ContributesNode(AppScope::class)
|
||||||
buildContext: BuildContext,
|
class LoggedInFlowNode @AssistedInject constructor(
|
||||||
val sessionId: SessionId,
|
@Assisted buildContext: BuildContext,
|
||||||
private val matrixClient: MatrixClient,
|
@Assisted plugins: List<Plugin>,
|
||||||
private val onOpenBugReport: () -> Unit,
|
private val roomListEntryPoint: RoomListEntryPoint,
|
||||||
private val backstack: BackStack<NavTarget> = BackStack(
|
private val preferencesEntryPoint: PreferencesEntryPoint,
|
||||||
|
private val createRoomEntryPoint: CreateRoomEntryPoint,
|
||||||
|
) : BackstackNode<LoggedInFlowNode.NavTarget>(
|
||||||
|
backstack = BackStack(
|
||||||
initialElement = NavTarget.RoomList,
|
initialElement = NavTarget.RoomList,
|
||||||
savedStateMap = buildContext.savedStateMap,
|
savedStateMap = buildContext.savedStateMap,
|
||||||
),
|
),
|
||||||
) : ParentNode<LoggedInFlowNode.NavTarget>(
|
buildContext = buildContext,
|
||||||
navModel = backstack,
|
plugins = plugins
|
||||||
buildContext = buildContext
|
) {
|
||||||
), DaggerComponentOwner {
|
|
||||||
|
|
||||||
override val daggerComponent: Any by lazy {
|
interface Callback : Plugin {
|
||||||
parent!!.bindings<SessionComponent.ParentBindings>().sessionComponentBuilder().client(matrixClient).build()
|
fun onOpenBugReport() = Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface LifecycleCallback : NodeLifecycleCallback {
|
||||||
|
fun onFlowCreated(client: MatrixClient) = Unit
|
||||||
|
|
||||||
|
fun onFlowReleased(client: MatrixClient) = Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Inputs(
|
||||||
|
val matrixClient: MatrixClient
|
||||||
|
) : NodeInputs
|
||||||
|
|
||||||
|
private val inputs: Inputs = inputs()
|
||||||
|
|
||||||
override fun onBuilt() {
|
override fun onBuilt() {
|
||||||
super.onBuilt()
|
super.onBuilt()
|
||||||
lifecycle.subscribe(
|
lifecycle.subscribe(
|
||||||
onCreate = {
|
onCreate = {
|
||||||
|
plugins<LifecycleCallback>().forEach { it.onFlowCreated(inputs.matrixClient) }
|
||||||
val imageLoaderFactory = bindings<MatrixUIBindings>().loggedInImageLoaderFactory()
|
val imageLoaderFactory = bindings<MatrixUIBindings>().loggedInImageLoaderFactory()
|
||||||
Coil.setImageLoader(imageLoaderFactory)
|
Coil.setImageLoader(imageLoaderFactory)
|
||||||
matrixClient.startSync()
|
inputs.matrixClient.startSync()
|
||||||
},
|
},
|
||||||
onDestroy = {
|
onDestroy = {
|
||||||
val imageLoaderFactory = bindings<MatrixUIBindings>().notLoggedInImageLoaderFactory()
|
val imageLoaderFactory = bindings<MatrixUIBindings>().notLoggedInImageLoaderFactory()
|
||||||
Coil.setImageLoader(imageLoaderFactory)
|
Coil.setImageLoader(imageLoaderFactory)
|
||||||
|
plugins<LifecycleCallback>().forEach { it.onFlowReleased(inputs.matrixClient) }
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val roomListCallback = object : RoomListNode.Callback {
|
|
||||||
override fun onRoomClicked(roomId: RoomId) {
|
|
||||||
backstack.push(NavTarget.Room(roomId))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSettingsClicked() {
|
|
||||||
backstack.push(NavTarget.Settings)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateRoomClicked() {
|
|
||||||
backstack.push(NavTarget.CreateRoom)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed interface NavTarget : Parcelable {
|
sealed interface NavTarget : Parcelable {
|
||||||
@Parcelize
|
@Parcelize
|
||||||
object RoomList : NavTarget
|
object RoomList : NavTarget
|
||||||
|
|
@ -110,10 +117,26 @@ class LoggedInFlowNode(
|
||||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||||
return when (navTarget) {
|
return when (navTarget) {
|
||||||
NavTarget.RoomList -> {
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateRoomClicked() {
|
||||||
|
backstack.push(NavTarget.CreateRoom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
roomListEntryPoint
|
||||||
|
.nodeBuilder(this, buildContext)
|
||||||
|
.callback(callback)
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
is NavTarget.Room -> {
|
is NavTarget.Room -> {
|
||||||
val room = matrixClient.getRoom(roomId = navTarget.roomId)
|
val room = inputs.matrixClient.getRoom(roomId = navTarget.roomId)
|
||||||
if (room == null) {
|
if (room == null) {
|
||||||
// TODO CREATE UNKNOWN ROOM NODE
|
// TODO CREATE UNKNOWN ROOM NODE
|
||||||
node(buildContext) {
|
node(buildContext) {
|
||||||
|
|
@ -122,14 +145,23 @@ class LoggedInFlowNode(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
RoomFlowNode(buildContext, room)
|
val nodeLifecycleCallbacks = plugins<NodeLifecycleCallback>()
|
||||||
|
val inputs = RoomFlowNode.Inputs(room)
|
||||||
|
createNode<RoomFlowNode>(buildContext, plugins = listOf(inputs) + nodeLifecycleCallbacks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NavTarget.Settings -> {
|
NavTarget.Settings -> {
|
||||||
PreferencesFlowNode(buildContext, onOpenBugReport)
|
val callback = object : PreferencesEntryPoint.Callback {
|
||||||
|
override fun onOpenBugReport() {
|
||||||
|
plugins<Callback>().forEach { it.onOpenBugReport() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
preferencesEntryPoint.nodeBuilder(this, buildContext)
|
||||||
|
.callback(callback)
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
NavTarget.CreateRoom -> {
|
NavTarget.CreateRoom -> {
|
||||||
CreateRoomFlowNode(buildContext)
|
createRoomEntryPoint.createNode(this, buildContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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.appnav
|
||||||
|
|
||||||
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
|
|
||||||
|
interface NodeLifecycleCallback : Plugin
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.x.node
|
package io.element.android.appnav
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -23,27 +23,34 @@ 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
|
||||||
import com.bumble.appyx.core.node.ParentNode
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.node.node
|
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||||
import io.element.android.features.login.LoginFlowNode
|
import dagger.assisted.Assisted
|
||||||
import io.element.android.features.onboarding.OnBoardingScreen
|
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.BackstackNode
|
||||||
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
|
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
|
||||||
|
import io.element.android.libraries.di.AppScope
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
class NotLoggedInFlowNode(
|
@ContributesNode(AppScope::class)
|
||||||
buildContext: BuildContext,
|
class NotLoggedInFlowNode @AssistedInject constructor(
|
||||||
private val backstack: BackStack<NavTarget> = BackStack(
|
@Assisted buildContext: BuildContext,
|
||||||
|
@Assisted plugins: List<Plugin>,
|
||||||
|
private val onBoardingEntryPoint: OnBoardingEntryPoint,
|
||||||
|
private val loginEntryPoint: LoginEntryPoint,
|
||||||
|
) : BackstackNode<NotLoggedInFlowNode.NavTarget>(
|
||||||
|
backstack = BackStack(
|
||||||
initialElement = NavTarget.OnBoarding,
|
initialElement = NavTarget.OnBoarding,
|
||||||
savedStateMap = buildContext.savedStateMap,
|
savedStateMap = buildContext.savedStateMap
|
||||||
),
|
),
|
||||||
) : ParentNode<NotLoggedInFlowNode.NavTarget>(
|
buildContext = buildContext,
|
||||||
navModel = backstack,
|
plugins = plugins,
|
||||||
buildContext = buildContext
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
lifecycle.subscribe(
|
lifecycle.subscribe(
|
||||||
onCreate = { Timber.v("OnCreate") },
|
onCreate = { Timber.v("OnCreate") },
|
||||||
|
|
@ -61,12 +68,24 @@ class NotLoggedInFlowNode(
|
||||||
|
|
||||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||||
return when (navTarget) {
|
return when (navTarget) {
|
||||||
NavTarget.OnBoarding -> node(buildContext) {
|
NavTarget.OnBoarding -> {
|
||||||
OnBoardingScreen(
|
val callback = object : OnBoardingEntryPoint.Callback {
|
||||||
onSignIn = { backstack.push(NavTarget.LoginFlow) }
|
override fun onSignUp() {
|
||||||
)
|
//NOOP
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSignIn() {
|
||||||
|
backstack.push(NavTarget.LoginFlow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onBoardingEntryPoint
|
||||||
|
.nodeBuilder(this, buildContext)
|
||||||
|
.callback(callback)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
NavTarget.LoginFlow -> {
|
||||||
|
loginEntryPoint.createNode(this, buildContext)
|
||||||
}
|
}
|
||||||
NavTarget.LoginFlow -> LoginFlowNode(buildContext)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.x.node
|
package io.element.android.appnav
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -23,43 +23,64 @@ 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
|
||||||
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 com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import io.element.android.features.messages.MessagesNode
|
import dagger.assisted.Assisted
|
||||||
import io.element.android.libraries.architecture.bindings
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.libraries.architecture.createNode
|
import io.element.android.anvilannotations.ContributesNode
|
||||||
import io.element.android.libraries.di.DaggerComponentOwner
|
import io.element.android.features.messages.api.MessagesEntryPoint
|
||||||
|
import io.element.android.libraries.architecture.BackstackNode
|
||||||
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
|
import io.element.android.libraries.architecture.inputs
|
||||||
|
import io.element.android.libraries.di.SessionScope
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
import io.element.android.x.di.RoomComponent
|
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
class RoomFlowNode(
|
@ContributesNode(SessionScope::class)
|
||||||
buildContext: BuildContext,
|
class RoomFlowNode @AssistedInject constructor(
|
||||||
private val room: MatrixRoom,
|
@Assisted buildContext: BuildContext,
|
||||||
private val backstack: BackStack<NavTarget> = BackStack(
|
@Assisted plugins: List<Plugin>,
|
||||||
|
private val messagesEntryPoint: MessagesEntryPoint,
|
||||||
|
) : BackstackNode<RoomFlowNode.NavTarget>(
|
||||||
|
backstack = BackStack(
|
||||||
initialElement = NavTarget.Messages,
|
initialElement = NavTarget.Messages,
|
||||||
savedStateMap = buildContext.savedStateMap,
|
savedStateMap = buildContext.savedStateMap,
|
||||||
),
|
),
|
||||||
) : ParentNode<RoomFlowNode.NavTarget>(
|
buildContext = buildContext,
|
||||||
navModel = backstack,
|
plugins = plugins,
|
||||||
buildContext = buildContext
|
) {
|
||||||
), DaggerComponentOwner {
|
|
||||||
|
|
||||||
override val daggerComponent: Any by lazy {
|
interface LifecycleCallback : NodeLifecycleCallback {
|
||||||
parent!!.bindings<RoomComponent.ParentBindings>().roomComponentBuilder().room(room).build()
|
fun onFlowCreated(room: MatrixRoom) = Unit
|
||||||
|
fun onFlowReleased(room: MatrixRoom) = Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class Inputs(
|
||||||
|
val room: MatrixRoom,
|
||||||
|
) : NodeInputs
|
||||||
|
|
||||||
|
private val inputs: Inputs = inputs()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
lifecycle.subscribe(
|
lifecycle.subscribe(
|
||||||
onCreate = { Timber.v("OnCreate") },
|
onCreate = {
|
||||||
onDestroy = { Timber.v("OnDestroy") }
|
Timber.v("OnCreate")
|
||||||
|
plugins<LifecycleCallback>().forEach { it.onFlowCreated(inputs.room) }
|
||||||
|
},
|
||||||
|
onDestroy = {
|
||||||
|
Timber.v("OnDestroy")
|
||||||
|
plugins<LifecycleCallback>().forEach { it.onFlowReleased(inputs.room) }
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||||
return when (navTarget) {
|
return when (navTarget) {
|
||||||
NavTarget.Messages -> createNode<MessagesNode>(buildContext)
|
NavTarget.Messages -> {
|
||||||
|
messagesEntryPoint.createNode(this, buildContext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -14,58 +14,66 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.x.node
|
package io.element.android.appnav
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.runtime.Composable
|
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.compose.ui.platform.LocalContext
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.bumble.appyx.core.composable.Children
|
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.ParentNode
|
|
||||||
import com.bumble.appyx.core.node.node
|
import com.bumble.appyx.core.node.node
|
||||||
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.newRoot
|
import com.bumble.appyx.navmodel.backstack.operation.newRoot
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.pop
|
import com.bumble.appyx.navmodel.backstack.operation.pop
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
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.appnav.di.MatrixClientsHolder
|
||||||
|
import io.element.android.appnav.root.RootPresenter
|
||||||
|
import io.element.android.appnav.root.RootView
|
||||||
|
import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint
|
||||||
|
import io.element.android.libraries.architecture.BackstackNode
|
||||||
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
|
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.designsystem.theme.components.CircularProgressIndicator
|
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
||||||
import io.element.android.libraries.di.DaggerComponentOwner
|
import io.element.android.libraries.di.AppScope
|
||||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.x.di.MatrixClientsHolder
|
import io.element.android.tests.uitests.openShowkase
|
||||||
import io.element.android.x.root.RootPresenter
|
|
||||||
import io.element.android.x.root.RootView
|
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
class RootFlowNode(
|
@ContributesNode(AppScope::class)
|
||||||
private val buildContext: BuildContext,
|
class RootFlowNode @AssistedInject constructor(
|
||||||
private val backstack: BackStack<NavTarget> = BackStack(
|
@Assisted val buildContext: BuildContext,
|
||||||
initialElement = NavTarget.SplashScreen,
|
@Assisted plugins: List<Plugin>,
|
||||||
savedStateMap = buildContext.savedStateMap,
|
|
||||||
),
|
|
||||||
private val appComponentOwner: DaggerComponentOwner,
|
|
||||||
private val authenticationService: MatrixAuthenticationService,
|
private val authenticationService: MatrixAuthenticationService,
|
||||||
private val matrixClientsHolder: MatrixClientsHolder,
|
private val matrixClientsHolder: MatrixClientsHolder,
|
||||||
private val presenter: RootPresenter
|
private val presenter: RootPresenter,
|
||||||
|
private val bugReportEntryPoint: BugReportEntryPoint,
|
||||||
) :
|
) :
|
||||||
ParentNode<RootFlowNode.NavTarget>(
|
BackstackNode<RootFlowNode.NavTarget>(
|
||||||
navModel = backstack,
|
backstack = BackStack(
|
||||||
buildContext = buildContext
|
initialElement = NavTarget.SplashScreen,
|
||||||
),
|
savedStateMap = buildContext.savedStateMap,
|
||||||
|
),
|
||||||
DaggerComponentOwner by appComponentOwner {
|
buildContext = buildContext,
|
||||||
|
plugins = plugins
|
||||||
|
) {
|
||||||
|
|
||||||
override fun onBuilt() {
|
override fun onBuilt() {
|
||||||
super.onBuilt()
|
super.onBuilt()
|
||||||
|
|
@ -127,11 +135,17 @@ class RootFlowNode(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun View(modifier: Modifier) {
|
override fun View(modifier: Modifier) {
|
||||||
|
val activity = LocalContext.current as Activity
|
||||||
|
fun openShowkase() {
|
||||||
|
openShowkase(activity)
|
||||||
|
}
|
||||||
|
|
||||||
val state = presenter.present()
|
val state = presenter.present()
|
||||||
RootView(
|
RootView(
|
||||||
state = state,
|
state = state,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onOpenBugReport = this::onOpenBugReport,
|
onOpenBugReport = this::onOpenBugReport,
|
||||||
|
onOpenShowkase = ::openShowkase
|
||||||
) {
|
) {
|
||||||
Children(
|
Children(
|
||||||
navModel = backstack,
|
navModel = backstack,
|
||||||
|
|
@ -141,12 +155,6 @@ class RootFlowNode(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val bugReportNodeCallback = object : BugReportNode.Callback {
|
|
||||||
override fun onBugReportSent() {
|
|
||||||
backstack.pop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed interface NavTarget : Parcelable {
|
sealed interface NavTarget : Parcelable {
|
||||||
@Parcelize
|
@Parcelize
|
||||||
object SplashScreen : NavTarget
|
object SplashScreen : NavTarget
|
||||||
|
|
@ -168,16 +176,28 @@ class RootFlowNode(
|
||||||
Timber.w("Couldn't find any session, go through SplashScreen")
|
Timber.w("Couldn't find any session, go through SplashScreen")
|
||||||
backstack.newRoot(NavTarget.SplashScreen)
|
backstack.newRoot(NavTarget.SplashScreen)
|
||||||
}
|
}
|
||||||
LoggedInFlowNode(
|
val inputs = LoggedInFlowNode.Inputs(matrixClient)
|
||||||
buildContext = buildContext,
|
val callback = object : LoggedInFlowNode.Callback {
|
||||||
sessionId = navTarget.sessionId,
|
override fun onOpenBugReport() {
|
||||||
matrixClient = matrixClient,
|
backstack.push(NavTarget.BugReport)
|
||||||
onOpenBugReport = this::onOpenBugReport
|
}
|
||||||
)
|
}
|
||||||
|
val nodeLifecycleCallbacks = plugins<NodeLifecycleCallback>()
|
||||||
|
createNode<LoggedInFlowNode>(buildContext, plugins = listOf(inputs, callback) + nodeLifecycleCallbacks)
|
||||||
}
|
}
|
||||||
NavTarget.NotLoggedInFlow -> NotLoggedInFlowNode(buildContext)
|
NavTarget.NotLoggedInFlow -> createNode<NotLoggedInFlowNode>(buildContext)
|
||||||
NavTarget.SplashScreen -> splashNode(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
|
||||||
|
.nodeBuilder(this, buildContext)
|
||||||
|
.callback(callback)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.x.di
|
package io.element.android.appnav.di
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.x.root
|
package io.element.android.appnav.root
|
||||||
|
|
||||||
sealed interface RootEvents {
|
sealed interface RootEvents {
|
||||||
object HideShowkaseButton : RootEvents
|
object HideShowkaseButton : RootEvents
|
||||||
|
|
@ -14,13 +14,13 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.x.root
|
package io.element.android.appnav.root
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import io.element.android.features.rageshake.crash.ui.CrashDetectionPresenter
|
import io.element.android.features.rageshake.api.crash.CrashDetectionPresenter
|
||||||
import io.element.android.features.rageshake.detection.RageshakeDetectionPresenter
|
import io.element.android.features.rageshake.api.detection.RageshakeDetectionPresenter
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
@ -14,13 +14,13 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.x.root
|
package io.element.android.appnav.root
|
||||||
|
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Immutable
|
||||||
import io.element.android.features.rageshake.crash.ui.CrashDetectionState
|
import io.element.android.features.rageshake.api.crash.CrashDetectionState
|
||||||
import io.element.android.features.rageshake.detection.RageshakeDetectionState
|
import io.element.android.features.rageshake.api.detection.RageshakeDetectionState
|
||||||
|
|
||||||
@Stable
|
@Immutable
|
||||||
data class RootState(
|
data class RootState(
|
||||||
val isShowkaseButtonVisible: Boolean,
|
val isShowkaseButtonVisible: Boolean,
|
||||||
val rageshakeDetectionState: RageshakeDetectionState,
|
val rageshakeDetectionState: RageshakeDetectionState,
|
||||||
|
|
@ -14,11 +14,11 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.x.root
|
package io.element.android.appnav.root
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.rageshake.crash.ui.aCrashDetectionState
|
import io.element.android.features.rageshake.api.crash.aCrashDetectionState
|
||||||
import io.element.android.features.rageshake.detection.aRageshakeDetectionState
|
import io.element.android.features.rageshake.api.detection.aRageshakeDetectionState
|
||||||
|
|
||||||
open class RootStateProvider : PreviewParameterProvider<RootState> {
|
open class RootStateProvider : PreviewParameterProvider<RootState> {
|
||||||
override val values: Sequence<RootState>
|
override val values: Sequence<RootState>
|
||||||
|
|
@ -14,33 +14,30 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.x.root
|
package io.element.android.appnav.root
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.BoxScope
|
import androidx.compose.foundation.layout.BoxScope
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.runtime.Composable
|
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.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import io.element.android.features.rageshake.crash.ui.CrashDetectionEvents
|
import io.element.android.features.rageshake.api.crash.CrashDetectionEvents
|
||||||
import io.element.android.features.rageshake.crash.ui.CrashDetectionView
|
import io.element.android.features.rageshake.api.crash.CrashDetectionView
|
||||||
import io.element.android.features.rageshake.detection.RageshakeDetectionEvents
|
import io.element.android.features.rageshake.api.detection.RageshakeDetectionEvents
|
||||||
import io.element.android.features.rageshake.detection.RageshakeDetectionView
|
import io.element.android.features.rageshake.api.detection.RageshakeDetectionView
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.tests.uitests.openShowkase
|
|
||||||
import io.element.android.x.component.ShowkaseButton
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RootView(
|
fun RootView(
|
||||||
state: RootState,
|
state: RootState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onOpenBugReport: () -> Unit = {},
|
onOpenBugReport: () -> Unit = {},
|
||||||
|
onOpenShowkase: () -> Unit = {},
|
||||||
children: @Composable BoxScope.() -> Unit,
|
children: @Composable BoxScope.() -> Unit,
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
|
|
@ -50,7 +47,6 @@ fun RootView(
|
||||||
) {
|
) {
|
||||||
children()
|
children()
|
||||||
val eventSink = state.eventSink
|
val eventSink = state.eventSink
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
fun onOpenBugReport() {
|
fun onOpenBugReport() {
|
||||||
state.crashDetectionState.eventSink(CrashDetectionEvents.ResetAppHasCrashed)
|
state.crashDetectionState.eventSink(CrashDetectionEvents.ResetAppHasCrashed)
|
||||||
|
|
@ -61,7 +57,7 @@ fun RootView(
|
||||||
ShowkaseButton(
|
ShowkaseButton(
|
||||||
isVisible = state.isShowkaseButtonVisible,
|
isVisible = state.isShowkaseButtonVisible,
|
||||||
onCloseClicked = { eventSink(RootEvents.HideShowkaseButton) },
|
onCloseClicked = { eventSink(RootEvents.HideShowkaseButton) },
|
||||||
onClick = { openShowkase(context as Activity) }
|
onClick = onOpenShowkase
|
||||||
)
|
)
|
||||||
RageshakeDetectionView(
|
RageshakeDetectionView(
|
||||||
state = state.rageshakeDetectionState,
|
state = state.rageshakeDetectionState,
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.x.component
|
package io.element.android.appnav.root
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
|
@ -16,15 +16,21 @@
|
||||||
|
|
||||||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
|
||||||
package io.element.android.x.root
|
package io.element.android.appnav
|
||||||
|
|
||||||
import app.cash.molecule.RecompositionClock
|
import app.cash.molecule.RecompositionClock
|
||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.rageshake.crash.ui.CrashDetectionPresenter
|
import io.element.android.appnav.root.RootEvents
|
||||||
import io.element.android.features.rageshake.detection.RageshakeDetectionPresenter
|
import io.element.android.appnav.root.RootPresenter
|
||||||
import io.element.android.features.rageshake.preferences.RageshakePreferencesPresenter
|
import io.element.android.features.rageshake.impl.crash.DefaultCrashDetectionPresenter
|
||||||
|
import io.element.android.features.rageshake.impl.detection.DefaultRageshakeDetectionPresenter
|
||||||
|
import io.element.android.features.rageshake.impl.preferences.DefaultRageshakePreferencesPresenter
|
||||||
|
import io.element.android.features.rageshake.test.crash.FakeCrashDataStore
|
||||||
|
import io.element.android.features.rageshake.test.rageshake.FakeRageShake
|
||||||
|
import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataStore
|
||||||
|
import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolder
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
@ -61,13 +67,13 @@ class RootPresenterTest {
|
||||||
val rageshakeDataStore = FakeRageshakeDataStore()
|
val rageshakeDataStore = FakeRageshakeDataStore()
|
||||||
val rageshake = FakeRageShake()
|
val rageshake = FakeRageShake()
|
||||||
val screenshotHolder = FakeScreenshotHolder()
|
val screenshotHolder = FakeScreenshotHolder()
|
||||||
val crashDetectionPresenter = CrashDetectionPresenter(
|
val crashDetectionPresenter = DefaultCrashDetectionPresenter(
|
||||||
crashDataStore = crashDataStore
|
crashDataStore = crashDataStore
|
||||||
)
|
)
|
||||||
val rageshakeDetectionPresenter = RageshakeDetectionPresenter(
|
val rageshakeDetectionPresenter = DefaultRageshakeDetectionPresenter(
|
||||||
screenshotHolder = screenshotHolder,
|
screenshotHolder = screenshotHolder,
|
||||||
rageShake = rageshake,
|
rageShake = rageshake,
|
||||||
preferencesPresenter = RageshakePreferencesPresenter(
|
preferencesPresenter = DefaultRageshakePreferencesPresenter(
|
||||||
rageshake = rageshake,
|
rageshake = rageshake,
|
||||||
rageshakeDataStore = rageshakeDataStore,
|
rageshakeDataStore = rageshakeDataStore,
|
||||||
)
|
)
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
import kotlinx.kover.api.KoverTaskExtension
|
import kotlinx.kover.api.KoverTaskExtension
|
||||||
import org.jetbrains.kotlin.cli.common.toBooleanLenient
|
import org.jetbrains.kotlin.cli.common.toBooleanLenient
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
dependencies {
|
||||||
|
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022 New Vector Ltd
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -14,19 +14,14 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login
|
plugins {
|
||||||
|
id("io.element.android-library")
|
||||||
import org.junit.Assert.assertEquals
|
}
|
||||||
import org.junit.Test
|
|
||||||
|
android {
|
||||||
/**
|
namespace = "io.element.android.features.createroom.api"
|
||||||
* Example local unit test, which will execute on the development machine (host).
|
}
|
||||||
*
|
|
||||||
* See [testing documentation](http://d.android.com/tools/testing).
|
dependencies {
|
||||||
*/
|
implementation(projects.libraries.architecture)
|
||||||
class ExampleUnitTest {
|
|
||||||
@Test
|
|
||||||
fun addition_isCorrect() {
|
|
||||||
assertEquals(4, 2 + 2)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -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.createroom.api
|
||||||
|
|
||||||
|
import io.element.android.libraries.architecture.SimpleFeatureEntryPoint
|
||||||
|
|
||||||
|
interface CreateRoomEntryPoint : SimpleFeatureEntryPoint
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2023 New Vector Ltd
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -18,13 +18,19 @@
|
||||||
@Suppress("DSL_SCOPE_VIOLATION")
|
@Suppress("DSL_SCOPE_VIOLATION")
|
||||||
plugins {
|
plugins {
|
||||||
id("io.element.android-compose-library")
|
id("io.element.android-compose-library")
|
||||||
alias(libs.plugins.ksp)
|
|
||||||
alias(libs.plugins.anvil)
|
alias(libs.plugins.anvil)
|
||||||
|
alias(libs.plugins.ksp)
|
||||||
id("kotlin-parcelize")
|
id("kotlin-parcelize")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "io.element.android.features.createroom"
|
namespace = "io.element.android.features.createroom.impl"
|
||||||
|
|
||||||
|
testOptions {
|
||||||
|
unitTests {
|
||||||
|
isIncludeAndroidResources = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
anvil {
|
anvil {
|
||||||
|
|
@ -42,6 +48,7 @@ dependencies {
|
||||||
implementation(projects.libraries.designsystem)
|
implementation(projects.libraries.designsystem)
|
||||||
implementation(projects.libraries.elementresources)
|
implementation(projects.libraries.elementresources)
|
||||||
implementation(projects.libraries.uiStrings)
|
implementation(projects.libraries.uiStrings)
|
||||||
|
api(projects.features.createroom.api)
|
||||||
|
|
||||||
testImplementation(libs.test.junit)
|
testImplementation(libs.test.junit)
|
||||||
testImplementation(libs.coroutines.test)
|
testImplementation(libs.coroutines.test)
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.createroom
|
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
|
||||||
|
|
@ -22,22 +22,29 @@ import androidx.compose.ui.Modifier
|
||||||
import com.bumble.appyx.core.composable.Children
|
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.ParentNode
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import io.element.android.features.createroom.root.CreateRoomRootNode
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
import io.element.android.anvilannotations.ContributesNode
|
||||||
|
import io.element.android.features.createroom.impl.root.CreateRoomRootNode
|
||||||
|
import io.element.android.libraries.architecture.BackstackNode
|
||||||
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
|
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 kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
class CreateRoomFlowNode(
|
@ContributesNode(SessionScope::class)
|
||||||
buildContext: BuildContext,
|
class CreateRoomFlowNode @AssistedInject constructor(
|
||||||
private val backstack: BackStack<NavTarget> = BackStack(
|
@Assisted buildContext: BuildContext,
|
||||||
|
@Assisted plugins: List<Plugin>,
|
||||||
|
) : BackstackNode<CreateRoomFlowNode.NavTarget>(
|
||||||
|
backstack = BackStack(
|
||||||
initialElement = NavTarget.Root,
|
initialElement = NavTarget.Root,
|
||||||
savedStateMap = buildContext.savedStateMap,
|
savedStateMap = buildContext.savedStateMap,
|
||||||
),
|
),
|
||||||
) : ParentNode<CreateRoomFlowNode.NavTarget>(
|
buildContext = buildContext,
|
||||||
navModel = backstack,
|
plugins = plugins
|
||||||
buildContext = buildContext
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
sealed interface NavTarget : Parcelable {
|
sealed interface NavTarget : Parcelable {
|
||||||
|
|
@ -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.createroom.impl
|
||||||
|
|
||||||
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
import com.bumble.appyx.core.node.Node
|
||||||
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
|
import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
||||||
|
import io.element.android.libraries.architecture.createNode
|
||||||
|
import io.element.android.libraries.di.AppScope
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ContributesBinding(AppScope::class)
|
||||||
|
class DefaultCreateRoomEntryPoint @Inject constructor() : CreateRoomEntryPoint {
|
||||||
|
override fun createNode(parentNode: Node, buildContext: BuildContext): Node {
|
||||||
|
return parentNode.createNode<CreateRoomFlowNode>(buildContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.createroom.root
|
package io.element.android.features.createroom.impl.root
|
||||||
|
|
||||||
sealed interface CreateRoomRootEvents {
|
sealed interface CreateRoomRootEvents {
|
||||||
object CreateRoom : CreateRoomRootEvents
|
object CreateRoom : CreateRoomRootEvents
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.createroom.root
|
package io.element.android.features.createroom.impl.root
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -43,7 +43,7 @@ class CreateRoomRootNode @AssistedInject constructor(
|
||||||
@Composable
|
@Composable
|
||||||
override fun View(modifier: Modifier) {
|
override fun View(modifier: Modifier) {
|
||||||
val state = presenter.present()
|
val state = presenter.present()
|
||||||
CreateRoomRootScreen(
|
CreateRoomRootView(
|
||||||
state = state,
|
state = state,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onClosePressed = this::navigateUp,
|
onClosePressed = this::navigateUp,
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.createroom.root
|
package io.element.android.features.createroom.impl.root
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.createroom.root
|
package io.element.android.features.createroom.impl.root
|
||||||
|
|
||||||
// TODO add your ui models. Remove the eventSink if you don't have events.
|
// TODO add your ui models. Remove the eventSink if you don't have events.
|
||||||
// Do not use default value, so no member get forgotten in the presenters.
|
// Do not use default value, so no member get forgotten in the presenters.
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.createroom.root
|
package io.element.android.features.createroom.impl.root
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.createroom.root
|
package io.element.android.features.createroom.impl.root
|
||||||
|
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
|
@ -59,7 +59,7 @@ import io.element.android.libraries.ui.strings.R as StringR
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun CreateRoomRootScreen(
|
fun CreateRoomRootView(
|
||||||
state: CreateRoomRootState,
|
state: CreateRoomRootState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onClosePressed: () -> Unit = {},
|
onClosePressed: () -> Unit = {},
|
||||||
|
|
@ -230,7 +230,7 @@ fun CreateRoomRootViewDarkPreview(@PreviewParameter(CreateRoomRootStateProvider:
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ContentToPreview(state: CreateRoomRootState) {
|
private fun ContentToPreview(state: CreateRoomRootState) {
|
||||||
CreateRoomRootScreen(
|
CreateRoomRootView(
|
||||||
state = state,
|
state = state,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -16,12 +16,14 @@
|
||||||
|
|
||||||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
|
||||||
package io.element.android.features.createroom.root
|
package io.element.android.features.createroom.impl.root
|
||||||
|
|
||||||
import app.cash.molecule.RecompositionClock
|
import app.cash.molecule.RecompositionClock
|
||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.features.createroom.impl.root.CreateRoomRootEvents
|
||||||
|
import io.element.android.features.createroom.impl.root.CreateRoomRootPresenter
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -14,19 +14,14 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.logout
|
plugins {
|
||||||
|
id("io.element.android-library")
|
||||||
import org.junit.Assert.assertEquals
|
}
|
||||||
import org.junit.Test
|
|
||||||
|
android {
|
||||||
/**
|
namespace = "io.element.android.features.login.api"
|
||||||
* Example local unit test, which will execute on the development machine (host).
|
}
|
||||||
*
|
|
||||||
* See [testing documentation](http://d.android.com/tools/testing).
|
dependencies {
|
||||||
*/
|
implementation(projects.libraries.architecture)
|
||||||
class ExampleUnitTest {
|
|
||||||
@Test
|
|
||||||
fun addition_isCorrect() {
|
|
||||||
assertEquals(4, 2 + 2)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -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.SimpleFeatureEntryPoint
|
||||||
|
|
||||||
|
interface LoginEntryPoint : SimpleFeatureEntryPoint
|
||||||
|
|
@ -18,13 +18,13 @@
|
||||||
@Suppress("DSL_SCOPE_VIOLATION")
|
@Suppress("DSL_SCOPE_VIOLATION")
|
||||||
plugins {
|
plugins {
|
||||||
id("io.element.android-compose-library")
|
id("io.element.android-compose-library")
|
||||||
alias(libs.plugins.ksp)
|
|
||||||
alias(libs.plugins.anvil)
|
alias(libs.plugins.anvil)
|
||||||
|
alias(libs.plugins.ksp)
|
||||||
id("kotlin-parcelize")
|
id("kotlin-parcelize")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "io.element.android.features.login"
|
namespace = "io.element.android.features.login.impl"
|
||||||
|
|
||||||
testOptions {
|
testOptions {
|
||||||
unitTests {
|
unitTests {
|
||||||
|
|
@ -47,6 +47,7 @@ dependencies {
|
||||||
implementation(projects.libraries.elementresources)
|
implementation(projects.libraries.elementresources)
|
||||||
implementation(projects.libraries.testtags)
|
implementation(projects.libraries.testtags)
|
||||||
implementation(projects.libraries.uiStrings)
|
implementation(projects.libraries.uiStrings)
|
||||||
|
api(projects.features.login.api)
|
||||||
ksp(libs.showkase.processor)
|
ksp(libs.showkase.processor)
|
||||||
|
|
||||||
testImplementation(libs.test.junit)
|
testImplementation(libs.test.junit)
|
||||||
|
|
@ -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.login.impl
|
||||||
|
|
||||||
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
import com.bumble.appyx.core.node.Node
|
||||||
|
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 createNode(parentNode: Node, buildContext: BuildContext): Node {
|
||||||
|
return parentNode.createNode<LoginFlowNode>(buildContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login
|
package io.element.android.features.login.impl
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -22,32 +22,33 @@ import androidx.compose.ui.Modifier
|
||||||
import com.bumble.appyx.core.composable.Children
|
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.ParentNode
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||||
import io.element.android.features.login.changeserver.ChangeServerNode
|
import dagger.assisted.Assisted
|
||||||
import io.element.android.features.login.root.LoginRootNode
|
import dagger.assisted.AssistedInject
|
||||||
|
import io.element.android.anvilannotations.ContributesNode
|
||||||
|
import io.element.android.features.login.impl.changeserver.ChangeServerNode
|
||||||
|
import io.element.android.features.login.impl.root.LoginRootNode
|
||||||
|
import io.element.android.libraries.architecture.BackstackNode
|
||||||
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
|
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.AppScope
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
class LoginFlowNode(
|
@ContributesNode(AppScope::class)
|
||||||
buildContext: BuildContext,
|
class LoginFlowNode @AssistedInject constructor(
|
||||||
private val backstack: BackStack<NavTarget> = BackStack(
|
@Assisted buildContext: BuildContext,
|
||||||
|
@Assisted plugins: List<Plugin>,
|
||||||
|
) : BackstackNode<LoginFlowNode.NavTarget>(
|
||||||
|
backstack = BackStack(
|
||||||
initialElement = NavTarget.Root,
|
initialElement = NavTarget.Root,
|
||||||
savedStateMap = buildContext.savedStateMap,
|
savedStateMap = buildContext.savedStateMap,
|
||||||
),
|
),
|
||||||
) : ParentNode<LoginFlowNode.NavTarget>(
|
buildContext = buildContext,
|
||||||
navModel = backstack,
|
plugins = plugins,
|
||||||
buildContext = buildContext
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val loginRootCallback = object : LoginRootNode.Callback {
|
|
||||||
override fun onChangeHomeServer() {
|
|
||||||
backstack.push(NavTarget.ChangeServer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed interface NavTarget : Parcelable {
|
sealed interface NavTarget : Parcelable {
|
||||||
@Parcelize
|
@Parcelize
|
||||||
object Root : NavTarget
|
object Root : NavTarget
|
||||||
|
|
@ -58,7 +59,14 @@ class LoginFlowNode(
|
||||||
|
|
||||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||||
return when (navTarget) {
|
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)
|
NavTarget.ChangeServer -> createNode<ChangeServerNode>(buildContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.changeserver
|
package io.element.android.features.login.impl.changeserver
|
||||||
|
|
||||||
sealed interface ChangeServerEvents {
|
sealed interface ChangeServerEvents {
|
||||||
data class SetServer(val server: String) : ChangeServerEvents
|
data class SetServer(val server: String) : ChangeServerEvents
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.changeserver
|
package io.element.android.features.login.impl.changeserver
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
|
@ -28,7 +28,7 @@ import com.bumble.appyx.core.plugin.Plugin
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.anvilannotations.ContributesNode
|
import io.element.android.anvilannotations.ContributesNode
|
||||||
import io.element.android.features.login.util.LoginConstants
|
import io.element.android.features.login.impl.util.LoginConstants
|
||||||
import io.element.android.libraries.core.data.tryOrNull
|
import io.element.android.libraries.core.data.tryOrNull
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.changeserver
|
package io.element.android.features.login.impl.changeserver
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
|
|
@ -22,7 +22,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import io.element.android.features.login.util.LoginConstants
|
import io.element.android.features.login.impl.util.LoginConstants
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.execute
|
import io.element.android.libraries.architecture.execute
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.changeserver
|
package io.element.android.features.login.impl.changeserver
|
||||||
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.changeserver
|
package io.element.android.features.login.impl.changeserver
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.changeserver
|
package io.element.android.features.login.impl.changeserver
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
|
@ -45,7 +45,6 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
|
||||||
import androidx.compose.ui.text.ExperimentalTextApi
|
import androidx.compose.ui.text.ExperimentalTextApi
|
||||||
import androidx.compose.ui.text.ParagraphStyle
|
import androidx.compose.ui.text.ParagraphStyle
|
||||||
import androidx.compose.ui.text.SpanStyle
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
|
@ -58,20 +57,20 @@ import androidx.compose.ui.text.withStyle
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.features.login.R
|
import io.element.android.features.login.impl.R
|
||||||
import io.element.android.features.login.error.changeServerError
|
import io.element.android.features.login.impl.error.changeServerError
|
||||||
import io.element.android.features.login.util.LoginConstants
|
import io.element.android.features.login.impl.util.LoginConstants
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.designsystem.ElementTextStyles
|
import io.element.android.libraries.designsystem.ElementTextStyles
|
||||||
import io.element.android.libraries.designsystem.LinkColor
|
import io.element.android.libraries.designsystem.LinkColor
|
||||||
import io.element.android.libraries.designsystem.components.ClickableLinkText
|
import io.element.android.libraries.designsystem.components.ClickableLinkText
|
||||||
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
||||||
import io.element.android.libraries.designsystem.components.form.textFieldState
|
import io.element.android.libraries.designsystem.components.form.textFieldState
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||||
import io.element.android.libraries.designsystem.theme.LocalColors
|
import io.element.android.libraries.designsystem.theme.LocalColors
|
||||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.Button
|
import io.element.android.libraries.designsystem.theme.components.Button
|
||||||
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
||||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||||
|
|
@ -81,7 +80,6 @@ import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.designsystem.theme.components.TextField
|
import io.element.android.libraries.designsystem.theme.components.TextField
|
||||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||||
import io.element.android.libraries.designsystem.theme.components.onTabOrEnterKeyFocusNext
|
import io.element.android.libraries.designsystem.theme.components.onTabOrEnterKeyFocusNext
|
||||||
import io.element.android.libraries.designsystem.theme.roomListRoomName
|
|
||||||
import io.element.android.libraries.testtags.TestTags
|
import io.element.android.libraries.testtags.TestTags
|
||||||
import io.element.android.libraries.testtags.testTag
|
import io.element.android.libraries.testtags.testTag
|
||||||
import org.matrix.rustcomponents.sdk.AuthenticationException
|
import org.matrix.rustcomponents.sdk.AuthenticationException
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.error
|
package io.element.android.features.login.impl.error
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.api.auth.AuthErrorCode
|
import io.element.android.libraries.matrix.api.auth.AuthErrorCode
|
||||||
import io.element.android.libraries.matrix.api.auth.errorCode
|
import io.element.android.libraries.matrix.api.auth.errorCode
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.root
|
package io.element.android.features.login.impl.root
|
||||||
|
|
||||||
sealed interface LoginRootEvents {
|
sealed interface LoginRootEvents {
|
||||||
data class SetLogin(val login: String) : LoginRootEvents
|
data class SetLogin(val login: String) : LoginRootEvents
|
||||||
|
|
@ -14,11 +14,10 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.root
|
package io.element.android.features.login.impl.root
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
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
|
||||||
|
|
@ -26,7 +25,6 @@ import com.bumble.appyx.core.plugin.plugins
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.anvilannotations.ContributesNode
|
import io.element.android.anvilannotations.ContributesNode
|
||||||
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
|
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
|
|
@ -47,7 +45,7 @@ class LoginRootNode @AssistedInject constructor(
|
||||||
@Composable
|
@Composable
|
||||||
override fun View(modifier: Modifier) {
|
override fun View(modifier: Modifier) {
|
||||||
val state = presenter.present()
|
val state = presenter.present()
|
||||||
LoginRootScreen(
|
LoginRootView(
|
||||||
state = state,
|
state = state,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onChangeServer = this::onChangeHomeServer,
|
onChangeServer = this::onChangeHomeServer,
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.root
|
package io.element.android.features.login.impl.root
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
|
|
@ -23,14 +23,12 @@ import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import io.element.android.features.login.util.LoginConstants
|
import io.element.android.features.login.impl.util.LoginConstants
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.data.tryOrNull
|
import io.element.android.libraries.core.data.tryOrNull
|
||||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||||
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
|
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
@ -87,5 +85,4 @@ class LoginRootPresenter @Inject constructor(private val authenticationService:
|
||||||
private fun updateFormState(formState: MutableState<LoginFormState>, updateLambda: LoginFormState.() -> LoginFormState) {
|
private fun updateFormState(formState: MutableState<LoginFormState>, updateLambda: LoginFormState.() -> LoginFormState) {
|
||||||
formState.value = updateLambda(formState.value)
|
formState.value = updateLambda(formState.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.root
|
package io.element.android.features.login.impl.root
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
|
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.root
|
package io.element.android.features.login.impl.root
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
|
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.root
|
package io.element.android.features.login.impl.root
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
|
@ -62,7 +62,7 @@ import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.features.login.error.loginError
|
import io.element.android.features.login.impl.error.loginError
|
||||||
import io.element.android.libraries.designsystem.ElementTextStyles
|
import io.element.android.libraries.designsystem.ElementTextStyles
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
||||||
import io.element.android.libraries.designsystem.components.form.textFieldState
|
import io.element.android.libraries.designsystem.components.form.textFieldState
|
||||||
|
|
@ -85,7 +85,7 @@ import io.element.android.libraries.ui.strings.R as StringR
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun LoginRootScreen(
|
fun LoginRootView(
|
||||||
state: LoginRootState,
|
state: LoginRootState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onChangeServer: () -> Unit = {},
|
onChangeServer: () -> Unit = {},
|
||||||
|
|
@ -349,7 +349,7 @@ internal fun LoginRootScreenDarkPreview(@PreviewParameter(LoginRootStateProvider
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ContentToPreview(state: LoginRootState) {
|
private fun ContentToPreview(state: LoginRootState) {
|
||||||
LoginRootScreen(
|
LoginRootView(
|
||||||
state = state,
|
state = state,
|
||||||
onBackPressed = {}
|
onBackPressed = {}
|
||||||
)
|
)
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.util
|
package io.element.android.features.login.impl.util
|
||||||
|
|
||||||
object LoginConstants {
|
object LoginConstants {
|
||||||
|
|
||||||
|
|
@ -16,12 +16,14 @@
|
||||||
|
|
||||||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
|
||||||
package io.element.android.features.login.changeserver
|
package io.element.android.features.login.impl.changeserver
|
||||||
|
|
||||||
import app.cash.molecule.RecompositionClock
|
import app.cash.molecule.RecompositionClock
|
||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.features.login.impl.changeserver.ChangeServerEvents
|
||||||
|
import io.element.android.features.login.impl.changeserver.ChangeServerPresenter
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.matrix.test.A_HOMESERVER
|
import io.element.android.libraries.matrix.test.A_HOMESERVER
|
||||||
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
||||||
|
|
@ -14,11 +14,11 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login.error
|
package io.element.android.features.login.impl.error
|
||||||
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import org.junit.Test
|
|
||||||
import io.element.android.libraries.ui.strings.R
|
import io.element.android.libraries.ui.strings.R
|
||||||
|
import org.junit.Test
|
||||||
import org.matrix.rustcomponents.sdk.AuthenticationException
|
import org.matrix.rustcomponents.sdk.AuthenticationException
|
||||||
|
|
||||||
class ErrorFormatterTests {
|
class ErrorFormatterTests {
|
||||||
|
|
@ -16,12 +16,16 @@
|
||||||
|
|
||||||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
|
||||||
package io.element.android.features.login.root
|
package io.element.android.features.login.impl.root
|
||||||
|
|
||||||
import app.cash.molecule.RecompositionClock
|
import app.cash.molecule.RecompositionClock
|
||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.features.login.impl.root.LoggedInState
|
||||||
|
import io.element.android.features.login.impl.root.LoginFormState
|
||||||
|
import io.element.android.features.login.impl.root.LoginRootEvents
|
||||||
|
import io.element.android.features.login.impl.root.LoginRootPresenter
|
||||||
import io.element.android.libraries.matrix.test.A_HOMESERVER
|
import io.element.android.libraries.matrix.test.A_HOMESERVER
|
||||||
import io.element.android.libraries.matrix.test.A_PASSWORD
|
import io.element.android.libraries.matrix.test.A_PASSWORD
|
||||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||||
21
features/login/proguard-rules.pro
vendored
21
features/login/proguard-rules.pro
vendored
|
|
@ -1,21 +0,0 @@
|
||||||
# Add project specific ProGuard rules here.
|
|
||||||
# You can control the set of applied configuration files using the
|
|
||||||
# proguardFiles setting in build.gradle.kts.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
|
||||||
# debugging stack traces.
|
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
|
||||||
# hide the original source file name.
|
|
||||||
#-renamesourcefileattribute SourceFile
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 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
|
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instrumented test, which will execute on an Android device.
|
|
||||||
*
|
|
||||||
* See [testing documentation](http://d.android.com/tools/testing).
|
|
||||||
*/
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
class ExampleInstrumentedTest {
|
|
||||||
@Test
|
|
||||||
fun useAppContext() {
|
|
||||||
// Context of the app under test.
|
|
||||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
|
||||||
assertEquals("io.element.android.features.login.test", appContext.packageName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
~ Copyright (c) 2022 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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<manifest>
|
|
||||||
|
|
||||||
</manifest>
|
|
||||||
1
features/logout/.gitignore
vendored
1
features/logout/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
||||||
/build
|
|
||||||
29
features/logout/api/build.gradle.kts
Normal file
29
features/logout/api/build.gradle.kts
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("io.element.android-compose-library")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "io.element.android.features.logout.api"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(projects.libraries.architecture)
|
||||||
|
implementation(projects.libraries.designsystem)
|
||||||
|
implementation(projects.libraries.uiStrings)
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.logout
|
package io.element.android.features.logout.api
|
||||||
|
|
||||||
sealed interface LogoutPreferenceEvents {
|
sealed interface LogoutPreferenceEvents {
|
||||||
object Logout : LogoutPreferenceEvents
|
object Logout : LogoutPreferenceEvents
|
||||||
|
|
@ -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.logout.api
|
||||||
|
|
||||||
|
import io.element.android.libraries.architecture.Presenter
|
||||||
|
|
||||||
|
interface LogoutPreferencePresenter : Presenter<LogoutPreferenceState>
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.logout
|
package io.element.android.features.logout.api
|
||||||
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Logout
|
import androidx.compose.material.icons.filled.Logout
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.logout
|
package io.element.android.features.logout.api
|
||||||
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.logout
|
package io.element.android.features.logout.api
|
||||||
|
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
|
|
||||||
|
|
@ -18,12 +18,12 @@
|
||||||
@Suppress("DSL_SCOPE_VIOLATION")
|
@Suppress("DSL_SCOPE_VIOLATION")
|
||||||
plugins {
|
plugins {
|
||||||
id("io.element.android-compose-library")
|
id("io.element.android-compose-library")
|
||||||
alias(libs.plugins.ksp)
|
|
||||||
alias(libs.plugins.anvil)
|
alias(libs.plugins.anvil)
|
||||||
|
alias(libs.plugins.ksp)
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "io.element.android.features.logout"
|
namespace = "io.element.android.features.logout.impl"
|
||||||
}
|
}
|
||||||
|
|
||||||
anvil {
|
anvil {
|
||||||
|
|
@ -33,12 +33,14 @@ anvil {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(projects.anvilannotations)
|
implementation(projects.anvilannotations)
|
||||||
anvil(projects.anvilcodegen)
|
anvil(projects.anvilcodegen)
|
||||||
implementation(projects.libraries.architecture)
|
|
||||||
implementation(projects.libraries.core)
|
implementation(projects.libraries.core)
|
||||||
|
implementation(projects.libraries.architecture)
|
||||||
implementation(projects.libraries.matrix.api)
|
implementation(projects.libraries.matrix.api)
|
||||||
implementation(projects.libraries.designsystem)
|
implementation(projects.libraries.designsystem)
|
||||||
implementation(projects.libraries.elementresources)
|
implementation(projects.libraries.elementresources)
|
||||||
|
implementation(projects.libraries.testtags)
|
||||||
implementation(projects.libraries.uiStrings)
|
implementation(projects.libraries.uiStrings)
|
||||||
|
api(projects.features.logout.api)
|
||||||
ksp(libs.showkase.processor)
|
ksp(libs.showkase.processor)
|
||||||
|
|
||||||
testImplementation(libs.test.junit)
|
testImplementation(libs.test.junit)
|
||||||
|
|
@ -15,6 +15,6 @@
|
||||||
~ limitations under the License.
|
~ limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<manifest>
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
@ -14,23 +14,28 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.logout
|
package io.element.android.features.logout.impl
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
|
import io.element.android.features.logout.api.LogoutPreferenceEvents
|
||||||
|
import io.element.android.features.logout.api.LogoutPreferencePresenter
|
||||||
|
import io.element.android.features.logout.api.LogoutPreferenceState
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.architecture.Presenter
|
|
||||||
import io.element.android.libraries.architecture.execute
|
import io.element.android.libraries.architecture.execute
|
||||||
|
import io.element.android.libraries.di.SessionScope
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class LogoutPreferencePresenter @Inject constructor(private val matrixClient: MatrixClient) :
|
@ContributesBinding(SessionScope::class)
|
||||||
Presenter<LogoutPreferenceState> {
|
class DefaultLogoutPreferencePresenter @Inject constructor(private val matrixClient: MatrixClient) :
|
||||||
|
LogoutPreferencePresenter {
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): LogoutPreferenceState {
|
override fun present(): LogoutPreferenceState {
|
||||||
|
|
@ -16,12 +16,14 @@
|
||||||
|
|
||||||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
|
||||||
package io.element.android.features.logout
|
package io.element.android.features.logout.impl
|
||||||
|
|
||||||
import app.cash.molecule.RecompositionClock
|
import app.cash.molecule.RecompositionClock
|
||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.features.logout.api.LogoutPreferenceEvents
|
||||||
|
import io.element.android.features.logout.api.LogoutPreferenceState
|
||||||
import io.element.android.libraries.architecture.Async
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||||
import io.element.android.libraries.matrix.test.A_THROWABLE
|
import io.element.android.libraries.matrix.test.A_THROWABLE
|
||||||
|
|
@ -33,7 +35,7 @@ import org.junit.Test
|
||||||
class LogoutPreferencePresenterTest {
|
class LogoutPreferencePresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state`() = runTest {
|
fun `present - initial state`() = runTest {
|
||||||
val presenter = LogoutPreferencePresenter(
|
val presenter = DefaultLogoutPreferencePresenter(
|
||||||
FakeMatrixClient(A_SESSION_ID),
|
FakeMatrixClient(A_SESSION_ID),
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionClock.Immediate) {
|
moleculeFlow(RecompositionClock.Immediate) {
|
||||||
|
|
@ -46,7 +48,7 @@ class LogoutPreferencePresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - logout`() = runTest {
|
fun `present - logout`() = runTest {
|
||||||
val presenter = LogoutPreferencePresenter(
|
val presenter = DefaultLogoutPreferencePresenter(
|
||||||
FakeMatrixClient(A_SESSION_ID),
|
FakeMatrixClient(A_SESSION_ID),
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionClock.Immediate) {
|
moleculeFlow(RecompositionClock.Immediate) {
|
||||||
|
|
@ -64,7 +66,7 @@ class LogoutPreferencePresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - logout with error`() = runTest {
|
fun `present - logout with error`() = runTest {
|
||||||
val matrixClient = FakeMatrixClient(A_SESSION_ID)
|
val matrixClient = FakeMatrixClient(A_SESSION_ID)
|
||||||
val presenter = LogoutPreferencePresenter(
|
val presenter = DefaultLogoutPreferencePresenter(
|
||||||
matrixClient,
|
matrixClient,
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionClock.Immediate) {
|
moleculeFlow(RecompositionClock.Immediate) {
|
||||||
21
features/logout/proguard-rules.pro
vendored
21
features/logout/proguard-rules.pro
vendored
|
|
@ -1,21 +0,0 @@
|
||||||
# Add project specific ProGuard rules here.
|
|
||||||
# You can control the set of applied configuration files using the
|
|
||||||
# proguardFiles setting in build.gradle.kts.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
|
||||||
# debugging stack traces.
|
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
|
||||||
# hide the original source file name.
|
|
||||||
#-renamesourcefileattribute SourceFile
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 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.logout
|
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instrumented test, which will execute on an Android device.
|
|
||||||
*
|
|
||||||
* See [testing documentation](http://d.android.com/tools/testing).
|
|
||||||
*/
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
class ExampleInstrumentedTest {
|
|
||||||
@Test
|
|
||||||
fun useAppContext() {
|
|
||||||
// Context of the app under test.
|
|
||||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
|
||||||
assertEquals("io.element.android.features.login.test", appContext.packageName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
~ Copyright (c) 2022 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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<manifest>
|
|
||||||
|
|
||||||
</manifest>
|
|
||||||
1
features/messages/.gitignore
vendored
1
features/messages/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
||||||
/build
|
|
||||||
|
|
@ -14,19 +14,16 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.login
|
// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed
|
||||||
|
@Suppress("DSL_SCOPE_VIOLATION")
|
||||||
import org.junit.Assert.assertEquals
|
plugins {
|
||||||
import org.junit.Test
|
id("io.element.android-library")
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Example local unit test, which will execute on the development machine (host).
|
android {
|
||||||
*
|
namespace = "io.element.android.features.messages.api"
|
||||||
* See [testing documentation](http://d.android.com/tools/testing).
|
}
|
||||||
*/
|
|
||||||
class ExampleUnitTest {
|
dependencies {
|
||||||
@Test
|
implementation(projects.libraries.architecture)
|
||||||
fun addition_isCorrect() {
|
|
||||||
assertEquals(4, 2 + 2)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -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.messages.api
|
||||||
|
|
||||||
|
import io.element.android.libraries.architecture.SimpleFeatureEntryPoint
|
||||||
|
|
||||||
|
interface MessagesEntryPoint : SimpleFeatureEntryPoint
|
||||||
|
|
@ -18,12 +18,12 @@
|
||||||
@Suppress("DSL_SCOPE_VIOLATION")
|
@Suppress("DSL_SCOPE_VIOLATION")
|
||||||
plugins {
|
plugins {
|
||||||
id("io.element.android-compose-library")
|
id("io.element.android-compose-library")
|
||||||
alias(libs.plugins.ksp)
|
|
||||||
alias(libs.plugins.anvil)
|
alias(libs.plugins.anvil)
|
||||||
|
alias(libs.plugins.ksp)
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "io.element.android.features.messages"
|
namespace = "io.element.android.features.messages.impl"
|
||||||
}
|
}
|
||||||
|
|
||||||
anvil {
|
anvil {
|
||||||
|
|
@ -33,6 +33,7 @@ anvil {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(projects.anvilannotations)
|
implementation(projects.anvilannotations)
|
||||||
anvil(projects.anvilcodegen)
|
anvil(projects.anvilcodegen)
|
||||||
|
api(projects.features.messages.api)
|
||||||
implementation(projects.libraries.core)
|
implementation(projects.libraries.core)
|
||||||
implementation(projects.libraries.architecture)
|
implementation(projects.libraries.architecture)
|
||||||
implementation(projects.libraries.matrix.api)
|
implementation(projects.libraries.matrix.api)
|
||||||
|
|
@ -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.messages.impl
|
||||||
|
|
||||||
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
import com.bumble.appyx.core.node.Node
|
||||||
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
|
import io.element.android.features.messages.api.MessagesEntryPoint
|
||||||
|
import io.element.android.libraries.architecture.createNode
|
||||||
|
import io.element.android.libraries.di.AppScope
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ContributesBinding(AppScope::class)
|
||||||
|
class DefaultMessagesEntryPoint @Inject constructor() : MessagesEntryPoint {
|
||||||
|
override fun createNode(parentNode: Node, buildContext: BuildContext): Node {
|
||||||
|
return parentNode.createNode<MessagesNode>(buildContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,10 +14,10 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages
|
package io.element.android.features.messages.impl
|
||||||
|
|
||||||
import io.element.android.features.messages.actionlist.model.TimelineItemAction
|
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||||
import io.element.android.features.messages.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
|
|
||||||
sealed interface MessagesEvents {
|
sealed interface MessagesEvents {
|
||||||
data class HandleAction(val action: TimelineItemAction, val event: TimelineItem.Event) : MessagesEvents
|
data class HandleAction(val action: TimelineItemAction, val event: TimelineItem.Event) : MessagesEvents
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages
|
package io.element.android.features.messages.impl
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.messages
|
package io.element.android.features.messages.impl
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
|
@ -24,15 +24,15 @@ import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import io.element.android.features.messages.actionlist.ActionListPresenter
|
import io.element.android.features.messages.impl.actionlist.ActionListPresenter
|
||||||
import io.element.android.features.messages.actionlist.model.TimelineItemAction
|
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||||
import io.element.android.features.messages.textcomposer.MessageComposerEvents
|
import io.element.android.features.messages.impl.textcomposer.MessageComposerEvents
|
||||||
import io.element.android.features.messages.textcomposer.MessageComposerPresenter
|
import io.element.android.features.messages.impl.textcomposer.MessageComposerPresenter
|
||||||
import io.element.android.features.messages.textcomposer.MessageComposerState
|
import io.element.android.features.messages.impl.textcomposer.MessageComposerState
|
||||||
import io.element.android.features.messages.timeline.TimelineEvents
|
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||||
import io.element.android.features.messages.timeline.TimelinePresenter
|
import io.element.android.features.messages.impl.timeline.TimelinePresenter
|
||||||
import io.element.android.features.messages.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
import io.element.android.features.messages.timeline.model.event.TimelineItemTextBasedContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue