forked from melod1n/fast-messenger
refactor: simplify app bootstrap and root errors
This commit is contained in:
@@ -2,7 +2,6 @@ package dev.meloda.fast
|
|||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.Log
|
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.os.LocaleListCompat
|
import androidx.core.os.LocaleListCompat
|
||||||
@@ -38,6 +37,7 @@ interface MainViewModel {
|
|||||||
val startDestination: StateFlow<Any?>
|
val startDestination: StateFlow<Any?>
|
||||||
val isNeedToReplaceWithAuth: StateFlow<Boolean>
|
val isNeedToReplaceWithAuth: StateFlow<Boolean>
|
||||||
val currentUser: StateFlow<VkUser?>
|
val currentUser: StateFlow<VkUser?>
|
||||||
|
val baseError: StateFlow<BaseError?>
|
||||||
|
|
||||||
val isNeedToShowNotificationsDeniedDialog: StateFlow<Boolean>
|
val isNeedToShowNotificationsDeniedDialog: StateFlow<Boolean>
|
||||||
val isNeedToShowNotificationsRationaleDialog: StateFlow<Boolean>
|
val isNeedToShowNotificationsRationaleDialog: StateFlow<Boolean>
|
||||||
@@ -45,6 +45,7 @@ interface MainViewModel {
|
|||||||
val isNeedToRequestNotifications: StateFlow<Boolean>
|
val isNeedToRequestNotifications: StateFlow<Boolean>
|
||||||
|
|
||||||
fun onError(error: BaseError)
|
fun onError(error: BaseError)
|
||||||
|
fun onErrorConsumed()
|
||||||
|
|
||||||
fun onNavigatedToAuth()
|
fun onNavigatedToAuth()
|
||||||
|
|
||||||
@@ -73,6 +74,7 @@ class MainViewModelImpl(
|
|||||||
override val startDestination = MutableStateFlow<Any?>(null)
|
override val startDestination = MutableStateFlow<Any?>(null)
|
||||||
override val isNeedToReplaceWithAuth = MutableStateFlow(false)
|
override val isNeedToReplaceWithAuth = MutableStateFlow(false)
|
||||||
override val currentUser = MutableStateFlow<VkUser?>(null)
|
override val currentUser = MutableStateFlow<VkUser?>(null)
|
||||||
|
override val baseError = MutableStateFlow<BaseError?>(null)
|
||||||
|
|
||||||
override val isNeedToShowNotificationsDeniedDialog = MutableStateFlow(false)
|
override val isNeedToShowNotificationsDeniedDialog = MutableStateFlow(false)
|
||||||
override val isNeedToShowNotificationsRationaleDialog = MutableStateFlow(false)
|
override val isNeedToShowNotificationsRationaleDialog = MutableStateFlow(false)
|
||||||
@@ -93,9 +95,15 @@ class MainViewModelImpl(
|
|||||||
isNeedToReplaceWithAuth.update { true }
|
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() {
|
override fun onNavigatedToAuth() {
|
||||||
isNeedToReplaceWithAuth.update { false }
|
isNeedToReplaceWithAuth.update { false }
|
||||||
@@ -207,8 +215,6 @@ class MainViewModelImpl(
|
|||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val currentAccount = getCurrentAccountUseCase()
|
val currentAccount = getCurrentAccountUseCase()
|
||||||
|
|
||||||
Log.d("MainViewModel", "currentAccount: $currentAccount")
|
|
||||||
|
|
||||||
if (currentAccount != null) {
|
if (currentAccount != null) {
|
||||||
UserConfig.apply {
|
UserConfig.apply {
|
||||||
this.userId = currentAccount.userId
|
this.userId = currentAccount.userId
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import android.content.res.Configuration
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.util.Log
|
|
||||||
import androidx.activity.SystemBarStyle
|
import androidx.activity.SystemBarStyle
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
@@ -65,9 +64,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
val viewModel: MainViewModel = koinViewModel<MainViewModelImpl>()
|
val viewModel: MainViewModel = koinViewModel<MainViewModelImpl>()
|
||||||
LaunchedEffect(viewModel) {
|
|
||||||
Log.d("VM_CREATE", "onCreate: viewModel: $viewModel")
|
|
||||||
}
|
|
||||||
|
|
||||||
LifecycleResumeEffect(true) {
|
LifecycleResumeEffect(true) {
|
||||||
viewModel.onAppResumed(intent)
|
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.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.util.Log
|
|
||||||
import androidx.activity.compose.LocalActivity
|
import androidx.activity.compose.LocalActivity
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
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.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@@ -92,9 +88,6 @@ fun RootScreen(
|
|||||||
val longPollStateToApply by longPollController.stateToApply.collectAsStateWithLifecycle()
|
val longPollStateToApply by longPollController.stateToApply.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val viewModel: MainViewModel = koinViewModel<MainViewModelImpl>()
|
val viewModel: MainViewModel = koinViewModel<MainViewModelImpl>()
|
||||||
LaunchedEffect(viewModel) {
|
|
||||||
Log.d("VM_CREATE", "RootScreen(): viewModel: $viewModel")
|
|
||||||
}
|
|
||||||
|
|
||||||
val currentUser: VkUser? by viewModel.currentUser.collectAsStateWithLifecycle()
|
val currentUser: VkUser? by viewModel.currentUser.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
@@ -126,13 +119,11 @@ fun RootScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
LifecycleResumeEffect(longPollStateToApply) {
|
LifecycleResumeEffect(longPollStateToApply) {
|
||||||
Log.d("LongPollMainActivity", "longPollStateToApply: $longPollStateToApply")
|
|
||||||
if (longPollStateToApply != LongPollState.Background) {
|
if (longPollStateToApply != LongPollState.Background) {
|
||||||
if (longPollStateToApply.isLaunched() && longPollCurrentState.isLaunched()
|
if (longPollStateToApply.isLaunched() && longPollCurrentState.isLaunched()
|
||||||
&& longPollCurrentState != longPollStateToApply
|
&& longPollCurrentState != longPollStateToApply
|
||||||
) {
|
) {
|
||||||
toggleLongPollService(false, null)
|
toggleLongPollService(false, null)
|
||||||
Log.d("LongPoll", "recreate()")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleLongPollService(
|
toggleLongPollService(
|
||||||
@@ -241,6 +232,7 @@ fun RootScreen(
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val startDestination by viewModel.startDestination.collectAsStateWithLifecycle()
|
val startDestination by viewModel.startDestination.collectAsStateWithLifecycle()
|
||||||
val isNeedToOpenAuth by viewModel.isNeedToReplaceWithAuth.collectAsStateWithLifecycle()
|
val isNeedToOpenAuth by viewModel.isNeedToReplaceWithAuth.collectAsStateWithLifecycle()
|
||||||
|
val baseError by viewModel.baseError.collectAsStateWithLifecycle()
|
||||||
val isNeedToShowDeniedDialog by viewModel.isNeedToShowNotificationsDeniedDialog.collectAsStateWithLifecycle()
|
val isNeedToShowDeniedDialog by viewModel.isNeedToShowNotificationsDeniedDialog.collectAsStateWithLifecycle()
|
||||||
val isNeedToShowRationaleDialog by viewModel.isNeedToShowNotificationsRationaleDialog.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) {
|
if (startDestination != null) {
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalNavRootController provides navController,
|
LocalNavRootController provides navController,
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ internal fun Project.configureKotlinAndroid(
|
|||||||
|
|
||||||
commonExtension.apply {
|
commonExtension.apply {
|
||||||
compileSdk = getVersionInt("compileSdk")
|
compileSdk = getVersionInt("compileSdk")
|
||||||
|
buildToolsVersion = "36.1.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
configureKotlin<KotlinAndroidProjectExtension>()
|
configureKotlin<KotlinAndroidProjectExtension>()
|
||||||
|
|||||||
Reference in New Issue
Block a user