fix token reset

This commit is contained in:
2026-09-15 13:23:04 +03:00
parent 9203209d17
commit 43528748d6
4 changed files with 43 additions and 27 deletions
@@ -4,8 +4,6 @@ import com.arkivanov.decompose.ComponentContext
import com.arkivanov.essenty.lifecycle.coroutines.coroutineScope import com.arkivanov.essenty.lifecycle.coroutines.coroutineScope
import com.arkivanov.essenty.lifecycle.doOnStart import com.arkivanov.essenty.lifecycle.doOnStart
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
import org.koin.core.Koin import org.koin.core.Koin
@@ -30,12 +28,10 @@ class GigaWearRootComponent(
lifecycle.doOnStart { lifecycle.doOnStart {
job?.cancel() job?.cancel()
job = scope.launch { job = scope.launch {
if (tokenStorage.token.firstOrNull() != null) { tokenStorage.token.collect {
runCatching { if (it != null) {
healthCheck.healthCheck() runCatching { healthCheck.healthCheck() }
}.onFailure { } else {
ensureActive()
tokenStorage.clear()
navReplace(Route.AuthScreen()) navReplace(Route.AuthScreen())
} }
} }
@@ -6,7 +6,6 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
@@ -16,12 +15,15 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.wear.compose.foundation.ScrollInfoProvider import androidx.wear.compose.foundation.requestFocusOnHierarchyActive
import androidx.wear.compose.foundation.rotary.RotaryScrollableDefaults
import androidx.wear.compose.foundation.rotary.rotaryScrollable
import androidx.wear.compose.material3.AppScaffold import androidx.wear.compose.material3.AppScaffold
import androidx.wear.compose.material3.CircularProgressIndicator import androidx.wear.compose.material3.CircularProgressIndicator
import androidx.wear.compose.material3.EdgeButton import androidx.wear.compose.material3.EdgeButton
@@ -29,11 +31,11 @@ import androidx.wear.compose.material3.EdgeButtonSize
import androidx.wear.compose.material3.Icon import androidx.wear.compose.material3.Icon
import androidx.wear.compose.material3.MaterialTheme import androidx.wear.compose.material3.MaterialTheme
import androidx.wear.compose.material3.ScreenScaffold import androidx.wear.compose.material3.ScreenScaffold
import androidx.wear.compose.material3.ScrollIndicator
import androidx.wear.compose.material3.Text import androidx.wear.compose.material3.Text
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import ru.shadowsparky.gigawear.R import ru.shadowsparky.gigawear.R
import ru.shadowsparky.gigawear.presentation.WearErrorContent import ru.shadowsparky.gigawear.presentation.WearErrorContent
import ru.shadowsparky.ui.components.LazyColumn
import ru.shadowsparky.updater.presentation.UpdateDialog import ru.shadowsparky.updater.presentation.UpdateDialog
@Composable @Composable
@@ -77,17 +79,16 @@ fun ChatScreen(component: ChatComponent) {
} }
} }
} }
val listState = rememberLazyListState() val listState = rememberLazyListState()
val focusRequester = remember { FocusRequester() }
val scrollInfoProvider = remember(listState) { val rotaryBehavior = RotaryScrollableDefaults.behavior(
ScrollInfoProvider(listState) scrollableState = listState
} )
AppScaffold { AppScaffold {
ScreenScaffold( ScreenScaffold(
scrollInfoProvider = scrollInfoProvider, scrollState = listState,
scrollIndicator = { ScrollIndicator(listState) },
edgeButton = { edgeButton = {
EdgeButton( EdgeButton(
onClick = { onClick = {
@@ -114,15 +115,24 @@ fun ChatScreen(component: ChatComponent) {
} }
) { contentPadding -> ) { contentPadding ->
UpdateDialog(component.updateComponent) UpdateDialog(component.updateComponent)
LazyColumn( LazyColumn(
state = listState, state = listState,
modifier = Modifier.fillMaxSize(), modifier = Modifier
.fillMaxSize()
.requestFocusOnHierarchyActive()
.rotaryScrollable(
behavior = rotaryBehavior,
focusRequester = focusRequester
),
contentPadding = contentPadding, contentPadding = contentPadding,
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
item { item {
val text = state.userText ?: stringResource(R.string.input_text_to_assist) val text = state.userText
?: stringResource(R.string.input_text_to_assist)
Text( Text(
text = text, text = text,
style = MaterialTheme.typography.labelMedium, style = MaterialTheme.typography.labelMedium,
@@ -144,12 +154,16 @@ fun ChatScreen(component: ChatComponent) {
} }
if (state.isLoading) { if (state.isLoading) {
item { CircularProgressIndicator() } item {
CircularProgressIndicator()
}
} }
state.error?.let { error -> state.error?.let { error ->
item { item {
WearErrorContent(error.message ?: error.toString()) WearErrorContent(
error.message ?: error.toString()
)
} }
} }
} }
@@ -16,6 +16,7 @@ import ru.shadowsparky.http.domain.LogoutAction
import ru.shadowsparky.http.domain.RefreshTokenAction import ru.shadowsparky.http.domain.RefreshTokenAction
import ru.shadowsparky.http.domain.TokenInfo import ru.shadowsparky.http.domain.TokenInfo
import ru.shadowsparky.http.domain.TokenStorage import ru.shadowsparky.http.domain.TokenStorage
import ru.shadowsparky.http.domain.isServerFailure
import ru.shadowsparky.koin import ru.shadowsparky.koin
@Module @Module
@@ -53,18 +54,19 @@ class HttpModule {
) )
} }
private suspend fun RefreshTokensParams.refreshTokenInternal(tokenStorage: TokenStorage?): BearerTokens? { private suspend fun RefreshTokensParams.refreshTokenInternal(
tokenStorage: TokenStorage?
): BearerTokens? {
val token = tokenStorage?.token?.firstOrNull()?.refresh ?: return null val token = tokenStorage?.token?.firstOrNull()?.refresh ?: return null
return try { return try {
val newToken = koin.getOrNull<RefreshTokenAction>() val newToken = koin.getOrNull<RefreshTokenAction>()
?.refresh(this.client, token) ?.refresh(client, token)
?: return null ?: return null
tokenStorage.updateToken(newToken) tokenStorage.updateToken(newToken)
newToken.toKtor() newToken.toKtor()
} catch (e: Throwable) { } catch (e: HttpException) {
if (e is HttpException) { if (e.isServerFailure()) throw e
koin.getOrNull<LogoutAction>()?.logout() koin.getOrNull<LogoutAction>()?.logout()
}
null null
} }
} }
@@ -8,3 +8,7 @@ open class HttpException(val httpCode: Int, override val message: String?) : Run
class BadRequestException(msg: String) : HttpException(HttpStatusCode.BadRequest.value, msg) class BadRequestException(msg: String) : HttpException(HttpStatusCode.BadRequest.value, msg)
open class NotFoundException(msg: String) : HttpException(HttpStatusCode.NotFound.value, msg) open class NotFoundException(msg: String) : HttpException(HttpStatusCode.NotFound.value, msg)
fun HttpException.isServerFailure(): Boolean {
return httpCode in 500..599
}