diff --git a/app/src/main/kotlin/dev/meloda/fast/MainViewModel.kt b/app/src/main/kotlin/dev/meloda/fast/MainViewModel.kt index 8670d011..be50f87b 100644 --- a/app/src/main/kotlin/dev/meloda/fast/MainViewModel.kt +++ b/app/src/main/kotlin/dev/meloda/fast/MainViewModel.kt @@ -86,6 +86,8 @@ class MainViewModelImpl( BaseError.SessionExpired -> { isNeedToReplaceWithAuth.update { true } } + + is BaseError.SimpleError -> Unit // TODO: 21-Mar-25, Danil Nikolaev: show error in ui } } diff --git a/core/model/src/main/kotlin/dev/meloda/fast/model/BaseError.kt b/core/model/src/main/kotlin/dev/meloda/fast/model/BaseError.kt index b2a8cb64..86cc6e2f 100644 --- a/core/model/src/main/kotlin/dev/meloda/fast/model/BaseError.kt +++ b/core/model/src/main/kotlin/dev/meloda/fast/model/BaseError.kt @@ -6,4 +6,6 @@ import androidx.compose.runtime.Immutable sealed class BaseError { data object SessionExpired : BaseError() + + data class SimpleError(val message: String) : BaseError() } diff --git a/core/model/src/main/kotlin/dev/meloda/fast/model/api/data/VkVideoData.kt b/core/model/src/main/kotlin/dev/meloda/fast/model/api/data/VkVideoData.kt index 8e9f3d5d..01bbfa3b 100644 --- a/core/model/src/main/kotlin/dev/meloda/fast/model/api/data/VkVideoData.kt +++ b/core/model/src/main/kotlin/dev/meloda/fast/model/api/data/VkVideoData.kt @@ -1,7 +1,7 @@ package dev.meloda.fast.model.api.data -import dev.meloda.fast.model.api.domain.VkVideoDomain import com.squareup.moshi.JsonClass +import dev.meloda.fast.model.api.domain.VkVideoDomain @JsonClass(generateAdapter = true) data class VkVideoData( @@ -12,7 +12,7 @@ data class VkVideoData( val duration: Int, val date: Int, val comments: Int?, - val description: String, + val description: String?, val player: String?, val added: Int?, val type: String, @@ -20,9 +20,9 @@ data class VkVideoData( val access_key: String?, val owner_id: Int, val is_favorite: Boolean?, - val image: List, + val image: List?, val first_frame: List?, - val files: File?, + val files: File? ) : VkAttachmentData { @JsonClass(generateAdapter = true) @@ -67,7 +67,7 @@ data class VkVideoData( fun toDomain() = VkVideoDomain( id = id, ownerId = owner_id, - images = image.map { it.asVideoImage() }, + images = image.orEmpty().map { it.asVideoImage() }, firstFrames = first_frame, accessKey = access_key, title = title diff --git a/core/network/src/main/kotlin/dev/meloda/fast/network/ResponseConverterFactory.kt b/core/network/src/main/kotlin/dev/meloda/fast/network/ResponseConverterFactory.kt index d4af413e..3168077d 100644 --- a/core/network/src/main/kotlin/dev/meloda/fast/network/ResponseConverterFactory.kt +++ b/core/network/src/main/kotlin/dev/meloda/fast/network/ResponseConverterFactory.kt @@ -46,8 +46,13 @@ class ResponseConverterFactory(private val converter: JsonConverter) : Converter return successModel }, onFailure = { failure -> - if(failure is JsonDataException) { - throw failure + if (failure is JsonDataException) { + throw ApiException( + RestApiError( + errorCode = -1, + errorMsg = failure.message.orEmpty() + ) + ) } val isUnit = successType == Unit::class.java diff --git a/core/network/src/main/kotlin/dev/meloda/fast/network/VkErrorCode.kt b/core/network/src/main/kotlin/dev/meloda/fast/network/VkErrorCode.kt index 42aaae91..b15d2734 100644 --- a/core/network/src/main/kotlin/dev/meloda/fast/network/VkErrorCode.kt +++ b/core/network/src/main/kotlin/dev/meloda/fast/network/VkErrorCode.kt @@ -1,6 +1,7 @@ package dev.meloda.fast.network enum class VkErrorCode(val code: Int) { + WTF(-1), UNKNOWN_ERROR(1), APP_DISABLED(2), UNKNOWN_METHOD(3), diff --git a/core/ui/src/main/kotlin/dev/meloda/fast/ui/components/ErrorView.kt b/core/ui/src/main/kotlin/dev/meloda/fast/ui/components/ErrorView.kt index 1d43f793..32b3db31 100644 --- a/core/ui/src/main/kotlin/dev/meloda/fast/ui/components/ErrorView.kt +++ b/core/ui/src/main/kotlin/dev/meloda/fast/ui/components/ErrorView.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -22,7 +23,9 @@ fun ErrorView( onButtonClick: (() -> Unit)? = null, ) { Column( - modifier = modifier.fillMaxSize(), + modifier = modifier + .fillMaxSize() + .padding(horizontal = 16.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { diff --git a/feature/chatmaterials/src/main/kotlin/dev/meloda/fast/chatmaterials/presentation/ChatMaterialsScreen.kt b/feature/chatmaterials/src/main/kotlin/dev/meloda/fast/chatmaterials/presentation/ChatMaterialsScreen.kt index bc73daef..f7acddf4 100644 --- a/feature/chatmaterials/src/main/kotlin/dev/meloda/fast/chatmaterials/presentation/ChatMaterialsScreen.kt +++ b/feature/chatmaterials/src/main/kotlin/dev/meloda/fast/chatmaterials/presentation/ChatMaterialsScreen.kt @@ -62,8 +62,8 @@ import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import dev.chrisbanes.haze.HazeState -import dev.chrisbanes.haze.haze -import dev.chrisbanes.haze.hazeChild +import dev.chrisbanes.haze.hazeEffect +import dev.chrisbanes.haze.hazeSource import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi import dev.chrisbanes.haze.materials.HazeMaterials import dev.meloda.fast.chatmaterials.ChatMaterialsViewModel @@ -137,7 +137,7 @@ fun ChatMaterialsScreen( ) } - val titles = listOf("Photos", "Videos", "Audios", "Files", "Links") + val titles = listOf("Photos", "Videos", "Audios")//, "Files", "Links") val listState = rememberLazyListState() val gridState = rememberLazyGridState() @@ -179,7 +179,7 @@ fun ChatMaterialsScreen( modifier = Modifier .then( if (currentTheme.enableBlur) { - Modifier.hazeChild( + Modifier.hazeEffect( state = hazeState, style = hazeStyle ) @@ -311,7 +311,7 @@ fun ChatMaterialsScreen( modifier = Modifier .then( if (currentTheme.enableBlur) { - Modifier.haze(state = hazeState) + Modifier.hazeSource(state = hazeState) } else { Modifier } @@ -346,7 +346,7 @@ fun ChatMaterialsScreen( modifier = Modifier .then( if (currentTheme.enableBlur) { - Modifier.haze(state = hazeState) + Modifier.hazeSource(state = hazeState) } else { Modifier } diff --git a/feature/conversations/src/main/kotlin/dev/meloda/fast/conversations/ConversationsViewModel.kt b/feature/conversations/src/main/kotlin/dev/meloda/fast/conversations/ConversationsViewModel.kt index 35a4fad3..5806134d 100644 --- a/feature/conversations/src/main/kotlin/dev/meloda/fast/conversations/ConversationsViewModel.kt +++ b/feature/conversations/src/main/kotlin/dev/meloda/fast/conversations/ConversationsViewModel.kt @@ -134,6 +134,7 @@ class ConversationsViewModelImpl( } override fun onRefresh() { + baseError.setValue { null } loadConversations(offset = 0) } @@ -273,17 +274,7 @@ class ConversationsViewModelImpl( conversationsUseCase.getConversations(count = LOAD_COUNT, offset = offset) .listenValue(viewModelScope) { state -> state.processState( - error = { error -> - if (error is State.Error.ApiError) { - when (error.errorCode) { - VkErrorCode.USER_AUTHORIZATION_FAILED -> { - baseError.setValue { BaseError.SessionExpired } - } - - else -> Unit - } - } - }, + error = ::handleError, success = { response -> val itemsCountSufficient = response.size == LOAD_COUNT canPaginate.setValue { itemsCountSufficient } @@ -339,6 +330,40 @@ class ConversationsViewModelImpl( } } + private fun handleError(error: State.Error) { + when (error) { + is State.Error.ApiError -> { + when (error.errorCode) { + VkErrorCode.USER_AUTHORIZATION_FAILED -> { + baseError.setValue { BaseError.SessionExpired } + } + + else -> { + baseError.setValue { + BaseError.SimpleError(message = error.errorMessage) + } + } + } + } + State.Error.ConnectionError -> { + baseError.setValue { + BaseError.SimpleError(message = "Connection error") + } + } + State.Error.InternalError -> { + baseError.setValue { + BaseError.SimpleError(message = "Internal error") + } + } + State.Error.UnknownError -> { + baseError.setValue { + BaseError.SimpleError(message = "Unknown error") + } + } + else -> Unit + } + } + private fun deleteConversation(peerId: Int) { conversationsUseCase.delete(peerId).listenValue(viewModelScope) { state -> state.processState( diff --git a/feature/conversations/src/main/kotlin/dev/meloda/fast/conversations/presentation/ConversationsScreen.kt b/feature/conversations/src/main/kotlin/dev/meloda/fast/conversations/presentation/ConversationsScreen.kt index cf19746b..e7c42f1a 100644 --- a/feature/conversations/src/main/kotlin/dev/meloda/fast/conversations/presentation/ConversationsScreen.kt +++ b/feature/conversations/src/main/kotlin/dev/meloda/fast/conversations/presentation/ConversationsScreen.kt @@ -339,12 +339,24 @@ fun ConversationsScreen( } ) { padding -> when { - baseError is BaseError.SessionExpired -> { - ErrorView( - text = "Session expired", - buttonText = "Log out", - onButtonClick = onSessionExpiredLogOutButtonClicked - ) + baseError != null -> { + when (baseError) { + is BaseError.SessionExpired -> { + ErrorView( + text = "Session expired", + buttonText = "Log out", + onButtonClick = onSessionExpiredLogOutButtonClicked + ) + } + + is BaseError.SimpleError -> { + ErrorView( + text = baseError.message, + buttonText = "Try again", + onButtonClick = onRefresh + ) + } + } } screenState.isLoading && screenState.conversations.isEmpty() -> FullScreenLoader()