From 43528748d632904c41c32d2c07b7293e199f6ecc Mon Sep 17 00:00:00 2001 From: Eugene Date: Tue, 15 Sep 2026 13:23:04 +0300 Subject: [PATCH] fix token reset --- .../presentation/GigaWearRootComponent.kt | 12 ++---- .../gigawear/presentation/chat/ChatScreen.kt | 40 +++++++++++++------ .../kotlin/ru/shadowsparky/http/HttpModule.kt | 14 ++++--- .../shadowsparky/http/domain/HttpException.kt | 4 ++ 4 files changed, 43 insertions(+), 27 deletions(-) diff --git a/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/GigaWearRootComponent.kt b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/GigaWearRootComponent.kt index 51a5132..6ba08a0 100644 --- a/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/GigaWearRootComponent.kt +++ b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/GigaWearRootComponent.kt @@ -4,8 +4,6 @@ import com.arkivanov.decompose.ComponentContext import com.arkivanov.essenty.lifecycle.coroutines.coroutineScope import com.arkivanov.essenty.lifecycle.doOnStart import kotlinx.coroutines.Job -import kotlinx.coroutines.ensureActive -import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch import kotlinx.serialization.KSerializer import org.koin.core.Koin @@ -30,12 +28,10 @@ class GigaWearRootComponent( lifecycle.doOnStart { job?.cancel() job = scope.launch { - if (tokenStorage.token.firstOrNull() != null) { - runCatching { - healthCheck.healthCheck() - }.onFailure { - ensureActive() - tokenStorage.clear() + tokenStorage.token.collect { + if (it != null) { + runCatching { healthCheck.healthCheck() } + } else { navReplace(Route.AuthScreen()) } } diff --git a/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/chat/ChatScreen.kt b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/chat/ChatScreen.kt index 95354af..6248694 100644 --- a/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/chat/ChatScreen.kt +++ b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/chat/ChatScreen.kt @@ -6,7 +6,6 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -16,12 +15,15 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign 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.CircularProgressIndicator 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.MaterialTheme import androidx.wear.compose.material3.ScreenScaffold -import androidx.wear.compose.material3.ScrollIndicator import androidx.wear.compose.material3.Text import kotlinx.coroutines.flow.collectLatest import ru.shadowsparky.gigawear.R import ru.shadowsparky.gigawear.presentation.WearErrorContent +import ru.shadowsparky.ui.components.LazyColumn import ru.shadowsparky.updater.presentation.UpdateDialog @Composable @@ -77,17 +79,16 @@ fun ChatScreen(component: ChatComponent) { } } } - val listState = rememberLazyListState() + val focusRequester = remember { FocusRequester() } - val scrollInfoProvider = remember(listState) { - ScrollInfoProvider(listState) - } + val rotaryBehavior = RotaryScrollableDefaults.behavior( + scrollableState = listState + ) AppScaffold { ScreenScaffold( - scrollInfoProvider = scrollInfoProvider, - scrollIndicator = { ScrollIndicator(listState) }, + scrollState = listState, edgeButton = { EdgeButton( onClick = { @@ -114,15 +115,24 @@ fun ChatScreen(component: ChatComponent) { } ) { contentPadding -> UpdateDialog(component.updateComponent) + LazyColumn( state = listState, - modifier = Modifier.fillMaxSize(), + modifier = Modifier + .fillMaxSize() + .requestFocusOnHierarchyActive() + .rotaryScrollable( + behavior = rotaryBehavior, + focusRequester = focusRequester + ), contentPadding = contentPadding, horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(8.dp) ) { 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, style = MaterialTheme.typography.labelMedium, @@ -144,12 +154,16 @@ fun ChatScreen(component: ChatComponent) { } if (state.isLoading) { - item { CircularProgressIndicator() } + item { + CircularProgressIndicator() + } } state.error?.let { error -> item { - WearErrorContent(error.message ?: error.toString()) + WearErrorContent( + error.message ?: error.toString() + ) } } } diff --git a/libs/http-client/src/commonMain/kotlin/ru/shadowsparky/http/HttpModule.kt b/libs/http-client/src/commonMain/kotlin/ru/shadowsparky/http/HttpModule.kt index 0790449..84b9f2f 100644 --- a/libs/http-client/src/commonMain/kotlin/ru/shadowsparky/http/HttpModule.kt +++ b/libs/http-client/src/commonMain/kotlin/ru/shadowsparky/http/HttpModule.kt @@ -16,6 +16,7 @@ import ru.shadowsparky.http.domain.LogoutAction import ru.shadowsparky.http.domain.RefreshTokenAction import ru.shadowsparky.http.domain.TokenInfo import ru.shadowsparky.http.domain.TokenStorage +import ru.shadowsparky.http.domain.isServerFailure import ru.shadowsparky.koin @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 return try { val newToken = koin.getOrNull() - ?.refresh(this.client, token) + ?.refresh(client, token) ?: return null tokenStorage.updateToken(newToken) newToken.toKtor() - } catch (e: Throwable) { - if (e is HttpException) { - koin.getOrNull()?.logout() - } + } catch (e: HttpException) { + if (e.isServerFailure()) throw e + koin.getOrNull()?.logout() null } } diff --git a/libs/http-client/src/commonMain/kotlin/ru/shadowsparky/http/domain/HttpException.kt b/libs/http-client/src/commonMain/kotlin/ru/shadowsparky/http/domain/HttpException.kt index c7d47e2..94ba2a0 100644 --- a/libs/http-client/src/commonMain/kotlin/ru/shadowsparky/http/domain/HttpException.kt +++ b/libs/http-client/src/commonMain/kotlin/ru/shadowsparky/http/domain/HttpException.kt @@ -8,3 +8,7 @@ open class HttpException(val httpCode: Int, override val message: String?) : Run class BadRequestException(msg: String) : HttpException(HttpStatusCode.BadRequest.value, msg) open class NotFoundException(msg: String) : HttpException(HttpStatusCode.NotFound.value, msg) + +fun HttpException.isServerFailure(): Boolean { + return httpCode in 500..599 +}