Skip to content

Commit a781f4b

Browse files
feat: add server exception event type and error animation (openMF#3123)
1 parent 7881ffe commit a781f4b

File tree

14 files changed

+129
-21
lines changed

14 files changed

+129
-21
lines changed

core/model/src/commonMain/kotlin/org/mifos/mobile/core/model/EventType.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,7 @@ enum class EventType {
1919

2020
@SerialName("failure")
2121
FAILURE,
22+
23+
@SerialName("server")
24+
SERVER_EXCEPTION,
2225
}

core/ui/src/commonMain/composeResources/files/error_animation.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2026 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
7+
*
8+
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
9+
*/
10+
package org.mifos.mobile.core.ui.component
11+
12+
import androidx.compose.foundation.Image
13+
import androidx.compose.foundation.layout.Box
14+
import androidx.compose.foundation.layout.size
15+
import androidx.compose.runtime.Composable
16+
import androidx.compose.runtime.getValue
17+
import androidx.compose.ui.Alignment
18+
import androidx.compose.ui.Modifier
19+
import androidx.compose.ui.unit.Dp
20+
import io.github.alexzhirkevich.compottie.LottieCompositionSpec
21+
import io.github.alexzhirkevich.compottie.animateLottieCompositionAsState
22+
import io.github.alexzhirkevich.compottie.rememberLottieComposition
23+
import io.github.alexzhirkevich.compottie.rememberLottiePainter
24+
import mifos_mobile.core.ui.generated.resources.Res
25+
import org.mifos.mobile.core.designsystem.theme.DesignToken
26+
27+
@Composable
28+
fun MifosLottieAnimation(
29+
path: String,
30+
modifier: Modifier = Modifier,
31+
size: Dp = DesignToken.sizes.imageDp212,
32+
iterations: Int? = null,
33+
) {
34+
val composition by rememberLottieComposition {
35+
LottieCompositionSpec.JsonString(
36+
Res.readBytes(path).decodeToString(),
37+
)
38+
}
39+
40+
val progress by animateLottieCompositionAsState(
41+
composition,
42+
iterations = iterations ?: Int.MAX_VALUE,
43+
)
44+
45+
Box(
46+
modifier = modifier,
47+
contentAlignment = Alignment.Center,
48+
) {
49+
Image(
50+
modifier = Modifier.size(size),
51+
painter = rememberLottiePainter(
52+
composition = composition,
53+
progress = { progress },
54+
),
55+
contentDescription = "Lottie animation",
56+
)
57+
}
58+
}

core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosStatusComponent.kt

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,26 +37,33 @@ import template.core.base.designsystem.theme.KptTheme
3737

3838
@Composable
3939
fun MifosStatusComponent(
40-
icon: DrawableResource,
4140
title: String,
4241
subTitle: String,
4342
buttonText: String,
4443
onClick: () -> Unit,
4544
modifier: Modifier = Modifier,
45+
icon: DrawableResource? = null,
46+
path: String? = null,
4647
) {
4748
Column(
4849
modifier = modifier
4950
.fillMaxWidth(),
5051
verticalArrangement = Arrangement.Center,
5152
horizontalAlignment = Alignment.CenterHorizontally,
5253
) {
53-
Image(
54-
modifier = Modifier
55-
.height(DesignToken.sizes.imageDp96)
56-
.width(DesignToken.sizes.imageDp96),
57-
painter = painterResource(icon),
58-
contentDescription = "Status icon",
59-
)
54+
icon?.let {
55+
Image(
56+
modifier = Modifier
57+
.height(DesignToken.sizes.imageDp96)
58+
.width(DesignToken.sizes.imageDp96),
59+
painter = painterResource(icon),
60+
contentDescription = "Status icon",
61+
)
62+
}
63+
64+
path?.let {
65+
MifosLottieAnimation(path = path, iterations = 1)
66+
}
6067

6168
Spacer(modifier = Modifier.height(DesignToken.spacing.medium))
6269

core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/LottieConstants.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ package org.mifos.mobile.core.ui.utils
1111

1212
object LottieConstants {
1313
const val LOADING_ANIMATION = "files/loading_animation.json"
14+
const val ERROR_ANIMATION = "files/error_animation.json"
1415
}

feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/otpAuthentication/OtpAuthenticationViewModel.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ package org.mifos.mobile.feature.auth.otpAuthentication
1212
import androidx.lifecycle.SavedStateHandle
1313
import androidx.lifecycle.viewModelScope
1414
import androidx.navigation.toRoute
15+
import io.ktor.client.plugins.ServerResponseException
1516
import kotlinx.coroutines.Job
1617
import kotlinx.coroutines.delay
1718
import kotlinx.coroutines.flow.update
@@ -195,7 +196,11 @@ internal class OtpAuthenticationViewModel(
195196
delay(1500)
196197
sendEvent(
197198
OtpAuthEvent.NavigateToStatus(
198-
eventType = EventType.FAILURE.name,
199+
eventType = if (action.exception.cause is ServerResponseException) {
200+
EventType.SERVER_EXCEPTION.name
201+
} else {
202+
EventType.FAILURE.name
203+
},
199204
eventDestination = LoginRoute::class.serializer().descriptor.serialName,
200205
title = getString(Res.string.feature_signup_user_registered_failed),
201206
subtitle = getString(Res.string.feature_signup_user_registered_failed_tip),

feature/beneficiary/src/commonMain/kotlin/org/mifos/mobile/feature/beneficiary/beneficiaryApplicationConfirmation/BeneficiaryApplicationConfirmationViewModel.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,11 @@ internal class BeneficiaryApplicationConfirmationViewModel(
186186
}
187187
sendEvent(
188188
BeneficiaryApplicationConfirmationEvent.NavigateToStatus(
189-
eventType = EventType.FAILURE.name,
189+
eventType = if (response.exception.cause is ServerResponseException) {
190+
EventType.SERVER_EXCEPTION.name
191+
} else {
192+
EventType.FAILURE.name
193+
},
190194
eventDestination = StatusNavigationDestination.PREVIOUS_SCREEN.name,
191195
title = getString(Res.string.beneficiary_creation_failed),
192196
subtitle = errorMsg,
@@ -261,7 +265,11 @@ internal class BeneficiaryApplicationConfirmationViewModel(
261265

262266
sendEvent(
263267
BeneficiaryApplicationConfirmationEvent.NavigateToStatus(
264-
eventType = EventType.FAILURE.name,
268+
eventType = if (response.exception.cause is ServerResponseException) {
269+
EventType.SERVER_EXCEPTION.name
270+
} else {
271+
EventType.FAILURE.name
272+
},
265273
eventDestination = StatusNavigationDestination.PREVIOUS_SCREEN.name,
266274
title = getString(Res.string.beneficiary_updation_failed),
267275
subtitle = errorMsg,

feature/loan-application/src/commonMain/kotlin/org/mifos/mobile/feature/loan/application/confirmDetails/ConfirmDetailsViewModel.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,11 @@ internal class ConfirmDetailsViewModel(
293293
}
294294
sendEvent(
295295
ConfirmDetailsEvent.NavigateToStatus(
296-
eventType = EventType.FAILURE.name,
296+
eventType = if (status.exception.cause is ServerResponseException) {
297+
EventType.SERVER_EXCEPTION.name
298+
} else {
299+
EventType.FAILURE.name
300+
},
297301
eventDestination = StatusNavigationDestination.PREVIOUS_SCREEN.name,
298302
title = getString(Res.string.feature_apply_loan_status_failure),
299303
subtitle = errorMsg,

feature/savings-account/src/commonMain/kotlin/org/mifos/mobile/feature/savingsaccount/savingsAccountUpdate/AccountUpdateViewModel.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,11 @@ internal class AccountUpdateViewModel(
324324
}
325325
sendEvent(
326326
AccountUpdateEvent.NavigateToStatus(
327-
eventType = EventType.FAILURE.name,
327+
eventType = if (dataState.exception.cause is ServerResponseException) {
328+
EventType.SERVER_EXCEPTION.name
329+
} else {
330+
EventType.FAILURE.name
331+
},
328332
eventDestination = StatusNavigationDestination.PREVIOUS_SCREEN.name,
329333
title = getString(Res.string.feature_savings_update_request_failed),
330334
subtitle = errorMsg,

feature/savings-account/src/commonMain/kotlin/org/mifos/mobile/feature/savingsaccount/savingsAccountWithdraw/AccountWithdrawViewModel.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,11 @@ internal class AccountWithdrawViewModel(
206206

207207
sendEvent(
208208
AccountWithdrawEvent.NavigateToStatus(
209-
eventType = EventType.FAILURE.name,
209+
eventType = if (dataState.exception.cause is ServerResponseException) {
210+
EventType.SERVER_EXCEPTION.name
211+
} else {
212+
EventType.FAILURE.name
213+
},
210214
eventDestination = StatusNavigationDestination.PREVIOUS_SCREEN.name,
211215
title = getString(Res.string.feature_savings_withdraw_request_failed),
212216
subtitle = errorMsg,

0 commit comments

Comments
 (0)