Merge pull request #218 from vector-im/feature/bma/appUiState
Introduce AppNavigationStateService.
This commit is contained in:
commit
0d28020d4b
16 changed files with 484 additions and 0 deletions
|
|
@ -19,6 +19,7 @@
|
||||||
import com.android.build.api.variant.FilterConfiguration.FilterType.ABI
|
import com.android.build.api.variant.FilterConfiguration.FilterType.ABI
|
||||||
import extension.allFeaturesImpl
|
import extension.allFeaturesImpl
|
||||||
import extension.allLibrariesImpl
|
import extension.allLibrariesImpl
|
||||||
|
import extension.allServicesImpl
|
||||||
|
|
||||||
// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed
|
// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed
|
||||||
@Suppress("DSL_SCOPE_VIOLATION")
|
@Suppress("DSL_SCOPE_VIOLATION")
|
||||||
|
|
@ -201,6 +202,7 @@ knit {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
allLibrariesImpl()
|
allLibrariesImpl()
|
||||||
|
allServicesImpl()
|
||||||
allFeaturesImpl()
|
allFeaturesImpl()
|
||||||
implementation(projects.tests.uitests)
|
implementation(projects.tests.uitests)
|
||||||
implementation(projects.anvilannotations)
|
implementation(projects.anvilannotations)
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,8 @@ dependencies {
|
||||||
implementation(projects.tests.uitests)
|
implementation(projects.tests.uitests)
|
||||||
implementation(libs.coil)
|
implementation(libs.coil)
|
||||||
|
|
||||||
|
implementation(projects.services.appnavstate.api)
|
||||||
|
|
||||||
testImplementation(libs.test.junit)
|
testImplementation(libs.test.junit)
|
||||||
testImplementation(libs.coroutines.test)
|
testImplementation(libs.coroutines.test)
|
||||||
testImplementation(libs.molecule.runtime)
|
testImplementation(libs.molecule.runtime)
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,10 @@ 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.AppScope
|
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.MAIN_SPACE
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.ui.di.MatrixUIBindings
|
import io.element.android.libraries.matrix.ui.di.MatrixUIBindings
|
||||||
|
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
|
|
@ -58,6 +60,7 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||||
private val roomListEntryPoint: RoomListEntryPoint,
|
private val roomListEntryPoint: RoomListEntryPoint,
|
||||||
private val preferencesEntryPoint: PreferencesEntryPoint,
|
private val preferencesEntryPoint: PreferencesEntryPoint,
|
||||||
private val createRoomEntryPoint: CreateRoomEntryPoint,
|
private val createRoomEntryPoint: CreateRoomEntryPoint,
|
||||||
|
private val appNavigationStateService: AppNavigationStateService,
|
||||||
) : BackstackNode<LoggedInFlowNode.NavTarget>(
|
) : BackstackNode<LoggedInFlowNode.NavTarget>(
|
||||||
backstack = BackStack(
|
backstack = BackStack(
|
||||||
initialElement = NavTarget.RoomList,
|
initialElement = NavTarget.RoomList,
|
||||||
|
|
@ -91,11 +94,16 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||||
val imageLoaderFactory = bindings<MatrixUIBindings>().loggedInImageLoaderFactory()
|
val imageLoaderFactory = bindings<MatrixUIBindings>().loggedInImageLoaderFactory()
|
||||||
Coil.setImageLoader(imageLoaderFactory)
|
Coil.setImageLoader(imageLoaderFactory)
|
||||||
inputs.matrixClient.startSync()
|
inputs.matrixClient.startSync()
|
||||||
|
appNavigationStateService.onNavigateToSession(inputs.matrixClient.sessionId)
|
||||||
|
// TODO We do not support Space yet, so directly navigate to main space
|
||||||
|
appNavigationStateService.onNavigateToSpace(MAIN_SPACE)
|
||||||
},
|
},
|
||||||
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) }
|
plugins<LifecycleCallback>().forEach { it.onFlowReleased(inputs.matrixClient) }
|
||||||
|
appNavigationStateService.onLeavingSpace()
|
||||||
|
appNavigationStateService.onLeavingSession()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import io.element.android.libraries.architecture.NodeInputs
|
||||||
import io.element.android.libraries.architecture.inputs
|
import io.element.android.libraries.architecture.inputs
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
|
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
|
@ -43,6 +44,7 @@ class RoomFlowNode @AssistedInject constructor(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
private val messagesEntryPoint: MessagesEntryPoint,
|
private val messagesEntryPoint: MessagesEntryPoint,
|
||||||
|
private val appNavigationStateService: AppNavigationStateService,
|
||||||
) : BackstackNode<RoomFlowNode.NavTarget>(
|
) : BackstackNode<RoomFlowNode.NavTarget>(
|
||||||
backstack = BackStack(
|
backstack = BackStack(
|
||||||
initialElement = NavTarget.Messages,
|
initialElement = NavTarget.Messages,
|
||||||
|
|
@ -68,11 +70,13 @@ class RoomFlowNode @AssistedInject constructor(
|
||||||
onCreate = {
|
onCreate = {
|
||||||
Timber.v("OnCreate")
|
Timber.v("OnCreate")
|
||||||
plugins<LifecycleCallback>().forEach { it.onFlowCreated(inputs.room) }
|
plugins<LifecycleCallback>().forEach { it.onFlowCreated(inputs.room) }
|
||||||
|
appNavigationStateService.onNavigateToRoom(inputs.room.roomId)
|
||||||
},
|
},
|
||||||
onDestroy = {
|
onDestroy = {
|
||||||
Timber.v("OnDestroy")
|
Timber.v("OnDestroy")
|
||||||
inputs.room.close()
|
inputs.room.close()
|
||||||
plugins<LifecycleCallback>().forEach { it.onFlowReleased(inputs.room) }
|
plugins<LifecycleCallback>().forEach { it.onFlowReleased(inputs.room) }
|
||||||
|
appNavigationStateService.onLeavingRoom()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.core.log.logger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent class for custom logger tags. Can be used with Timber :
|
||||||
|
*
|
||||||
|
* val loggerTag = LoggerTag("MyTag", LoggerTag.VOIP)
|
||||||
|
* Timber.tag(loggerTag.value).v("My log message")
|
||||||
|
*/
|
||||||
|
open class LoggerTag(name: String, parentTag: LoggerTag? = null) {
|
||||||
|
|
||||||
|
object SYNC : LoggerTag("SYNC")
|
||||||
|
object VOIP : LoggerTag("VOIP")
|
||||||
|
object CRYPTO : LoggerTag("CRYPTO")
|
||||||
|
object RENDEZVOUS : LoggerTag("RZ")
|
||||||
|
|
||||||
|
val value: String = if (parentTag == null) {
|
||||||
|
name
|
||||||
|
} else {
|
||||||
|
"${parentTag.value}/$name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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.libraries.matrix.api.core
|
||||||
|
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class SpaceId(val value: String) : Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value to use when no space is selected by the user.
|
||||||
|
*/
|
||||||
|
val MAIN_SPACE = SpaceId("!mainSpace")
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* 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.libraries.matrix.api.core
|
||||||
|
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class ThreadId(val value: String) : Serializable
|
||||||
|
|
@ -20,6 +20,8 @@ import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
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.SpaceId
|
||||||
|
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
|
||||||
const val A_USER_NAME = "alice"
|
const val A_USER_NAME = "alice"
|
||||||
|
|
@ -27,7 +29,9 @@ const val A_PASSWORD = "password"
|
||||||
|
|
||||||
val A_USER_ID = UserId("@alice:server.org")
|
val A_USER_ID = UserId("@alice:server.org")
|
||||||
val A_SESSION_ID = SessionId(A_USER_ID.value)
|
val A_SESSION_ID = SessionId(A_USER_ID.value)
|
||||||
|
val A_SPACE_ID = SpaceId("!aSpaceId")
|
||||||
val A_ROOM_ID = RoomId("!aRoomId")
|
val A_ROOM_ID = RoomId("!aRoomId")
|
||||||
|
val A_THREAD_ID = ThreadId("\$aThreadId")
|
||||||
val AN_EVENT_ID = EventId("\$anEventId")
|
val AN_EVENT_ID = EventId("\$anEventId")
|
||||||
|
|
||||||
const val A_ROOM_NAME = "A room name"
|
const val A_ROOM_NAME = "A room name"
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,10 @@ fun DependencyHandlerScope.allLibrariesImpl() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun DependencyHandlerScope.allServicesImpl() {
|
||||||
|
implementation(project(":services:appnavstate:impl"))
|
||||||
|
}
|
||||||
|
|
||||||
fun DependencyHandlerScope.allFeaturesApi() {
|
fun DependencyHandlerScope.allFeaturesApi() {
|
||||||
implementation(project(":features:onboarding:api"))
|
implementation(project(":features:onboarding:api"))
|
||||||
implementation(project(":features:login:api"))
|
implementation(project(":features:login:api"))
|
||||||
|
|
|
||||||
30
services/appnavstate/api/build.gradle.kts
Normal file
30
services/appnavstate/api/build.gradle.kts
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed
|
||||||
|
@Suppress("DSL_SCOPE_VIOLATION")
|
||||||
|
plugins {
|
||||||
|
id("io.element.android-library")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "io.element.android.services.appnavstate.api"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.coroutines.core)
|
||||||
|
implementation(projects.libraries.matrix.api)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.services.appnavstate.api
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
|
import io.element.android.libraries.matrix.api.core.SpaceId
|
||||||
|
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||||
|
|
||||||
|
sealed interface AppNavigationState {
|
||||||
|
object Root : AppNavigationState
|
||||||
|
|
||||||
|
data class Session(
|
||||||
|
val sessionId: SessionId,
|
||||||
|
) : AppNavigationState
|
||||||
|
|
||||||
|
data class Space(
|
||||||
|
// Can be fake value, if no space is selected
|
||||||
|
val spaceId: SpaceId,
|
||||||
|
val parentSession: Session,
|
||||||
|
) : AppNavigationState
|
||||||
|
|
||||||
|
data class Room(
|
||||||
|
val roomId: RoomId,
|
||||||
|
val parentSpace: Space,
|
||||||
|
) : AppNavigationState
|
||||||
|
|
||||||
|
data class Thread(
|
||||||
|
val threadId: ThreadId,
|
||||||
|
val parentRoom: Room,
|
||||||
|
) : AppNavigationState
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.services.appnavstate.api
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
|
import io.element.android.libraries.matrix.api.core.SpaceId
|
||||||
|
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
|
interface AppNavigationStateService {
|
||||||
|
val appNavigationStateFlow: StateFlow<AppNavigationState>
|
||||||
|
|
||||||
|
fun onNavigateToSession(sessionId: SessionId)
|
||||||
|
fun onLeavingSession()
|
||||||
|
|
||||||
|
fun onNavigateToSpace(spaceId: SpaceId)
|
||||||
|
fun onLeavingSpace()
|
||||||
|
|
||||||
|
fun onNavigateToRoom(roomId: RoomId)
|
||||||
|
fun onLeavingRoom()
|
||||||
|
|
||||||
|
fun onNavigateToThread(threadId: ThreadId)
|
||||||
|
fun onLeavingThread()
|
||||||
|
}
|
||||||
50
services/appnavstate/impl/build.gradle.kts
Normal file
50
services/appnavstate/impl/build.gradle.kts
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed
|
||||||
|
@Suppress("DSL_SCOPE_VIOLATION")
|
||||||
|
plugins {
|
||||||
|
id("io.element.android-library")
|
||||||
|
alias(libs.plugins.ksp)
|
||||||
|
alias(libs.plugins.anvil)
|
||||||
|
}
|
||||||
|
|
||||||
|
anvil {
|
||||||
|
generateDaggerFactories.set(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "io.element.android.services.appnavstate.impl"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
anvil(projects.anvilcodegen)
|
||||||
|
implementation(libs.dagger)
|
||||||
|
implementation(projects.libraries.core)
|
||||||
|
implementation(projects.libraries.di)
|
||||||
|
implementation(projects.libraries.matrix.api)
|
||||||
|
implementation(projects.anvilannotations)
|
||||||
|
|
||||||
|
implementation(libs.coroutines.core)
|
||||||
|
implementation(libs.androidx.corektx)
|
||||||
|
|
||||||
|
api(projects.services.appnavstate.api)
|
||||||
|
|
||||||
|
testImplementation(libs.test.junit)
|
||||||
|
testImplementation(libs.coroutines.test)
|
||||||
|
testImplementation(libs.test.truth)
|
||||||
|
testImplementation(projects.libraries.matrix.test)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* 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.services.appnavstate.impl
|
||||||
|
|
||||||
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
|
import io.element.android.libraries.core.log.logger.LoggerTag
|
||||||
|
import io.element.android.libraries.di.AppScope
|
||||||
|
import io.element.android.libraries.di.SingleIn
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
|
import io.element.android.libraries.matrix.api.core.SpaceId
|
||||||
|
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||||
|
import io.element.android.services.appnavstate.api.AppNavigationState
|
||||||
|
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private val loggerTag = LoggerTag("Navigation")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO This will maybe not support properly navigation using permalink.
|
||||||
|
*/
|
||||||
|
@ContributesBinding(AppScope::class)
|
||||||
|
@SingleIn(AppScope::class)
|
||||||
|
class DefaultAppNavigationStateService @Inject constructor() : AppNavigationStateService {
|
||||||
|
|
||||||
|
private val currentAppNavigationState = MutableStateFlow<AppNavigationState>(AppNavigationState.Root)
|
||||||
|
|
||||||
|
override val appNavigationStateFlow: StateFlow<AppNavigationState> = currentAppNavigationState
|
||||||
|
|
||||||
|
override fun onNavigateToSession(sessionId: SessionId) {
|
||||||
|
val currentValue = currentAppNavigationState.value
|
||||||
|
Timber.tag(loggerTag.value).d("Navigating to session $sessionId. Current state: $currentValue")
|
||||||
|
val newValue: AppNavigationState.Session = when (currentValue) {
|
||||||
|
is AppNavigationState.Session,
|
||||||
|
is AppNavigationState.Space,
|
||||||
|
is AppNavigationState.Room,
|
||||||
|
is AppNavigationState.Thread,
|
||||||
|
AppNavigationState.Root -> AppNavigationState.Session(sessionId)
|
||||||
|
}
|
||||||
|
currentAppNavigationState.value = newValue
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNavigateToSpace(spaceId: SpaceId) {
|
||||||
|
val currentValue = currentAppNavigationState.value
|
||||||
|
Timber.tag(loggerTag.value).d("Navigating to space $spaceId. Current state: $currentValue")
|
||||||
|
val newValue: AppNavigationState.Space = when (currentValue) {
|
||||||
|
AppNavigationState.Root -> error("onNavigateToSession() must be called first")
|
||||||
|
is AppNavigationState.Session -> AppNavigationState.Space(spaceId, currentValue)
|
||||||
|
is AppNavigationState.Space -> AppNavigationState.Space(spaceId, currentValue.parentSession)
|
||||||
|
is AppNavigationState.Room -> AppNavigationState.Space(spaceId, currentValue.parentSpace.parentSession)
|
||||||
|
is AppNavigationState.Thread -> AppNavigationState.Space(spaceId, currentValue.parentRoom.parentSpace.parentSession)
|
||||||
|
}
|
||||||
|
currentAppNavigationState.value = newValue
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNavigateToRoom(roomId: RoomId) {
|
||||||
|
val currentValue = currentAppNavigationState.value
|
||||||
|
Timber.tag(loggerTag.value).d("Navigating to room $roomId. Current state: $currentValue")
|
||||||
|
val newValue: AppNavigationState.Room = when (currentValue) {
|
||||||
|
AppNavigationState.Root -> error("onNavigateToSession() must be called first")
|
||||||
|
is AppNavigationState.Session -> error("onNavigateToSpace() must be called first")
|
||||||
|
is AppNavigationState.Space -> AppNavigationState.Room(roomId, currentValue)
|
||||||
|
is AppNavigationState.Room -> AppNavigationState.Room(roomId, currentValue.parentSpace)
|
||||||
|
is AppNavigationState.Thread -> AppNavigationState.Room(roomId, currentValue.parentRoom.parentSpace)
|
||||||
|
}
|
||||||
|
currentAppNavigationState.value = newValue
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNavigateToThread(threadId: ThreadId) {
|
||||||
|
val currentValue = currentAppNavigationState.value
|
||||||
|
Timber.tag(loggerTag.value).d("Navigating to thread $threadId. Current state: $currentValue")
|
||||||
|
val newValue: AppNavigationState.Thread = when (currentValue) {
|
||||||
|
AppNavigationState.Root -> error("onNavigateToSession() must be called first")
|
||||||
|
is AppNavigationState.Session -> error("onNavigateToSpace() must be called first")
|
||||||
|
is AppNavigationState.Space -> error("onNavigateToRoom() must be called first")
|
||||||
|
is AppNavigationState.Room -> AppNavigationState.Thread(threadId, currentValue)
|
||||||
|
is AppNavigationState.Thread -> AppNavigationState.Thread(threadId, currentValue.parentRoom)
|
||||||
|
}
|
||||||
|
currentAppNavigationState.value = newValue
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLeavingThread() {
|
||||||
|
val currentValue = currentAppNavigationState.value
|
||||||
|
Timber.tag(loggerTag.value).d("Leaving thread. Current state: $currentValue")
|
||||||
|
val newValue: AppNavigationState.Room = when (currentValue) {
|
||||||
|
AppNavigationState.Root -> error("onNavigateToSession() must be called first")
|
||||||
|
is AppNavigationState.Session -> error("onNavigateToSpace() must be called first")
|
||||||
|
is AppNavigationState.Space -> error("onNavigateToRoom() must be called first")
|
||||||
|
is AppNavigationState.Room -> error("onNavigateToThread() must be called first")
|
||||||
|
is AppNavigationState.Thread -> currentValue.parentRoom
|
||||||
|
}
|
||||||
|
currentAppNavigationState.value = newValue
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLeavingRoom() {
|
||||||
|
val currentValue = currentAppNavigationState.value
|
||||||
|
Timber.tag(loggerTag.value).d("Leaving room. Current state: $currentValue")
|
||||||
|
val newValue: AppNavigationState.Space = when (currentValue) {
|
||||||
|
AppNavigationState.Root -> error("onNavigateToSession() must be called first")
|
||||||
|
is AppNavigationState.Session -> error("onNavigateToSpace() must be called first")
|
||||||
|
is AppNavigationState.Space -> error("onNavigateToRoom() must be called first")
|
||||||
|
is AppNavigationState.Room -> currentValue.parentSpace
|
||||||
|
is AppNavigationState.Thread -> currentValue.parentRoom.parentSpace
|
||||||
|
}
|
||||||
|
currentAppNavigationState.value = newValue
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLeavingSpace() {
|
||||||
|
val currentValue = currentAppNavigationState.value
|
||||||
|
Timber.tag(loggerTag.value).d("Leaving space. Current state: $currentValue")
|
||||||
|
val newValue: AppNavigationState.Session = when (currentValue) {
|
||||||
|
AppNavigationState.Root -> error("onNavigateToSession() must be called first")
|
||||||
|
is AppNavigationState.Session -> error("onNavigateToSpace() must be called first")
|
||||||
|
is AppNavigationState.Space -> currentValue.parentSession
|
||||||
|
is AppNavigationState.Room -> currentValue.parentSpace.parentSession
|
||||||
|
is AppNavigationState.Thread -> currentValue.parentRoom.parentSpace.parentSession
|
||||||
|
}
|
||||||
|
currentAppNavigationState.value = newValue
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLeavingSession() {
|
||||||
|
val currentValue = currentAppNavigationState.value
|
||||||
|
Timber.tag(loggerTag.value).d("Leaving session. Current state: $currentValue")
|
||||||
|
currentAppNavigationState.value = AppNavigationState.Root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
|
||||||
|
package io.element.android.services.appnavstate.impl
|
||||||
|
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
|
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||||
|
import io.element.android.libraries.matrix.test.A_SPACE_ID
|
||||||
|
import io.element.android.libraries.matrix.test.A_THREAD_ID
|
||||||
|
import io.element.android.services.appnavstate.api.AppNavigationState
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Assert.assertThrows
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class DefaultAppNavigationStateServiceTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testNavigation() = runTest {
|
||||||
|
val service = DefaultAppNavigationStateService()
|
||||||
|
service.onNavigateToSession(A_SESSION_ID)
|
||||||
|
service.onNavigateToSpace(A_SPACE_ID)
|
||||||
|
service.onNavigateToRoom(A_ROOM_ID)
|
||||||
|
service.onNavigateToThread(A_THREAD_ID)
|
||||||
|
assertThat(service.appNavigationStateFlow.first()).isEqualTo(
|
||||||
|
AppNavigationState.Thread(
|
||||||
|
A_THREAD_ID,
|
||||||
|
AppNavigationState.Room(
|
||||||
|
A_ROOM_ID,
|
||||||
|
AppNavigationState.Space(
|
||||||
|
A_SPACE_ID,
|
||||||
|
AppNavigationState.Session(
|
||||||
|
A_SESSION_ID
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFailure() = runTest {
|
||||||
|
val service = DefaultAppNavigationStateService()
|
||||||
|
assertThrows(IllegalStateException::class.java) { service.onNavigateToSpace(A_SPACE_ID) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -65,6 +65,9 @@ include(":libraries:session-storage:api")
|
||||||
include(":libraries:session-storage:impl")
|
include(":libraries:session-storage:impl")
|
||||||
include(":libraries:session-storage:impl-memory")
|
include(":libraries:session-storage:impl-memory")
|
||||||
|
|
||||||
|
include(":services:appnavstate:api")
|
||||||
|
include(":services:appnavstate:impl")
|
||||||
|
|
||||||
include(":features:onboarding:api")
|
include(":features:onboarding:api")
|
||||||
include(":features:onboarding:impl")
|
include(":features:onboarding:impl")
|
||||||
include(":features:logout:api")
|
include(":features:logout:api")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue