add routing screen
This commit is contained in:
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package ru.shadowsparky.gigawear.data
|
||||||
|
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.request.get
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import org.koin.core.annotation.Factory
|
||||||
|
import ru.shadowsparky.gigawear.domain.HealthCheck
|
||||||
|
|
||||||
|
@Factory
|
||||||
|
class HealthCheckImpl(
|
||||||
|
private val httpClient: HttpClient
|
||||||
|
) : HealthCheck {
|
||||||
|
override suspend fun healthCheck() {
|
||||||
|
val response = httpClient.get("$ENDPOINT/healthCheck")
|
||||||
|
check(response.status == HttpStatusCode.OK)
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
package ru.shadowsparky.gigawear.data
|
||||||
|
|
||||||
|
import org.koin.core.annotation.Factory
|
||||||
|
import ru.shadowsparky.http.domain.LogoutAction
|
||||||
|
import ru.shadowsparky.http.domain.TokenStorage
|
||||||
|
|
||||||
|
@Factory
|
||||||
|
class LogoutActionImpl(private val tokenStorage: TokenStorage) : LogoutAction {
|
||||||
|
override suspend fun logout() { tokenStorage.clear() }
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package ru.shadowsparky.gigawear.domain
|
||||||
|
|
||||||
|
interface HealthCheck {
|
||||||
|
suspend fun healthCheck()
|
||||||
|
}
|
||||||
+1
-1
@@ -10,7 +10,7 @@ import ru.shadowsparky.ui.nav.RootComponent
|
|||||||
class GigaWearRootComponent(
|
class GigaWearRootComponent(
|
||||||
context: ComponentContext
|
context: ComponentContext
|
||||||
) : RootComponent<Route>(context) {
|
) : RootComponent<Route>(context) {
|
||||||
override val initStack: List<Route> = listOf(Route.AuthScreen())
|
override val initStack: List<Route> = listOf(Route.RoutingScreen())
|
||||||
override val serializer: KSerializer<Route> = Route.serializer()
|
override val serializer: KSerializer<Route> = Route.serializer()
|
||||||
override val koin: Koin = ru.shadowsparky.koin
|
override val koin: Koin = ru.shadowsparky.koin
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -8,6 +8,7 @@ import org.koin.android.ext.android.inject
|
|||||||
import ru.shadowsparky.gigawear.presentation.auth.AuthScreen
|
import ru.shadowsparky.gigawear.presentation.auth.AuthScreen
|
||||||
import ru.shadowsparky.gigawear.presentation.chat.ChatScreen
|
import ru.shadowsparky.gigawear.presentation.chat.ChatScreen
|
||||||
import ru.shadowsparky.gigawear.presentation.nav.Child
|
import ru.shadowsparky.gigawear.presentation.nav.Child
|
||||||
|
import ru.shadowsparky.gigawear.presentation.routing.RoutingScreen
|
||||||
import ru.shadowsparky.ui.nav.Nav
|
import ru.shadowsparky.ui.nav.Nav
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
@@ -21,6 +22,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
when (child) {
|
when (child) {
|
||||||
is Child.MainChild -> ChatScreen(child.component)
|
is Child.MainChild -> ChatScreen(child.component)
|
||||||
is Child.AuthChild -> AuthScreen(child.component)
|
is Child.AuthChild -> AuthScreen(child.component)
|
||||||
|
is Child.RoutingChild -> RoutingScreen(child.component)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-5
@@ -3,6 +3,7 @@ package ru.shadowsparky.gigawear.presentation.chat
|
|||||||
import com.arkivanov.decompose.ComponentContext
|
import com.arkivanov.decompose.ComponentContext
|
||||||
import com.arkivanov.decompose.childContext
|
import com.arkivanov.decompose.childContext
|
||||||
import com.arkivanov.essenty.lifecycle.coroutines.coroutineScope
|
import com.arkivanov.essenty.lifecycle.coroutines.coroutineScope
|
||||||
|
import com.arkivanov.essenty.lifecycle.doOnStart
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
@@ -19,6 +20,7 @@ import ru.shadowsparky.chat.domain.ChatRoles
|
|||||||
import ru.shadowsparky.chat.domain.Message
|
import ru.shadowsparky.chat.domain.Message
|
||||||
import ru.shadowsparky.gigawear.presentation.nav.Child
|
import ru.shadowsparky.gigawear.presentation.nav.Child
|
||||||
import ru.shadowsparky.gigawear.presentation.nav.Route
|
import ru.shadowsparky.gigawear.presentation.nav.Route
|
||||||
|
import ru.shadowsparky.http.domain.TokenStorage
|
||||||
import ru.shadowsparky.ui.nav.ComponentFactory
|
import ru.shadowsparky.ui.nav.ComponentFactory
|
||||||
import ru.shadowsparky.ui.nav.RouteNavigator
|
import ru.shadowsparky.ui.nav.RouteNavigator
|
||||||
import ru.shadowsparky.updater.presentation.UpdateComponentFactory
|
import ru.shadowsparky.updater.presentation.UpdateComponentFactory
|
||||||
@@ -26,7 +28,9 @@ import ru.shadowsparky.updater.presentation.UpdateComponentFactory
|
|||||||
class ChatComponent(
|
class ChatComponent(
|
||||||
private val context: ComponentContext,
|
private val context: ComponentContext,
|
||||||
private val repository: ChatRepository,
|
private val repository: ChatRepository,
|
||||||
private val updateComponentFactory: UpdateComponentFactory
|
updateComponentFactory: UpdateComponentFactory,
|
||||||
|
private val tokenStorage: TokenStorage,
|
||||||
|
private val navigateToRouting: () -> Unit
|
||||||
) : ComponentContext by context {
|
) : ComponentContext by context {
|
||||||
private val _state = MutableStateFlow(ChatUiState())
|
private val _state = MutableStateFlow(ChatUiState())
|
||||||
val state: StateFlow<ChatUiState> = _state.asStateFlow()
|
val state: StateFlow<ChatUiState> = _state.asStateFlow()
|
||||||
@@ -40,7 +44,16 @@ class ChatComponent(
|
|||||||
private val _events = Channel<ComponentEvent>(Channel.BUFFERED)
|
private val _events = Channel<ComponentEvent>(Channel.BUFFERED)
|
||||||
val events = _events.receiveAsFlow()
|
val events = _events.receiveAsFlow()
|
||||||
|
|
||||||
init { updateComponent.checkUpdates() }
|
init {
|
||||||
|
lifecycle.doOnStart {
|
||||||
|
updateComponent.checkUpdates(false)
|
||||||
|
}
|
||||||
|
scope.launch {
|
||||||
|
tokenStorage.token.collect {
|
||||||
|
if (it == null) navigateToRouting()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onMicClicked() {
|
fun onMicClicked() {
|
||||||
if (_state.value.isLoading) return
|
if (_state.value.isLoading) return
|
||||||
@@ -83,7 +96,8 @@ class ChatComponent(
|
|||||||
@Named(Route.CHAT)
|
@Named(Route.CHAT)
|
||||||
class ChatComponentFactory(
|
class ChatComponentFactory(
|
||||||
private val chatRepository: ChatRepository,
|
private val chatRepository: ChatRepository,
|
||||||
private val updateComponentFactory: UpdateComponentFactory
|
private val updateComponentFactory: UpdateComponentFactory,
|
||||||
|
private val tokenStorage: TokenStorage
|
||||||
) : ComponentFactory<Child, Route.ChatScreen, Route> {
|
) : ComponentFactory<Child, Route.ChatScreen, Route> {
|
||||||
override fun create(
|
override fun create(
|
||||||
route: Route.ChatScreen,
|
route: Route.ChatScreen,
|
||||||
@@ -93,8 +107,9 @@ class ChatComponentFactory(
|
|||||||
val component = ChatComponent(
|
val component = ChatComponent(
|
||||||
child,
|
child,
|
||||||
chatRepository,
|
chatRepository,
|
||||||
updateComponentFactory
|
updateComponentFactory,
|
||||||
)
|
tokenStorage
|
||||||
|
) { root.navReplace(Route.RoutingScreen()) }
|
||||||
return Child.MainChild(component)
|
return Child.MainChild(component)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -2,8 +2,10 @@ package ru.shadowsparky.gigawear.presentation.nav
|
|||||||
|
|
||||||
import ru.shadowsparky.gigawear.presentation.auth.AuthComponent
|
import ru.shadowsparky.gigawear.presentation.auth.AuthComponent
|
||||||
import ru.shadowsparky.gigawear.presentation.chat.ChatComponent
|
import ru.shadowsparky.gigawear.presentation.chat.ChatComponent
|
||||||
|
import ru.shadowsparky.gigawear.presentation.routing.RoutingComponent
|
||||||
|
|
||||||
sealed class Child : ru.shadowsparky.ui.nav.Child {
|
sealed class Child : ru.shadowsparky.ui.nav.Child {
|
||||||
class MainChild(val component: ChatComponent) : Child()
|
class MainChild(val component: ChatComponent) : Child()
|
||||||
class AuthChild(val component: AuthComponent) : Child()
|
class AuthChild(val component: AuthComponent) : Child()
|
||||||
|
class RoutingChild(val component: RoutingComponent) : Child()
|
||||||
}
|
}
|
||||||
|
|||||||
+4
@@ -11,7 +11,11 @@ sealed class Route : ru.shadowsparky.ui.nav.Route() {
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class AuthScreen(override val routeId: String = AUTH) : Route()
|
data class AuthScreen(override val routeId: String = AUTH) : Route()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class RoutingScreen(override val routeId: String = ROUTING) : Route()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
const val ROUTING = "routing"
|
||||||
const val CHAT = "chat"
|
const val CHAT = "chat"
|
||||||
const val AUTH = "auth"
|
const val AUTH = "auth"
|
||||||
}
|
}
|
||||||
|
|||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
package ru.shadowsparky.gigawear.presentation.routing
|
||||||
|
|
||||||
|
import com.arkivanov.decompose.ComponentContext
|
||||||
|
import com.arkivanov.essenty.lifecycle.coroutines.coroutineScope
|
||||||
|
import kotlinx.coroutines.flow.firstOrNull
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.core.annotation.Factory
|
||||||
|
import org.koin.core.annotation.Named
|
||||||
|
import ru.shadowsparky.gigawear.domain.HealthCheck
|
||||||
|
import ru.shadowsparky.gigawear.presentation.nav.Child
|
||||||
|
import ru.shadowsparky.gigawear.presentation.nav.Route
|
||||||
|
import ru.shadowsparky.http.domain.TokenStorage
|
||||||
|
import ru.shadowsparky.ui.nav.ComponentFactory
|
||||||
|
import ru.shadowsparky.ui.nav.RouteNavigator
|
||||||
|
|
||||||
|
class RoutingComponent(
|
||||||
|
private val context: ComponentContext,
|
||||||
|
private val tokenStorage: TokenStorage,
|
||||||
|
private val healthCheck: HealthCheck,
|
||||||
|
private val navigateTo: (Route) -> Unit
|
||||||
|
) : ComponentContext by context {
|
||||||
|
private val scope = coroutineScope()
|
||||||
|
|
||||||
|
init {
|
||||||
|
scope.launch {
|
||||||
|
if (tokenStorage.token.firstOrNull() != null) {
|
||||||
|
runCatching {
|
||||||
|
healthCheck.healthCheck()
|
||||||
|
navigateTo(Route.ChatScreen())
|
||||||
|
}.onFailure {
|
||||||
|
tokenStorage.clear()
|
||||||
|
navigateTo(Route.AuthScreen())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
navigateTo(Route.AuthScreen())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Factory
|
||||||
|
@Named(Route.ROUTING)
|
||||||
|
class RoutingComponentFactory(
|
||||||
|
private val tokenStorage: TokenStorage,
|
||||||
|
private val healthCheck: HealthCheck
|
||||||
|
) : ComponentFactory<Child.RoutingChild, Route.RoutingScreen, Route> {
|
||||||
|
override fun create(
|
||||||
|
route: Route.RoutingScreen,
|
||||||
|
child: ComponentContext,
|
||||||
|
root: RouteNavigator<Route>
|
||||||
|
): Child.RoutingChild {
|
||||||
|
val comp = RoutingComponent(child, tokenStorage, healthCheck) {
|
||||||
|
root.navReplace(it)
|
||||||
|
}
|
||||||
|
return Child.RoutingChild(comp)
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
package ru.shadowsparky.gigawear.presentation.routing
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.wear.compose.material3.AppScaffold
|
||||||
|
import androidx.wear.compose.material3.CircularProgressIndicator
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RoutingScreen(component: RoutingComponent) {
|
||||||
|
AppScaffold {
|
||||||
|
Box(Modifier.fillMaxSize()) {
|
||||||
|
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -49,6 +49,7 @@ class UpdateComponent(
|
|||||||
|
|
||||||
fun checkUpdates(byUser: Boolean = true) {
|
fun checkUpdates(byUser: Boolean = true) {
|
||||||
if (!hasSupport) return
|
if (!hasSupport) return
|
||||||
|
lastJob?.cancel()
|
||||||
lastJob = scope.launch {
|
lastJob = scope.launch {
|
||||||
interactor.checkUpdate().collect {
|
interactor.checkUpdate().collect {
|
||||||
isForceUpdate = (it as? UpdateState.UpdateAvailable)?.isForceUpdate ?: false
|
isForceUpdate = (it as? UpdateState.UpdateAvailable)?.isForceUpdate ?: false
|
||||||
|
|||||||
Reference in New Issue
Block a user