From c4352d71bfdd2dd082987db5d26cfa4c3592ed55 Mon Sep 17 00:00:00 2001 From: Roman Makeev Date: Mon, 9 Sep 2024 17:37:02 +0300 Subject: [PATCH 1/3] add errors to remote controls --- .../api-backend-flipper/build.gradle.kts | 29 ++++++ .../api/infrared/FlipperInfraredBackendApi.kt | 6 ++ .../internal/FlipperInfraredBackendApiImpl.kt | 97 +++++++++++++++++++ .../{ => internal}/InfraredBackendApiImpl.kt | 3 +- .../brands/impl/build.gradle.kts | 2 + .../impl/brands/composable/BrandsScreen.kt | 14 ++- .../data/BackendBrandsRepository.kt | 4 +- .../decompose/BrandsDecomposeComponent.kt | 3 +- .../internal/BrandsDecomposeComponentImpl.kt | 2 +- .../BrandsScreenDecomposeComponentImpl.kt | 8 +- .../viewmodel/BrandsListViewModel.kt | 8 +- .../categories/impl/build.gradle.kts | 2 + .../composable/DeviceCategoriesScreen.kt | 14 ++- .../data/BackendDeviceCategoriesRepository.kt | 4 +- .../decompose/DeviceCategoriesComponent.kt | 3 +- .../CategoriesScreenDecomposeComponentImpl.kt | 7 +- .../viewmodel/DeviceCategoryListViewModel.kt | 5 +- .../remote-controls/core-ui/build.gradle.kts | 1 + .../grid/remote/impl/build.gradle.kts | 2 + .../remote/composable/RemoteGridComposable.kt | 5 +- .../components/RemoteGridComposableContent.kt | 14 ++- .../composable/util/GridComponentModelExt.kt | 2 +- .../data/pages/BackendPagesRepository.kt | 4 +- .../decompose/RemoteGridComponent.kt | 4 +- .../RemoteGridScreenDecomposeComponentImpl.kt | 4 + .../mapping/GridComponentStateMapper.kt | 3 +- .../viewmodel/RemoteGridViewModel.kt | 8 +- .../grid/saved/impl/build.gradle.kts | 1 + .../main/impl/build.gradle.kts | 1 + .../remotecontrols/api/SaveTempSignalApi.kt | 1 - .../setup/impl/build.gradle.kts | 2 + .../impl/setup/composable/SetupScreen.kt | 15 ++- .../presentation/decompose/SetupComponent.kt | 3 +- .../decompose/internal/SetupComponentImpl.kt | 3 +- .../SetupScreenDecomposeComponentImpl.kt | 4 + .../viewmodel/CurrentSignalViewModel.kt | 10 +- instances/android/app/build.gradle.kts | 1 + settings.gradle.kts | 1 + 38 files changed, 254 insertions(+), 46 deletions(-) create mode 100644 components/remote-controls/api-backend-flipper/build.gradle.kts create mode 100644 components/remote-controls/api-backend-flipper/src/androidMain/kotlin/com/flipperdevices/ifrmvp/api/infrared/FlipperInfraredBackendApi.kt create mode 100644 components/remote-controls/api-backend-flipper/src/androidMain/kotlin/com/flipperdevices/ifrmvp/api/infrared/internal/FlipperInfraredBackendApiImpl.kt rename components/remote-controls/api-backend/src/commonMain/kotlin/com/flipperdevices/ifrmvp/api/infrared/{ => internal}/InfraredBackendApiImpl.kt (95%) diff --git a/components/remote-controls/api-backend-flipper/build.gradle.kts b/components/remote-controls/api-backend-flipper/build.gradle.kts new file mode 100644 index 000000000..b7753da33 --- /dev/null +++ b/components/remote-controls/api-backend-flipper/build.gradle.kts @@ -0,0 +1,29 @@ +plugins { + id("flipper.multiplatform") + id("flipper.multiplatform-dependencies") + id("kotlinx-serialization") + id("flipper.anvil") +} + +android.namespace = "com.flipperdevices.remotecontrols.api.backend.flipper" + +androidDependencies { + implementation(libs.kotlin.coroutines) + implementation(libs.kotlin.serialization.json) + implementation(projects.components.core.di) + implementation(projects.components.remoteControls.coreModel) + implementation(projects.components.remoteControls.apiBackend) + implementation(projects.components.faphub.target.api) + implementation(projects.components.faphub.errors.api) + implementation(projects.components.bridge.service.api) + implementation(projects.components.bridge.api) + implementation(projects.components.bridge.rpcinfo.api) + implementation(projects.components.bridge.rpc.api) + + implementation(libs.dagger) + implementation(libs.square.anvil.annotations) + implementation(libs.ktor.client) + implementation(libs.ktor.serialization) + implementation(libs.ktor.logging) + implementation(libs.ktor.negotiation) +} diff --git a/components/remote-controls/api-backend-flipper/src/androidMain/kotlin/com/flipperdevices/ifrmvp/api/infrared/FlipperInfraredBackendApi.kt b/components/remote-controls/api-backend-flipper/src/androidMain/kotlin/com/flipperdevices/ifrmvp/api/infrared/FlipperInfraredBackendApi.kt new file mode 100644 index 000000000..797f22895 --- /dev/null +++ b/components/remote-controls/api-backend-flipper/src/androidMain/kotlin/com/flipperdevices/ifrmvp/api/infrared/FlipperInfraredBackendApi.kt @@ -0,0 +1,6 @@ +package com.flipperdevices.ifrmvp.api.infrared + +/** + * This api will also check for [FapHubError] + */ +interface FlipperInfraredBackendApi : InfraredBackendApi diff --git a/components/remote-controls/api-backend-flipper/src/androidMain/kotlin/com/flipperdevices/ifrmvp/api/infrared/internal/FlipperInfraredBackendApiImpl.kt b/components/remote-controls/api-backend-flipper/src/androidMain/kotlin/com/flipperdevices/ifrmvp/api/infrared/internal/FlipperInfraredBackendApiImpl.kt new file mode 100644 index 000000000..333d4e6e7 --- /dev/null +++ b/components/remote-controls/api-backend-flipper/src/androidMain/kotlin/com/flipperdevices/ifrmvp/api/infrared/internal/FlipperInfraredBackendApiImpl.kt @@ -0,0 +1,97 @@ +package com.flipperdevices.ifrmvp.api.infrared.internal + +import com.flipperdevices.bridge.api.manager.ktx.state.ConnectionState +import com.flipperdevices.bridge.rpc.api.model.exceptions.NoSdCardException +import com.flipperdevices.bridge.rpcinfo.api.FlipperStorageInformationApi +import com.flipperdevices.bridge.rpcinfo.model.FlipperInformationStatus +import com.flipperdevices.bridge.rpcinfo.model.StorageStats +import com.flipperdevices.bridge.service.api.provider.FlipperServiceProvider +import com.flipperdevices.core.di.AppGraph +import com.flipperdevices.faphub.errors.api.throwable.FirmwareNotSupported +import com.flipperdevices.faphub.errors.api.throwable.FlipperNotConnected +import com.flipperdevices.faphub.target.api.FlipperTargetProviderApi +import com.flipperdevices.faphub.target.model.FlipperTarget +import com.flipperdevices.ifrmvp.api.infrared.FlipperInfraredBackendApi +import com.flipperdevices.ifrmvp.api.infrared.InfraredBackendApi +import com.flipperdevices.ifrmvp.backend.model.BrandsResponse +import com.flipperdevices.ifrmvp.backend.model.CategoriesResponse +import com.flipperdevices.ifrmvp.backend.model.IfrFileContentResponse +import com.flipperdevices.ifrmvp.backend.model.InfraredsResponse +import com.flipperdevices.ifrmvp.backend.model.PagesLayoutBackendModel +import com.flipperdevices.ifrmvp.backend.model.SignalRequestModel +import com.flipperdevices.ifrmvp.backend.model.SignalResponseModel +import com.squareup.anvil.annotations.ContributesBinding +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +@ContributesBinding(AppGraph::class, FlipperInfraredBackendApi::class) +class FlipperInfraredBackendApiImpl @Inject constructor( + private val api: InfraredBackendApi, + private val flipperTargetProviderApi: FlipperTargetProviderApi, + private val flipperServiceProvider: FlipperServiceProvider, + private val flipperStorageInformationApi: FlipperStorageInformationApi, +) : FlipperInfraredBackendApi { + private suspend fun isSdCardPresent(): Boolean { + val stats = flipperStorageInformationApi.getStorageInformationFlow() + .map { fStorageInformation -> fStorageInformation.externalStorageStatus } + .filterIsInstance>() + .map { fStatusInformation -> fStatusInformation.data } + .filterNotNull() + .first() + return stats is StorageStats.Loaded + } + + private suspend fun isDeviceConnected(): Boolean { + return flipperServiceProvider.getServiceApi() + .connectionInformationApi + .getConnectionStateFlow() + .first() is ConnectionState.Ready + } + + @Suppress("ThrowsCount", "RethrowCaughtException") + private suspend fun wrapRequest(block: suspend () -> T): T { + return try { + when (flipperTargetProviderApi.getFlipperTarget().value) { + FlipperTarget.NotConnected -> throw FlipperNotConnected() + FlipperTarget.Unsupported -> throw FirmwareNotSupported() + else -> Unit + } + if (!isDeviceConnected()) { + throw FlipperNotConnected() + } + if (!isSdCardPresent()) { + throw NoSdCardException() + } + block.invoke() + } catch (e: Throwable) { + throw e + } + } + + override suspend fun getCategories(): CategoriesResponse { + return wrapRequest { api.getCategories() } + } + + override suspend fun getManufacturers(categoryId: Long): BrandsResponse { + return wrapRequest { api.getManufacturers(categoryId) } + } + + override suspend fun getSignal(request: SignalRequestModel): SignalResponseModel { + return wrapRequest { api.getSignal(request) } + } + + override suspend fun getIfrFileContent(ifrFileId: Long): IfrFileContentResponse { + return wrapRequest { api.getIfrFileContent(ifrFileId) } + } + + override suspend fun getUiFile(ifrFileId: Long): PagesLayoutBackendModel { + return wrapRequest { api.getUiFile(ifrFileId) } + } + + override suspend fun getInfrareds(brandId: Long): InfraredsResponse { + return wrapRequest { api.getInfrareds(brandId) } + } +} diff --git a/components/remote-controls/api-backend/src/commonMain/kotlin/com/flipperdevices/ifrmvp/api/infrared/InfraredBackendApiImpl.kt b/components/remote-controls/api-backend/src/commonMain/kotlin/com/flipperdevices/ifrmvp/api/infrared/internal/InfraredBackendApiImpl.kt similarity index 95% rename from components/remote-controls/api-backend/src/commonMain/kotlin/com/flipperdevices/ifrmvp/api/infrared/InfraredBackendApiImpl.kt rename to components/remote-controls/api-backend/src/commonMain/kotlin/com/flipperdevices/ifrmvp/api/infrared/internal/InfraredBackendApiImpl.kt index ee8081b34..47c58da7b 100644 --- a/components/remote-controls/api-backend/src/commonMain/kotlin/com/flipperdevices/ifrmvp/api/infrared/InfraredBackendApiImpl.kt +++ b/components/remote-controls/api-backend/src/commonMain/kotlin/com/flipperdevices/ifrmvp/api/infrared/internal/InfraredBackendApiImpl.kt @@ -1,6 +1,7 @@ -package com.flipperdevices.ifrmvp.api.infrared +package com.flipperdevices.ifrmvp.api.infrared.internal import com.flipperdevices.core.di.AppGraph +import com.flipperdevices.ifrmvp.api.infrared.InfraredBackendApi import com.flipperdevices.ifrmvp.api.infrared.model.InfraredHost import com.flipperdevices.ifrmvp.backend.model.BrandsResponse import com.flipperdevices.ifrmvp.backend.model.CategoriesResponse diff --git a/components/remote-controls/brands/impl/build.gradle.kts b/components/remote-controls/brands/impl/build.gradle.kts index e1b640284..5df95aec0 100644 --- a/components/remote-controls/brands/impl/build.gradle.kts +++ b/components/remote-controls/brands/impl/build.gradle.kts @@ -17,9 +17,11 @@ dependencies { implementation(projects.components.core.ui.res) implementation(projects.components.remoteControls.apiBackend) + implementation(projects.components.remoteControls.apiBackendFlipper) implementation(projects.components.remoteControls.coreModel) implementation(projects.components.remoteControls.coreUi) implementation(projects.components.remoteControls.brands.api) + implementation(projects.components.faphub.errors.api) implementation(projects.components.rootscreen.api) diff --git a/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/composable/BrandsScreen.kt b/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/composable/BrandsScreen.kt index 980d4bb1f..2aaf28562 100644 --- a/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/composable/BrandsScreen.kt +++ b/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/composable/BrandsScreen.kt @@ -1,6 +1,7 @@ package com.flipperdevices.remotecontrols.impl.brands.composable import androidx.compose.animation.Crossfade +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.Scaffold import androidx.compose.runtime.Composable @@ -10,7 +11,8 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import com.flipperdevices.ifrmvp.core.ui.layout.shared.ErrorComposable +import com.flipperdevices.faphub.errors.api.FapErrorSize +import com.flipperdevices.faphub.errors.api.FapHubComposableErrorsRenderer import com.flipperdevices.ifrmvp.core.ui.layout.shared.SharedTopBar import com.flipperdevices.remotecontrols.impl.brands.composable.composable.BrandsLoadedContent import com.flipperdevices.remotecontrols.impl.brands.composable.composable.BrandsLoadingComposable @@ -20,6 +22,7 @@ import com.flipperdevices.remotecontrols.brands.impl.R as BrandsR @Composable fun BrandsScreen( brandsDecomposeComponent: BrandsDecomposeComponent, + errorsRenderer: FapHubComposableErrorsRenderer, modifier: Modifier = Modifier ) { val coroutineScope = rememberCoroutineScope() @@ -38,8 +41,13 @@ fun BrandsScreen( ) { scaffoldPaddings -> Crossfade(targetState = model) { model -> when (model) { - BrandsDecomposeComponent.Model.Error -> { - ErrorComposable(onReload = brandsDecomposeComponent::tryLoad) + is BrandsDecomposeComponent.Model.Error -> { + errorsRenderer.ComposableThrowableError( + throwable = model.throwable, + onRetry = brandsDecomposeComponent::tryLoad, + fapErrorSize = FapErrorSize.FULLSCREEN, + modifier = Modifier.fillMaxSize() + ) } is BrandsDecomposeComponent.Model.Loaded -> { diff --git a/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/data/BackendBrandsRepository.kt b/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/data/BackendBrandsRepository.kt index 2ef7e5112..1d251c1c3 100644 --- a/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/data/BackendBrandsRepository.kt +++ b/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/data/BackendBrandsRepository.kt @@ -2,7 +2,7 @@ package com.flipperdevices.remotecontrols.impl.brands.presentation.data import com.flipperdevices.core.di.AppGraph import com.flipperdevices.core.ktx.jre.FlipperDispatchers -import com.flipperdevices.ifrmvp.api.infrared.InfraredBackendApi +import com.flipperdevices.ifrmvp.api.infrared.FlipperInfraredBackendApi import com.flipperdevices.ifrmvp.backend.model.BrandModel import com.squareup.anvil.annotations.ContributesBinding import kotlinx.coroutines.withContext @@ -10,7 +10,7 @@ import javax.inject.Inject @ContributesBinding(AppGraph::class, BrandsRepository::class) class BackendBrandsRepository @Inject constructor( - private val infraredBackendApi: InfraredBackendApi, + private val infraredBackendApi: FlipperInfraredBackendApi, ) : BrandsRepository { override suspend fun fetchBrands( categoryId: Long diff --git a/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/decompose/BrandsDecomposeComponent.kt b/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/decompose/BrandsDecomposeComponent.kt index c4cdb50fb..16767a4ca 100644 --- a/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/decompose/BrandsDecomposeComponent.kt +++ b/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/decompose/BrandsDecomposeComponent.kt @@ -1,6 +1,7 @@ package com.flipperdevices.remotecontrols.impl.brands.presentation.decompose import com.arkivanov.decompose.ComponentContext +import com.flipperdevices.faphub.errors.api.throwable.FapHubError import com.flipperdevices.ifrmvp.backend.model.BrandModel import com.flipperdevices.remotecontrols.impl.brands.presentation.util.charSection import com.flipperdevices.ui.decompose.DecomposeOnBackParameter @@ -27,7 +28,7 @@ interface BrandsDecomposeComponent { sealed interface Model { data object Loading : Model - data object Error : Model + data class Error(val throwable: FapHubError) : Model class Loaded( val brands: ImmutableList, val query: String diff --git a/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/decompose/internal/BrandsDecomposeComponentImpl.kt b/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/decompose/internal/BrandsDecomposeComponentImpl.kt index 40cbbce3f..9aac00f5a 100644 --- a/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/decompose/internal/BrandsDecomposeComponentImpl.kt +++ b/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/decompose/internal/BrandsDecomposeComponentImpl.kt @@ -63,7 +63,7 @@ class BrandsDecomposeComponentImpl @AssistedInject constructor( } is BrandsListViewModel.State.Error -> { - BrandsDecomposeComponent.Model.Error + BrandsDecomposeComponent.Model.Error(pagingState.throwable) } } } diff --git a/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/decompose/internal/BrandsScreenDecomposeComponentImpl.kt b/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/decompose/internal/BrandsScreenDecomposeComponentImpl.kt index 6d76c9ce5..04ad84748 100644 --- a/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/decompose/internal/BrandsScreenDecomposeComponentImpl.kt +++ b/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/decompose/internal/BrandsScreenDecomposeComponentImpl.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.childContext import com.flipperdevices.core.di.AppGraph +import com.flipperdevices.faphub.errors.api.FapHubComposableErrorsRenderer import com.flipperdevices.remotecontrols.api.BrandsScreenDecomposeComponent import com.flipperdevices.remotecontrols.impl.brands.composable.BrandsScreen import com.flipperdevices.remotecontrols.impl.brands.presentation.decompose.BrandsDecomposeComponent @@ -11,6 +12,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import me.gulya.anvil.assisted.ContributesAssistedFactory +@Suppress("LongParameterList") @ContributesAssistedFactory(AppGraph::class, BrandsScreenDecomposeComponent.Factory::class) class BrandsScreenDecomposeComponentImpl @AssistedInject constructor( @Assisted componentContext: ComponentContext, @@ -19,6 +21,7 @@ class BrandsScreenDecomposeComponentImpl @AssistedInject constructor( @Assisted onBrandClick: (brandId: Long, brandName: String) -> Unit, @Assisted onBrandLongClick: (brandId: Long) -> Unit, brandsDecomposeComponentFactory: BrandsDecomposeComponent.Factory, + private val errorsRenderer: FapHubComposableErrorsRenderer ) : BrandsScreenDecomposeComponent(componentContext) { private val brandsComponent = brandsDecomposeComponentFactory.createBrandsComponent( componentContext = childContext("BrandsComponent"), @@ -30,6 +33,9 @@ class BrandsScreenDecomposeComponentImpl @AssistedInject constructor( @Composable override fun Render() { - BrandsScreen(brandsDecomposeComponent = brandsComponent) + BrandsScreen( + brandsDecomposeComponent = brandsComponent, + errorsRenderer = errorsRenderer + ) } } diff --git a/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/viewmodel/BrandsListViewModel.kt b/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/viewmodel/BrandsListViewModel.kt index 05c6c4b5f..2419d65ef 100644 --- a/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/viewmodel/BrandsListViewModel.kt +++ b/components/remote-controls/brands/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/brands/presentation/viewmodel/BrandsListViewModel.kt @@ -3,6 +3,8 @@ package com.flipperdevices.remotecontrols.impl.brands.presentation.viewmodel import com.flipperdevices.core.log.LogTagProvider import com.flipperdevices.core.log.error import com.flipperdevices.core.ui.lifecycle.DecomposeViewModel +import com.flipperdevices.faphub.errors.api.throwable.FapHubError +import com.flipperdevices.faphub.errors.api.throwable.toFapHubError import com.flipperdevices.ifrmvp.backend.model.BrandModel import com.flipperdevices.remotecontrols.impl.brands.presentation.data.BrandsRepository import dagger.assisted.Assisted @@ -27,7 +29,9 @@ class BrandsListViewModel @AssistedInject constructor( _state.update { State.Loading } brandsRepository.fetchBrands(categoryId) .onSuccess { _state.emit(State.Loaded(it.toImmutableList())) } - .onFailure { _state.emit(State.Error) } + .onFailure { + _state.emit(State.Error(it.toFapHubError())) + } .onFailure { throwable -> error(throwable) { "#tryLoad could not load brands" } } } @@ -38,7 +42,7 @@ class BrandsListViewModel @AssistedInject constructor( sealed interface State { data object Loading : State data class Loaded(val brands: ImmutableList) : State - data object Error : State + data class Error(val throwable: FapHubError) : State } @AssistedFactory diff --git a/components/remote-controls/categories/impl/build.gradle.kts b/components/remote-controls/categories/impl/build.gradle.kts index cd4b35bd6..bc6ace48e 100644 --- a/components/remote-controls/categories/impl/build.gradle.kts +++ b/components/remote-controls/categories/impl/build.gradle.kts @@ -15,8 +15,10 @@ dependencies { implementation(projects.components.core.ui.decompose) implementation(projects.components.core.ui.ktx) implementation(projects.components.core.ui.res) + implementation(projects.components.faphub.errors.api) implementation(projects.components.remoteControls.apiBackend) + implementation(projects.components.remoteControls.apiBackendFlipper) implementation(projects.components.remoteControls.coreModel) implementation(projects.components.remoteControls.coreUi) implementation(projects.components.remoteControls.categories.api) diff --git a/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/composable/DeviceCategoriesScreen.kt b/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/composable/DeviceCategoriesScreen.kt index 298006b5f..a5266f73a 100644 --- a/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/composable/DeviceCategoriesScreen.kt +++ b/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/composable/DeviceCategoriesScreen.kt @@ -1,6 +1,7 @@ package com.flipperdevices.remotecontrols.impl.categories.composable import androidx.compose.animation.Crossfade +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.Scaffold import androidx.compose.runtime.Composable @@ -9,7 +10,8 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import com.flipperdevices.core.ui.theme.LocalPalletV2 -import com.flipperdevices.ifrmvp.core.ui.layout.shared.ErrorComposable +import com.flipperdevices.faphub.errors.api.FapErrorSize +import com.flipperdevices.faphub.errors.api.FapHubComposableErrorsRenderer import com.flipperdevices.ifrmvp.core.ui.layout.shared.SharedTopBar import com.flipperdevices.remotecontrols.impl.categories.composable.components.DeviceCategoriesLoadedContent import com.flipperdevices.remotecontrols.impl.categories.composable.components.DeviceCategoriesLoadingContent @@ -19,6 +21,7 @@ import com.flipperdevices.remotecontrols.categories.impl.R as CategoriesR @Composable internal fun DeviceCategoriesScreen( deviceCategoriesComponent: DeviceCategoriesComponent, + errorsRenderer: FapHubComposableErrorsRenderer, modifier: Modifier = Modifier ) { val model by deviceCategoriesComponent.model.collectAsState() @@ -35,8 +38,13 @@ internal fun DeviceCategoriesScreen( ) { scaffoldPaddings -> Crossfade(model) { model -> when (model) { - DeviceCategoriesComponent.Model.Error -> { - ErrorComposable(onReload = deviceCategoriesComponent::tryLoad) + is DeviceCategoriesComponent.Model.Error -> { + errorsRenderer.ComposableThrowableError( + throwable = model.throwable, + onRetry = deviceCategoriesComponent::tryLoad, + fapErrorSize = FapErrorSize.FULLSCREEN, + modifier = Modifier.fillMaxSize() + ) } is DeviceCategoriesComponent.Model.Loaded -> { diff --git a/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/presentation/data/BackendDeviceCategoriesRepository.kt b/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/presentation/data/BackendDeviceCategoriesRepository.kt index 545bce7dd..fe5a6969c 100644 --- a/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/presentation/data/BackendDeviceCategoriesRepository.kt +++ b/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/presentation/data/BackendDeviceCategoriesRepository.kt @@ -2,7 +2,7 @@ package com.flipperdevices.remotecontrols.impl.categories.presentation.data import com.flipperdevices.core.di.AppGraph import com.flipperdevices.core.ktx.jre.FlipperDispatchers -import com.flipperdevices.ifrmvp.api.infrared.InfraredBackendApi +import com.flipperdevices.ifrmvp.api.infrared.FlipperInfraredBackendApi import com.flipperdevices.ifrmvp.backend.model.DeviceCategory import com.squareup.anvil.annotations.ContributesBinding import kotlinx.coroutines.withContext @@ -10,7 +10,7 @@ import javax.inject.Inject @ContributesBinding(AppGraph::class, DeviceCategoriesRepository::class) class BackendDeviceCategoriesRepository @Inject constructor( - private val infraredBackendApi: InfraredBackendApi, + private val infraredBackendApi: FlipperInfraredBackendApi, ) : DeviceCategoriesRepository { override suspend fun fetchCategories(): Result> = runCatching { diff --git a/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/presentation/decompose/DeviceCategoriesComponent.kt b/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/presentation/decompose/DeviceCategoriesComponent.kt index 703452673..1be4275bc 100644 --- a/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/presentation/decompose/DeviceCategoriesComponent.kt +++ b/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/presentation/decompose/DeviceCategoriesComponent.kt @@ -1,6 +1,7 @@ package com.flipperdevices.remotecontrols.impl.categories.presentation.decompose import com.arkivanov.decompose.ComponentContext +import com.flipperdevices.faphub.errors.api.throwable.FapHubError import com.flipperdevices.ifrmvp.backend.model.DeviceCategory import com.flipperdevices.ui.decompose.DecomposeOnBackParameter import kotlinx.collections.immutable.ImmutableList @@ -18,7 +19,7 @@ interface DeviceCategoriesComponent { sealed interface Model { data object Loading : Model class Loaded(val deviceTypes: ImmutableList) : Model - data object Error : Model + data class Error(val throwable: FapHubError) : Model } fun interface Factory { diff --git a/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/presentation/decompose/internal/CategoriesScreenDecomposeComponentImpl.kt b/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/presentation/decompose/internal/CategoriesScreenDecomposeComponentImpl.kt index ceb8531fc..45cfb93ca 100644 --- a/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/presentation/decompose/internal/CategoriesScreenDecomposeComponentImpl.kt +++ b/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/presentation/decompose/internal/CategoriesScreenDecomposeComponentImpl.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.childContext import com.flipperdevices.core.di.AppGraph +import com.flipperdevices.faphub.errors.api.FapHubComposableErrorsRenderer import com.flipperdevices.remotecontrols.api.CategoriesScreenDecomposeComponent import com.flipperdevices.remotecontrols.impl.categories.composable.DeviceCategoriesScreen import com.flipperdevices.remotecontrols.impl.categories.presentation.decompose.DeviceCategoriesComponent @@ -17,6 +18,7 @@ class CategoriesScreenDecomposeComponentImpl @AssistedInject constructor( @Assisted onBackClick: () -> Unit, @Assisted onCategoryClick: (categoryId: Long, categoryName: String) -> Unit, deviceCategoriesComponentFactory: DeviceCategoriesComponent.Factory, + private val errorsRenderer: FapHubComposableErrorsRenderer ) : CategoriesScreenDecomposeComponent(componentContext) { private val deviceCategoriesComponent = deviceCategoriesComponentFactory.invoke( componentContext = childContext("DeviceCategoriesComponent"), @@ -26,6 +28,9 @@ class CategoriesScreenDecomposeComponentImpl @AssistedInject constructor( @Composable override fun Render() { - DeviceCategoriesScreen(deviceCategoriesComponent = deviceCategoriesComponent) + DeviceCategoriesScreen( + deviceCategoriesComponent = deviceCategoriesComponent, + errorsRenderer = errorsRenderer + ) } } diff --git a/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/presentation/viewmodel/DeviceCategoryListViewModel.kt b/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/presentation/viewmodel/DeviceCategoryListViewModel.kt index c2cb396cc..b7ff5fc87 100644 --- a/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/presentation/viewmodel/DeviceCategoryListViewModel.kt +++ b/components/remote-controls/categories/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/categories/presentation/viewmodel/DeviceCategoryListViewModel.kt @@ -3,6 +3,7 @@ package com.flipperdevices.remotecontrols.impl.categories.presentation.viewmodel import com.flipperdevices.core.log.LogTagProvider import com.flipperdevices.core.log.error import com.flipperdevices.core.ui.lifecycle.DecomposeViewModel +import com.flipperdevices.faphub.errors.api.throwable.toFapHubError import com.flipperdevices.remotecontrols.impl.categories.presentation.data.DeviceCategoriesRepository import com.flipperdevices.remotecontrols.impl.categories.presentation.decompose.DeviceCategoriesComponent import kotlinx.collections.immutable.toImmutableList @@ -23,7 +24,9 @@ class DeviceCategoryListViewModel @Inject constructor( fun tryLoad() = viewModelScope.launch { _model.emit(DeviceCategoriesComponent.Model.Loading) deviceCategoriesRepository.fetchCategories() - .onFailure { _model.emit(DeviceCategoriesComponent.Model.Error) } + .onFailure { + _model.emit(DeviceCategoriesComponent.Model.Error(it.toFapHubError())) + } .onFailure { throwable -> error(throwable) { "#tryLoad could not fetch categories" } } .onSuccess { categories -> _model.emit(DeviceCategoriesComponent.Model.Loaded(categories.toImmutableList())) diff --git a/components/remote-controls/core-ui/build.gradle.kts b/components/remote-controls/core-ui/build.gradle.kts index 3836db6ef..64e581120 100644 --- a/components/remote-controls/core-ui/build.gradle.kts +++ b/components/remote-controls/core-ui/build.gradle.kts @@ -13,6 +13,7 @@ dependencies { implementation(projects.components.core.ui.dialog) implementation(projects.components.remoteControls.apiBackend) + implementation(projects.components.remoteControls.apiBackendFlipper) implementation(projects.components.remoteControls.coreModel) // Compose diff --git a/components/remote-controls/grid/remote/impl/build.gradle.kts b/components/remote-controls/grid/remote/impl/build.gradle.kts index 95a891c25..4dd9d37eb 100644 --- a/components/remote-controls/grid/remote/impl/build.gradle.kts +++ b/components/remote-controls/grid/remote/impl/build.gradle.kts @@ -23,6 +23,7 @@ dependencies { implementation(projects.components.infrared.api) implementation(projects.components.remoteControls.apiBackend) + implementation(projects.components.remoteControls.apiBackendFlipper) implementation(projects.components.remoteControls.coreModel) implementation(projects.components.remoteControls.coreUi) implementation(projects.components.remoteControls.grid.main.api) @@ -31,6 +32,7 @@ dependencies { implementation(projects.components.keyedit.api) implementation(projects.components.rootscreen.api) + implementation(projects.components.faphub.errors.api) // Compose implementation(libs.compose.ui) diff --git a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/RemoteGridComposable.kt b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/RemoteGridComposable.kt index 0b58e998d..1218a30ad 100644 --- a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/RemoteGridComposable.kt +++ b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/RemoteGridComposable.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import com.flipperdevices.core.ui.theme.LocalPalletV2 +import com.flipperdevices.faphub.errors.api.FapHubComposableErrorsRenderer import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi import com.flipperdevices.remotecontrols.impl.grid.remote.composable.components.RemoteGridComposableContent import com.flipperdevices.remotecontrols.impl.grid.remote.composable.components.RemoteGridTopBar @@ -20,6 +21,7 @@ import com.flipperdevices.remotecontrols.impl.grid.remote.presentation.decompose fun RemoteGridComposable( remoteGridComponent: RemoteGridComponent, flipperDispatchDialogApi: FlipperDispatchDialogApi, + errorsRenderer: FapHubComposableErrorsRenderer, modifier: Modifier = Modifier ) { val coroutineScope = rememberCoroutineScope() @@ -49,7 +51,8 @@ fun RemoteGridComposable( model = model, modifier = Modifier .padding(scaffoldPaddings) - .navigationBarsPadding() + .navigationBarsPadding(), + errorsRenderer = errorsRenderer ) } ) diff --git a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/components/RemoteGridComposableContent.kt b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/components/RemoteGridComposableContent.kt index dcf860787..2b6fb4851 100644 --- a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/components/RemoteGridComposableContent.kt +++ b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/components/RemoteGridComposableContent.kt @@ -18,7 +18,8 @@ import androidx.compose.ui.unit.dp import com.flipperdevices.core.ui.theme.LocalPallet import com.flipperdevices.core.ui.theme.LocalPalletV2 import com.flipperdevices.core.ui.theme.LocalTypography -import com.flipperdevices.ifrmvp.core.ui.layout.shared.ErrorComposable +import com.flipperdevices.faphub.errors.api.FapErrorSize +import com.flipperdevices.faphub.errors.api.FapHubComposableErrorsRenderer import com.flipperdevices.ifrmvp.core.ui.layout.shared.GridPagesContent import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi import com.flipperdevices.remotecontrols.grid.remote.impl.R @@ -29,6 +30,7 @@ import com.flipperdevices.remotecontrols.impl.grid.remote.presentation.decompose internal fun RemoteGridComposableContent( remoteGridComponent: RemoteGridComponent, flipperDispatchDialogApi: FlipperDispatchDialogApi, + errorsRenderer: FapHubComposableErrorsRenderer, model: RemoteGridComponent.Model, modifier: Modifier = Modifier ) { @@ -39,10 +41,12 @@ internal fun RemoteGridComposableContent( contentKey = { model.contentKey } ) { animatedModel -> when (animatedModel) { - RemoteGridComponent.Model.Error -> { - ErrorComposable( - desc = stringResource(R.string.empty_page), - onReload = remoteGridComponent::tryLoad + is RemoteGridComponent.Model.Error -> { + errorsRenderer.ComposableThrowableError( + throwable = animatedModel.throwable, + onRetry = remoteGridComponent::tryLoad, + fapErrorSize = FapErrorSize.FULLSCREEN, + modifier = Modifier.fillMaxSize() ) } diff --git a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/util/GridComponentModelExt.kt b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/util/GridComponentModelExt.kt index 139d9b38f..d7cb2e564 100644 --- a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/util/GridComponentModelExt.kt +++ b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/util/GridComponentModelExt.kt @@ -4,7 +4,7 @@ import com.flipperdevices.remotecontrols.impl.grid.remote.presentation.decompose internal val RemoteGridComponent.Model.contentKey: Any get() = when (this) { - RemoteGridComponent.Model.Error -> 0 + is RemoteGridComponent.Model.Error -> 0 is RemoteGridComponent.Model.Loaded -> 1 is RemoteGridComponent.Model.Loading -> 2 } diff --git a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/data/pages/BackendPagesRepository.kt b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/data/pages/BackendPagesRepository.kt index 66e1df5e8..40f692074 100644 --- a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/data/pages/BackendPagesRepository.kt +++ b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/data/pages/BackendPagesRepository.kt @@ -1,7 +1,7 @@ package com.flipperdevices.remotecontrols.impl.grid.remote.presentation.data.pages import com.flipperdevices.core.di.AppGraph -import com.flipperdevices.ifrmvp.api.infrared.InfraredBackendApi +import com.flipperdevices.ifrmvp.api.infrared.FlipperInfraredBackendApi import com.flipperdevices.ifrmvp.backend.model.toPagesLayout import com.flipperdevices.ifrmvp.model.PagesLayout import com.squareup.anvil.annotations.ContributesBinding @@ -9,7 +9,7 @@ import javax.inject.Inject @ContributesBinding(AppGraph::class, PagesRepository::class) class BackendPagesRepository @Inject constructor( - private val infraredBackendApi: InfraredBackendApi, + private val infraredBackendApi: FlipperInfraredBackendApi, ) : PagesRepository { override suspend fun fetchDefaultPageLayout( ifrFileId: Long diff --git a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/RemoteGridComponent.kt b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/RemoteGridComponent.kt index 61480cbe2..2995063df 100644 --- a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/RemoteGridComponent.kt +++ b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/RemoteGridComponent.kt @@ -1,6 +1,7 @@ package com.flipperdevices.remotecontrols.impl.grid.remote.presentation.decompose import com.arkivanov.decompose.ComponentContext +import com.flipperdevices.faphub.errors.api.throwable.FapHubError import com.flipperdevices.ifrmvp.model.IfrKeyIdentifier import com.flipperdevices.ifrmvp.model.PagesLayout import com.flipperdevices.infrared.api.InfraredConnectionApi.InfraredEmulateState @@ -9,6 +10,7 @@ import com.flipperdevices.keyedit.api.NotSavedFlipperKey import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi import com.flipperdevices.remotecontrols.api.SaveTempSignalApi import com.flipperdevices.remotecontrols.api.model.ServerRemoteControlParam +import com.flipperdevices.remotecontrols.impl.grid.remote.presentation.viewmodel.RemoteGridViewModel.State import com.flipperdevices.ui.decompose.DecomposeOnBackParameter import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.CoroutineScope @@ -50,7 +52,7 @@ interface RemoteGridComponent { ?.coerceIn(minimumValue = 0, maximumValue = 100) } - data object Error : Model + data class Error(val throwable: FapHubError) : Model val isFilesSaved: Boolean get() = this is Loaded && !this.isSavingFiles diff --git a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/internal/RemoteGridScreenDecomposeComponentImpl.kt b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/internal/RemoteGridScreenDecomposeComponentImpl.kt index 4ff53ea06..6561d27ff 100644 --- a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/internal/RemoteGridScreenDecomposeComponentImpl.kt +++ b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/internal/RemoteGridScreenDecomposeComponentImpl.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.childContext import com.flipperdevices.core.di.AppGraph +import com.flipperdevices.faphub.errors.api.FapHubComposableErrorsRenderer import com.flipperdevices.keyedit.api.NotSavedFlipperKey import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi import com.flipperdevices.remotecontrols.api.model.ServerRemoteControlParam @@ -15,6 +16,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import me.gulya.anvil.assisted.ContributesAssistedFactory +@Suppress("LongParameterList") @ContributesAssistedFactory(AppGraph::class, RemoteGridScreenDecomposeComponent.Factory::class) class RemoteGridScreenDecomposeComponentImpl @AssistedInject constructor( @Assisted componentContext: ComponentContext, @@ -23,6 +25,7 @@ class RemoteGridScreenDecomposeComponentImpl @AssistedInject constructor( @Assisted onSaveKey: (NotSavedFlipperKey) -> Unit, remoteGridComponentFactory: RemoteGridComponent.Factory, flipperDispatchDialogApiFactory: FlipperDispatchDialogApi.Factory, + private val errorsRenderer: FapHubComposableErrorsRenderer ) : RemoteGridScreenDecomposeComponent(componentContext) { private val gridComponent = remoteGridComponentFactory.invoke( componentContext = childContext("GridComponent"), @@ -36,6 +39,7 @@ class RemoteGridScreenDecomposeComponentImpl @AssistedInject constructor( override fun Render() { RemoteGridComposable( remoteGridComponent = gridComponent, + errorsRenderer = errorsRenderer, flipperDispatchDialogApi = flipperDispatchDialogApi ) } diff --git a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/mapping/GridComponentStateMapper.kt b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/mapping/GridComponentStateMapper.kt index 4c9fffcd6..08200fbc6 100644 --- a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/mapping/GridComponentStateMapper.kt +++ b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/mapping/GridComponentStateMapper.kt @@ -14,10 +14,9 @@ internal object GridComponentStateMapper { dispatchState: DispatchSignalApi.State, connectionState: InfraredConnectionApi.InfraredEmulateState ): RemoteGridComponent.Model = when (gridState) { - RemoteGridViewModel.State.Error -> RemoteGridComponent.Model.Error + is RemoteGridViewModel.State.Error -> RemoteGridComponent.Model.Error(gridState.throwable) is RemoteGridViewModel.State.Loaded -> { when (saveState) { - SaveTempSignalApi.State.Error -> RemoteGridComponent.Model.Error is SaveTempSignalApi.State.Uploading, SaveTempSignalApi.State.Uploaded, SaveTempSignalApi.State.Pending -> { diff --git a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/viewmodel/RemoteGridViewModel.kt b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/viewmodel/RemoteGridViewModel.kt index ba56d56bd..da3cda004 100644 --- a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/viewmodel/RemoteGridViewModel.kt +++ b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/viewmodel/RemoteGridViewModel.kt @@ -4,6 +4,8 @@ import com.flipperdevices.bridge.dao.api.model.FlipperFileFormat import com.flipperdevices.core.log.LogTagProvider import com.flipperdevices.core.log.error import com.flipperdevices.core.ui.lifecycle.DecomposeViewModel +import com.flipperdevices.faphub.errors.api.throwable.FapHubError +import com.flipperdevices.faphub.errors.api.throwable.toFapHubError import com.flipperdevices.ifrmvp.model.PagesLayout import com.flipperdevices.infrared.editor.core.model.InfraredRemote import com.flipperdevices.infrared.editor.core.parser.InfraredKeyParser @@ -41,13 +43,13 @@ class RemoteGridViewModel @AssistedInject constructor( viewModelScope.launch { val pagesLayout = pagesRepository.fetchDefaultPageLayout(ifrFileId = param.infraredFileId) - .onFailure { _state.emit(State.Error) } + .onFailure { _state.emit(State.Error(it.toFapHubError())) } .onFailure { throwable -> error(throwable) { "#tryLoad could not load ui model" } } .getOrNull() ?: return@launch val pagesLayoutRaw = json.encodeToString(pagesLayout) val remotesRaw = pagesRepository.fetchKeyContent(param.infraredFileId) - .onFailure { _state.emit(State.Error) } + .onFailure { _state.emit(State.Error(it.toFapHubError())) } .onFailure { throwable -> error(throwable) { "#tryLoad could not load key content" } } .getOrNull() .orEmpty() @@ -87,7 +89,7 @@ class RemoteGridViewModel @AssistedInject constructor( sealed interface State { data object Loading : State - data object Error : State + data class Error(val throwable: FapHubError) : State data class Loaded( val pagesLayout: PagesLayout, val remotes: ImmutableList, diff --git a/components/remote-controls/grid/saved/impl/build.gradle.kts b/components/remote-controls/grid/saved/impl/build.gradle.kts index 4c16559dd..60fe15166 100644 --- a/components/remote-controls/grid/saved/impl/build.gradle.kts +++ b/components/remote-controls/grid/saved/impl/build.gradle.kts @@ -33,6 +33,7 @@ dependencies { implementation(projects.components.keyscreen.api) implementation(projects.components.share.api) + implementation(projects.components.faphub.errors.api) // Compose implementation(libs.compose.ui) diff --git a/components/remote-controls/main/impl/build.gradle.kts b/components/remote-controls/main/impl/build.gradle.kts index 9c8a8f521..ce8796e70 100644 --- a/components/remote-controls/main/impl/build.gradle.kts +++ b/components/remote-controls/main/impl/build.gradle.kts @@ -24,6 +24,7 @@ dependencies { implementation(projects.components.infrared.utils) implementation(projects.components.remoteControls.apiBackend) + implementation(projects.components.remoteControls.apiBackendFlipper) implementation(projects.components.remoteControls.coreModel) implementation(projects.components.remoteControls.coreUi) implementation(projects.components.remoteControls.main.api) diff --git a/components/remote-controls/setup/api/src/main/kotlin/com/flipperdevices/remotecontrols/api/SaveTempSignalApi.kt b/components/remote-controls/setup/api/src/main/kotlin/com/flipperdevices/remotecontrols/api/SaveTempSignalApi.kt index 47082c43d..85331f82a 100644 --- a/components/remote-controls/setup/api/src/main/kotlin/com/flipperdevices/remotecontrols/api/SaveTempSignalApi.kt +++ b/components/remote-controls/setup/api/src/main/kotlin/com/flipperdevices/remotecontrols/api/SaveTempSignalApi.kt @@ -17,7 +17,6 @@ interface SaveTempSignalApi : InstanceKeeper.Instance { sealed interface State { data object Pending : State - data object Error : State data class Uploading(val progressInternal: Long, val total: Long) : State { val progressPercent: Float = if (total == 0L) 0f else progressInternal / total.toFloat() } diff --git a/components/remote-controls/setup/impl/build.gradle.kts b/components/remote-controls/setup/impl/build.gradle.kts index 4b1013749..d6f6a1d0b 100644 --- a/components/remote-controls/setup/impl/build.gradle.kts +++ b/components/remote-controls/setup/impl/build.gradle.kts @@ -29,10 +29,12 @@ dependencies { implementation(projects.components.deeplink.api) implementation(projects.components.remoteControls.apiBackend) + implementation(projects.components.remoteControls.apiBackendFlipper) implementation(projects.components.remoteControls.coreModel) implementation(projects.components.remoteControls.coreUi) implementation(projects.components.remoteControls.setup.api) + implementation(projects.components.faphub.errors.api) implementation(projects.components.rootscreen.api) // Compose diff --git a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/composable/SetupScreen.kt b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/composable/SetupScreen.kt index 352f10c39..e4763faa8 100644 --- a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/composable/SetupScreen.kt +++ b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/composable/SetupScreen.kt @@ -16,7 +16,8 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import com.flipperdevices.core.ui.theme.LocalPalletV2 -import com.flipperdevices.ifrmvp.core.ui.layout.shared.ErrorComposable +import com.flipperdevices.faphub.errors.api.FapErrorSize +import com.flipperdevices.faphub.errors.api.FapHubComposableErrorsRenderer import com.flipperdevices.ifrmvp.core.ui.layout.shared.SharedTopBar import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi import com.flipperdevices.remotecontrols.impl.setup.composable.components.AnimatedConfirmContent @@ -31,7 +32,7 @@ import com.flipperdevices.remotecontrols.setup.impl.R as SetupR private val SetupComponent.Model.key: Any get() = when (this) { - SetupComponent.Model.Error -> "error" + is SetupComponent.Model.Error -> "error" is SetupComponent.Model.Loaded -> "loaded" is SetupComponent.Model.Loading -> "loading" } @@ -40,6 +41,7 @@ private val SetupComponent.Model.key: Any @Composable fun SetupScreen( setupComponent: SetupComponent, + errorsRenderer: FapHubComposableErrorsRenderer, flipperDispatchDialogApi: FlipperDispatchDialogApi, modifier: Modifier = Modifier ) { @@ -77,8 +79,13 @@ fun SetupScreen( contentKey = { it.key } ) { model -> when (model) { - SetupComponent.Model.Error -> { - ErrorComposable(onReload = setupComponent::onSuccessClick) + is SetupComponent.Model.Error -> { + errorsRenderer.ComposableThrowableError( + throwable = model.throwable, + onRetry = setupComponent::tryLoad, + fapErrorSize = FapErrorSize.FULLSCREEN, + modifier = Modifier.fillMaxSize() + ) } is SetupComponent.Model.Loaded -> { diff --git a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/SetupComponent.kt b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/SetupComponent.kt index e5903509a..56d1ae54e 100644 --- a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/SetupComponent.kt +++ b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/SetupComponent.kt @@ -1,6 +1,7 @@ package com.flipperdevices.remotecontrols.impl.setup.presentation.decompose import com.arkivanov.decompose.ComponentContext +import com.flipperdevices.faphub.errors.api.throwable.FapHubError import com.flipperdevices.ifrmvp.backend.model.IfrFileModel import com.flipperdevices.ifrmvp.backend.model.SignalResponse import com.flipperdevices.ifrmvp.backend.model.SignalResponseModel @@ -48,7 +49,7 @@ interface SetupComponent { val isConnected = connectionState != InfraredEmulateState.NOT_CONNECTED } - data object Error : Model + data class Error(val throwable: FapHubError) : Model } interface Factory { diff --git a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/internal/SetupComponentImpl.kt b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/internal/SetupComponentImpl.kt index 1ab37962f..d4a5b9c98 100644 --- a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/internal/SetupComponentImpl.kt +++ b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/internal/SetupComponentImpl.kt @@ -103,10 +103,9 @@ class SetupComponentImpl @AssistedInject constructor( transform = { signalState, saveState, dispatchState, connectionState -> val emulatingState = (dispatchState as? DispatchSignalApi.State.Emulating) when (signalState) { - CurrentSignalViewModel.State.Error -> SetupComponent.Model.Error + is CurrentSignalViewModel.State.Error -> SetupComponent.Model.Error(signalState.throwable) is CurrentSignalViewModel.State.Loaded -> { when (saveState) { - SaveTempSignalApi.State.Error -> SetupComponent.Model.Error is SaveTempSignalApi.State.Uploading, SaveTempSignalApi.State.Uploaded, SaveTempSignalApi.State.Pending -> SetupComponent.Model.Loaded( diff --git a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/internal/SetupScreenDecomposeComponentImpl.kt b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/internal/SetupScreenDecomposeComponentImpl.kt index 847f148db..2256c8b5d 100644 --- a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/internal/SetupScreenDecomposeComponentImpl.kt +++ b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/internal/SetupScreenDecomposeComponentImpl.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.childContext import com.flipperdevices.core.di.AppGraph +import com.flipperdevices.faphub.errors.api.FapHubComposableErrorsRenderer import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi import com.flipperdevices.remotecontrols.api.SetupScreenDecomposeComponent import com.flipperdevices.remotecontrols.impl.setup.composable.SetupScreen @@ -12,6 +13,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import me.gulya.anvil.assisted.ContributesAssistedFactory +@Suppress("LongParameterList") @ContributesAssistedFactory(AppGraph::class, SetupScreenDecomposeComponent.Factory::class) class SetupScreenDecomposeComponentImpl @AssistedInject constructor( @Assisted componentContext: ComponentContext, @@ -20,6 +22,7 @@ class SetupScreenDecomposeComponentImpl @AssistedInject constructor( @Assisted onIrFileReady: (id: Long) -> Unit, setupComponentFactory: SetupComponent.Factory, flipperDispatchDialogApiFactory: FlipperDispatchDialogApi.Factory, + private val errorsRenderer: FapHubComposableErrorsRenderer, ) : SetupScreenDecomposeComponent(componentContext) { private val setupComponent = setupComponentFactory.createSetupComponent( componentContext = childContext("SetupComponent"), @@ -33,6 +36,7 @@ class SetupScreenDecomposeComponentImpl @AssistedInject constructor( override fun Render() { SetupScreen( setupComponent = setupComponent, + errorsRenderer = errorsRenderer, flipperDispatchDialogApi = flipperDispatchDialogApi ) } diff --git a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/viewmodel/CurrentSignalViewModel.kt b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/viewmodel/CurrentSignalViewModel.kt index 8349db1d8..2651b486b 100644 --- a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/viewmodel/CurrentSignalViewModel.kt +++ b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/viewmodel/CurrentSignalViewModel.kt @@ -3,7 +3,9 @@ package com.flipperdevices.remotecontrols.impl.setup.presentation.viewmodel import com.flipperdevices.core.log.LogTagProvider import com.flipperdevices.core.log.error import com.flipperdevices.core.ui.lifecycle.DecomposeViewModel -import com.flipperdevices.ifrmvp.api.infrared.InfraredBackendApi +import com.flipperdevices.faphub.errors.api.throwable.FapHubError +import com.flipperdevices.faphub.errors.api.throwable.toFapHubError +import com.flipperdevices.ifrmvp.api.infrared.FlipperInfraredBackendApi import com.flipperdevices.ifrmvp.backend.model.SignalRequestModel import com.flipperdevices.ifrmvp.backend.model.SignalRequestModel.SignalResultData import com.flipperdevices.ifrmvp.backend.model.SignalResponseModel @@ -16,7 +18,7 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch class CurrentSignalViewModel @AssistedInject constructor( - private val infraredBackendApi: InfraredBackendApi, + private val infraredBackendApi: FlipperInfraredBackendApi, @Assisted private val param: SetupScreenDecomposeComponent.Param, @Assisted private val onLoaded: (SignalResponseModel) -> Unit ) : DecomposeViewModel(), LogTagProvider { @@ -40,7 +42,7 @@ class CurrentSignalViewModel @AssistedInject constructor( infraredBackendApi.getSignal(request) } result - .onFailure { _state.emit(State.Error) } + .onFailure { _state.emit(State.Error(it.toFapHubError())) } .onFailure { throwable -> error(throwable) { "#tryLoad could not load signal model" } } .onSuccess { _state.emit(State.Loaded(it)) } .onSuccess(onLoaded) @@ -52,7 +54,7 @@ class CurrentSignalViewModel @AssistedInject constructor( sealed interface State { data object Loading : State - data object Error : State + data class Error(val throwable: FapHubError) : State data class Loaded(val response: SignalResponseModel) : State } diff --git a/instances/android/app/build.gradle.kts b/instances/android/app/build.gradle.kts index 05bb80dfe..ed819e462 100644 --- a/instances/android/app/build.gradle.kts +++ b/instances/android/app/build.gradle.kts @@ -43,6 +43,7 @@ dependencies { implementation(projects.components.filemanager.impl) implementation(projects.components.remoteControls.apiBackend) + implementation(projects.components.remoteControls.apiBackendFlipper) implementation(projects.components.remoteControls.coreModel) implementation(projects.components.remoteControls.coreUi) implementation(projects.components.remoteControls.brands.api) diff --git a/settings.gradle.kts b/settings.gradle.kts index b7453cbdd..077c03932 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -281,6 +281,7 @@ include( ":components:notification:noop", ":components:remote-controls:api-backend", + ":components:remote-controls:api-backend-flipper", ":components:remote-controls:core-model", ":components:remote-controls:core-ui", ":components:remote-controls:main:impl", From a33b9beb288a3c18e67e088c70e12304c66bb192 Mon Sep 17 00:00:00 2001 From: Roman Makeev Date: Mon, 9 Sep 2024 17:37:32 +0300 Subject: [PATCH 2/3] update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 513cb017f..ac5002b3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Attention: don't forget to add the flag for F-Droid before release - [Feature] Skip infrared signals on setup screen - [Feature] Better user-ux when configuring remote control - [Feature] Add flipper action dialogs into remote control and move it into bottombar +- [Feature] Add error display into remote controls screens - [Refactor] Load RemoteControls from flipper, emulating animation - [Refactor] Update to Kotlin 2.0 - [Refactor] Replace Ktorfit with Ktor requests in remote-controls From 3f5ad70949d72354e1613a95840ad51efcb1dc41 Mon Sep 17 00:00:00 2001 From: Roman Makeev Date: Mon, 9 Sep 2024 17:42:30 +0300 Subject: [PATCH 3/3] fix type for infrared remote --- .../remotecontrols/impl/setup/util/InfraredRemoteExt.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/util/InfraredRemoteExt.kt b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/util/InfraredRemoteExt.kt index aeff5343d..a8451c12f 100644 --- a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/util/InfraredRemoteExt.kt +++ b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/util/InfraredRemoteExt.kt @@ -12,6 +12,7 @@ internal fun InfraredRemote.toByteArray(): ByteArray { ) is InfraredRemote.Raw -> listOf( + type, frequency, dutyCycle, data