refactor: simplify app bootstrap and root errors

This commit is contained in:
Codex
2026-05-18 20:46:46 +03:00
parent 514b8859c7
commit ff8e2fdd49
5 changed files with 58 additions and 19 deletions
@@ -2,7 +2,6 @@ package dev.meloda.fast
import android.content.Intent
import android.os.Build
import android.util.Log
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.app.NotificationCompat
import androidx.core.os.LocaleListCompat
@@ -38,6 +37,7 @@ interface MainViewModel {
val startDestination: StateFlow<Any?>
val isNeedToReplaceWithAuth: StateFlow<Boolean>
val currentUser: StateFlow<VkUser?>
val baseError: StateFlow<BaseError?>
val isNeedToShowNotificationsDeniedDialog: StateFlow<Boolean>
val isNeedToShowNotificationsRationaleDialog: StateFlow<Boolean>
@@ -45,6 +45,7 @@ interface MainViewModel {
val isNeedToRequestNotifications: StateFlow<Boolean>
fun onError(error: BaseError)
fun onErrorConsumed()
fun onNavigatedToAuth()
@@ -73,6 +74,7 @@ class MainViewModelImpl(
override val startDestination = MutableStateFlow<Any?>(null)
override val isNeedToReplaceWithAuth = MutableStateFlow(false)
override val currentUser = MutableStateFlow<VkUser?>(null)
override val baseError = MutableStateFlow<BaseError?>(null)
override val isNeedToShowNotificationsDeniedDialog = MutableStateFlow(false)
override val isNeedToShowNotificationsRationaleDialog = MutableStateFlow(false)
@@ -93,10 +95,16 @@ class MainViewModelImpl(
isNeedToReplaceWithAuth.update { true }
}
else -> Unit // TODO: 21-Mar-25, Danil Nikolaev: show error in ui
else -> {
baseError.update { error }
}
}
}
override fun onErrorConsumed() {
baseError.update { null }
}
override fun onNavigatedToAuth() {
isNeedToReplaceWithAuth.update { false }
}
@@ -207,8 +215,6 @@ class MainViewModelImpl(
viewModelScope.launch(Dispatchers.IO) {
val currentAccount = getCurrentAccountUseCase()
Log.d("MainViewModel", "currentAccount: $currentAccount")
if (currentAccount != null) {
UserConfig.apply {
this.userId = currentAccount.userId
@@ -9,7 +9,6 @@ import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import androidx.activity.SystemBarStyle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
@@ -65,9 +64,6 @@ class MainActivity : AppCompatActivity() {
setContent {
val viewModel: MainViewModel = koinViewModel<MainViewModelImpl>()
LaunchedEffect(viewModel) {
Log.d("VM_CREATE", "onCreate: viewModel: $viewModel")
}
LifecycleResumeEffect(true) {
viewModel.onAppResumed(intent)
@@ -0,0 +1,38 @@
package dev.meloda.fast.presentation
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import dev.meloda.fast.model.BaseError
import dev.meloda.fast.ui.R
@Composable
fun RootErrorDialog(
baseError: BaseError?,
onDismiss: () -> Unit,
onConfirm: () -> Unit,
) {
if (baseError == null) return
val errorText = when (baseError) {
BaseError.ConnectionError -> "Connection error"
BaseError.InternalError -> "Internal error"
BaseError.UnknownError -> "Unknown error"
is BaseError.SimpleError -> baseError.message
BaseError.SessionExpired -> "Session expired"
BaseError.AccountBlocked -> "Account blocked"
}
AlertDialog(
onDismissRequest = onDismiss,
title = { Text(text = stringResource(id = R.string.warning)) },
text = { Text(text = errorText) },
confirmButton = {
TextButton(onClick = onConfirm) {
Text(text = stringResource(id = R.string.try_again))
}
}
)
}
@@ -4,16 +4,12 @@ import android.Manifest
import android.content.Intent
import android.net.Uri
import android.provider.Settings
import android.util.Log
import androidx.activity.compose.LocalActivity
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
@@ -91,10 +87,7 @@ fun RootScreen(
val longPollCurrentState by longPollController.currentState.collectAsStateWithLifecycle()
val longPollStateToApply by longPollController.stateToApply.collectAsStateWithLifecycle()
val viewModel: MainViewModel = koinViewModel<MainViewModelImpl>()
LaunchedEffect(viewModel) {
Log.d("VM_CREATE", "RootScreen(): viewModel: $viewModel")
}
val viewModel: MainViewModel = koinViewModel<MainViewModelImpl>()
val currentUser: VkUser? by viewModel.currentUser.collectAsStateWithLifecycle()
@@ -125,14 +118,12 @@ fun RootScreen(
}
}
LifecycleResumeEffect(longPollStateToApply) {
Log.d("LongPollMainActivity", "longPollStateToApply: $longPollStateToApply")
LifecycleResumeEffect(longPollStateToApply) {
if (longPollStateToApply != LongPollState.Background) {
if (longPollStateToApply.isLaunched() && longPollCurrentState.isLaunched()
&& longPollCurrentState != longPollStateToApply
) {
toggleLongPollService(false, null)
Log.d("LongPoll", "recreate()")
}
toggleLongPollService(
@@ -241,6 +232,7 @@ fun RootScreen(
val context = LocalContext.current
val startDestination by viewModel.startDestination.collectAsStateWithLifecycle()
val isNeedToOpenAuth by viewModel.isNeedToReplaceWithAuth.collectAsStateWithLifecycle()
val baseError by viewModel.baseError.collectAsStateWithLifecycle()
val isNeedToShowDeniedDialog by viewModel.isNeedToShowNotificationsDeniedDialog.collectAsStateWithLifecycle()
val isNeedToShowRationaleDialog by viewModel.isNeedToShowNotificationsRationaleDialog.collectAsStateWithLifecycle()
@@ -304,6 +296,12 @@ fun RootScreen(
)
}
RootErrorDialog(
baseError = baseError,
onDismiss = viewModel::onErrorConsumed,
onConfirm = viewModel::onErrorConsumed
)
if (startDestination != null) {
CompositionLocalProvider(
LocalNavRootController provides navController,
@@ -25,6 +25,7 @@ internal fun Project.configureKotlinAndroid(
commonExtension.apply {
compileSdk = getVersionInt("compileSdk")
buildToolsVersion = "36.1.0"
}
configureKotlin<KotlinAndroidProjectExtension>()