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(
|
||||
context: ComponentContext
|
||||
) : 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 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.chat.ChatScreen
|
||||
import ru.shadowsparky.gigawear.presentation.nav.Child
|
||||
import ru.shadowsparky.gigawear.presentation.routing.RoutingScreen
|
||||
import ru.shadowsparky.ui.nav.Nav
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
@@ -21,6 +22,7 @@ class MainActivity : ComponentActivity() {
|
||||
when (child) {
|
||||
is Child.MainChild -> ChatScreen(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.childContext
|
||||
import com.arkivanov.essenty.lifecycle.coroutines.coroutineScope
|
||||
import com.arkivanov.essenty.lifecycle.doOnStart
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
@@ -19,6 +20,7 @@ import ru.shadowsparky.chat.domain.ChatRoles
|
||||
import ru.shadowsparky.chat.domain.Message
|
||||
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
|
||||
import ru.shadowsparky.updater.presentation.UpdateComponentFactory
|
||||
@@ -26,7 +28,9 @@ import ru.shadowsparky.updater.presentation.UpdateComponentFactory
|
||||
class ChatComponent(
|
||||
private val context: ComponentContext,
|
||||
private val repository: ChatRepository,
|
||||
private val updateComponentFactory: UpdateComponentFactory
|
||||
updateComponentFactory: UpdateComponentFactory,
|
||||
private val tokenStorage: TokenStorage,
|
||||
private val navigateToRouting: () -> Unit
|
||||
) : ComponentContext by context {
|
||||
private val _state = MutableStateFlow(ChatUiState())
|
||||
val state: StateFlow<ChatUiState> = _state.asStateFlow()
|
||||
@@ -40,7 +44,16 @@ class ChatComponent(
|
||||
private val _events = Channel<ComponentEvent>(Channel.BUFFERED)
|
||||
val events = _events.receiveAsFlow()
|
||||
|
||||
init { updateComponent.checkUpdates() }
|
||||
init {
|
||||
lifecycle.doOnStart {
|
||||
updateComponent.checkUpdates(false)
|
||||
}
|
||||
scope.launch {
|
||||
tokenStorage.token.collect {
|
||||
if (it == null) navigateToRouting()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onMicClicked() {
|
||||
if (_state.value.isLoading) return
|
||||
@@ -83,7 +96,8 @@ class ChatComponent(
|
||||
@Named(Route.CHAT)
|
||||
class ChatComponentFactory(
|
||||
private val chatRepository: ChatRepository,
|
||||
private val updateComponentFactory: UpdateComponentFactory
|
||||
private val updateComponentFactory: UpdateComponentFactory,
|
||||
private val tokenStorage: TokenStorage
|
||||
) : ComponentFactory<Child, Route.ChatScreen, Route> {
|
||||
override fun create(
|
||||
route: Route.ChatScreen,
|
||||
@@ -93,8 +107,9 @@ class ChatComponentFactory(
|
||||
val component = ChatComponent(
|
||||
child,
|
||||
chatRepository,
|
||||
updateComponentFactory
|
||||
)
|
||||
updateComponentFactory,
|
||||
tokenStorage
|
||||
) { root.navReplace(Route.RoutingScreen()) }
|
||||
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.chat.ChatComponent
|
||||
import ru.shadowsparky.gigawear.presentation.routing.RoutingComponent
|
||||
|
||||
sealed class Child : ru.shadowsparky.ui.nav.Child {
|
||||
class MainChild(val component: ChatComponent) : 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
|
||||
data class AuthScreen(override val routeId: String = AUTH) : Route()
|
||||
|
||||
@Serializable
|
||||
data class RoutingScreen(override val routeId: String = ROUTING) : Route()
|
||||
|
||||
companion object {
|
||||
const val ROUTING = "routing"
|
||||
const val CHAT = "chat"
|
||||
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) {
|
||||
if (!hasSupport) return
|
||||
lastJob?.cancel()
|
||||
lastJob = scope.launch {
|
||||
interactor.checkUpdate().collect {
|
||||
isForceUpdate = (it as? UpdateState.UpdateAvailable)?.isForceUpdate ?: false
|
||||
|
||||
Reference in New Issue
Block a user