From 727983fc3df50f00d090f1ce86ecbba5b1cb7be0 Mon Sep 17 00:00:00 2001 From: Nagarjuna Date: Thu, 28 Aug 2025 15:05:24 +0530 Subject: [PATCH 1/3] refactor: auth module with loading animation --- .../repositoryImpl/UserAuthRepositoryImp.kt | 5 +- core/ui/build.gradle.kts | 2 + .../files/loading_animation.json | 1 + .../ui/component/MifosProgressIndicator.kt | 75 ++++++++++++---- .../utils/PresentOrFutureSelectableDates.kt | 5 +- .../mobile/core/ui/utils/ScreenUiState.kt | 64 ++++++++++++++ .../TransactionViewModel.kt | 5 +- .../accounts/accounts/AccountsViewModel.kt | 9 +- .../mobile/feature/auth/login/LoginScreen.kt | 41 ++++----- .../feature/auth/login/LoginViewModel.kt | 35 +++----- .../OtpAuthenticationScreen.kt | 85 +++++++++---------- .../OtpAuthenticationViewModel.kt | 16 ++-- .../recoverPassword/RecoverPasswordScreen.kt | 79 ++++++++--------- .../RecoverPasswordViewModel.kt | 9 +- .../auth/registration/RegistrationScreen.kt | 45 +++++----- .../registration/RegistrationViewModel.kt | 17 ++-- .../auth/setNewPassword/SetPasswordScreen.kt | 77 ++++++++--------- .../setNewPassword/SetPasswordViewModel.kt | 11 +-- .../component/RepaymentPeriodCard.kt | 4 +- .../loanApplication/LoanApplyViewModel.kt | 5 +- .../FillApplicationViewModel.kt | 4 +- .../SavingsApplyViewModel.kt | 4 +- .../FillApplicationViewModel.kt | 4 +- .../shareApplication/ShareApplyViewModel.kt | 4 +- gradle/libs.versions.toml | 8 ++ 25 files changed, 359 insertions(+), 255 deletions(-) create mode 100644 core/ui/src/commonMain/composeResources/files/loading_animation.json create mode 100644 core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/ScreenUiState.kt diff --git a/core/data/src/commonMain/kotlin/org/mifos/mobile/core/data/repositoryImpl/UserAuthRepositoryImp.kt b/core/data/src/commonMain/kotlin/org/mifos/mobile/core/data/repositoryImpl/UserAuthRepositoryImp.kt index cb8ec6b530..f6beb76a03 100644 --- a/core/data/src/commonMain/kotlin/org/mifos/mobile/core/data/repositoryImpl/UserAuthRepositoryImp.kt +++ b/core/data/src/commonMain/kotlin/org/mifos/mobile/core/data/repositoryImpl/UserAuthRepositoryImp.kt @@ -73,8 +73,9 @@ class UserAuthRepositoryImp( DataState.Error(Exception("Invalid Credentials"), null) } } - } catch (e: Exception) { - DataState.Error(e, null) + } catch (e: ClientRequestException) { + val errorMessage = extractErrorMessage(e.response) + DataState.Error(Exception(errorMessage), null) } } diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 9f0c24c237..f968ef272e 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -44,6 +44,8 @@ kotlin{ implementation(libs.jb.composeNavigation) implementation(libs.filekit.compose) implementation(libs.filekit.core) + implementation(libs.compottie.resources) + implementation(libs.compottie.lite) } } } diff --git a/core/ui/src/commonMain/composeResources/files/loading_animation.json b/core/ui/src/commonMain/composeResources/files/loading_animation.json new file mode 100644 index 0000000000..ca6901d062 --- /dev/null +++ b/core/ui/src/commonMain/composeResources/files/loading_animation.json @@ -0,0 +1 @@ +{"nm":"Main Scene","ddd":0,"h":500,"w":500,"meta":{"g":"@lottiefiles/creator 1.47.1"},"layers":[{"ty":0,"nm":"Nested Scene 1","sr":1,"st":0,"op":92,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[32.00010198974633,32.00128392491024]},"s":{"a":0,"k":[214.1158,214.1158]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[250,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":500,"h":500,"refId":"precomp_Shape Layer - SVG_9a65cf1e-7dbf-4e06-85a4-5dc45595fbad","ind":1},{"ty":4,"nm":"Shape Layer 5","sr":1,"st":0,"op":387.000015762833,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-17.052704811096184,-15.0467209815979],"ix":1},"s":{"a":0,"k":[362.228,362.228,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[188.2303284168625,195.49656352277756],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Shape 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[-2.003,0],[-0.231,-6.654],[-4.034,-2.519],[0,-4.262],[3.615,-2.257],[0.165,-4.753],[6.655,0],[1.814,0.965],[2.33,0],[2.057,-1.094],[2.003,0],[0.231,6.654],[4.034,2.519],[0,4.262],[-3.615,2.257],[-0.165,4.753],[-6.655,0],[-1.814,-0.965],[-2.33,0],[-2.057,1.094]],"o":[[6.655,0],[0.165,4.753],[3.615,2.257],[0,4.262],[-4.034,2.519],[-0.231,6.654],[-2.003,0],[-2.057,-1.094],[-2.33,0],[-1.814,0.965],[-6.655,0],[-0.165,-4.753],[-3.615,-2.257],[0,-4.262],[4.034,-2.519],[0.231,-6.654],[2.003,0],[2.057,1.094],[2.33,0],[1.814,-0.965]],"v":[[12.457,-33.889],[24.737,-22.024],[31.441,-10.411],[37.214,0],[31.442,10.411],[24.737,22.024],[12.457,33.889],[6.705,32.435],[0,30.762],[-6.705,32.435],[-12.457,33.889],[-24.737,22.024],[-31.441,10.411],[-37.214,0],[-31.441,-10.411],[-24.737,-22.024],[-12.457,-33.889],[-6.705,-32.434],[0,-30.762],[6.705,-32.434]]},"ix":2}},{"ty":"gs","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Stroke","nm":"Gradient Stroke 1","e":{"a":0,"k":[100,0],"ix":5},"g":{"p":3,"k":{"a":0,"k":[0,1,0.4470588235294118,0,0.485,0.9725490196078431,0.2235294117647059,0.5019607843137255,1,0.9411764705882353,0,1],"ix":8}},"t":1,"a":{"a":0,"k":0},"h":{"a":0,"k":0},"s":{"a":0,"k":[0,0],"ix":4},"lc":2,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":9},"w":{"a":0,"k":2,"ix":10}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.0941,0.5294,0.6863],"ix":4},"r":1,"o":{"a":0,"k":0,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"tm","bm":0,"hd":false,"mn":"ADBE Vector Filter - Trim","nm":"Trim Paths 1","ix":2,"e":{"a":0,"k":28,"ix":2},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":0},{"s":[360],"t":90.0000036657751}],"ix":3},"s":{"a":0,"k":0,"ix":1},"m":1}],"ind":2},{"ty":4,"nm":"Shape Layer 4","sr":1,"st":0,"op":387.000015762833,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[362.228,362.228,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[250,250,0],"ix":2},"r":{"a":0,"k":60,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Shape 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[-2.003,0],[-0.231,-6.654],[-4.034,-2.519],[0,-4.262],[3.615,-2.257],[0.165,-4.753],[6.655,0],[1.814,0.965],[2.33,0],[2.057,-1.094],[2.003,0],[0.231,6.654],[4.034,2.519],[0,4.262],[-3.615,2.257],[-0.165,4.753],[-6.655,0],[-1.814,-0.965],[-2.33,0],[-2.057,1.094]],"o":[[6.655,0],[0.165,4.753],[3.615,2.257],[0,4.262],[-4.034,2.519],[-0.231,6.654],[-2.003,0],[-2.057,-1.094],[-2.33,0],[-1.814,0.965],[-6.655,0],[-0.165,-4.753],[-3.615,-2.257],[0,-4.262],[4.034,-2.519],[0.231,-6.654],[2.003,0],[2.057,1.094],[2.33,0],[1.814,-0.965]],"v":[[12.457,-33.889],[24.737,-22.024],[31.441,-10.411],[37.214,0],[31.442,10.411],[24.737,22.024],[12.457,33.889],[6.705,32.435],[0,30.762],[-6.705,32.435],[-12.457,33.889],[-24.737,22.024],[-31.441,10.411],[-37.214,0],[-31.441,-10.411],[-24.737,-22.024],[-12.457,-33.889],[-6.705,-32.434],[0,-30.762],[6.705,-32.434]]},"ix":2}},{"ty":"gs","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Stroke","nm":"Gradient Stroke 1","e":{"a":0,"k":[100,0],"ix":5},"g":{"p":3,"k":{"a":0,"k":[0,0,0.7764705882352941,1,0.485,0.24705882352941178,0.38823529411764707,1,1,0.49411764705882355,0,1],"ix":8}},"t":1,"a":{"a":0,"k":0},"h":{"a":0,"k":0},"s":{"a":0,"k":[0,0],"ix":4},"lc":2,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":9},"w":{"a":0,"k":0.5,"ix":10}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.4118,0.6784,0.7765],"ix":4},"r":1,"o":{"a":0,"k":0,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"tm","bm":0,"hd":false,"mn":"ADBE Vector Filter - Trim","nm":"Trim Paths 1","ix":2,"e":{"a":0,"k":28,"ix":2},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":0},{"s":[360],"t":90.0000036657751}],"ix":3},"s":{"a":0,"k":0,"ix":1},"m":1}],"ind":3},{"ty":4,"nm":"Shape Layer 3","sr":1,"st":0,"op":387.000015762833,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[362.228,362.228,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[250,250,0],"ix":2},"r":{"a":0,"k":30,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Shape 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[-2.003,0],[-0.231,-6.654],[-4.034,-2.519],[0,-4.262],[3.615,-2.257],[0.165,-4.753],[6.655,0],[1.814,0.965],[2.33,0],[2.057,-1.094],[2.003,0],[0.231,6.654],[4.034,2.519],[0,4.262],[-3.615,2.257],[-0.165,4.753],[-6.655,0],[-1.814,-0.965],[-2.33,0],[-2.057,1.094]],"o":[[6.655,0],[0.165,4.753],[3.615,2.257],[0,4.262],[-4.034,2.519],[-0.231,6.654],[-2.003,0],[-2.057,-1.094],[-2.33,0],[-1.814,0.965],[-6.655,0],[-0.165,-4.753],[-3.615,-2.257],[0,-4.262],[4.034,-2.519],[0.231,-6.654],[2.003,0],[2.057,1.094],[2.33,0],[1.814,-0.965]],"v":[[12.457,-33.889],[24.737,-22.024],[31.441,-10.411],[37.214,0],[31.442,10.411],[24.737,22.024],[12.457,33.889],[6.705,32.435],[0,30.762],[-6.705,32.435],[-12.457,33.889],[-24.737,22.024],[-31.441,10.411],[-37.214,0],[-31.441,-10.411],[-24.737,-22.024],[-12.457,-33.889],[-6.705,-32.434],[0,-30.762],[6.705,-32.434]]},"ix":2}},{"ty":"gs","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Stroke","nm":"Gradient Stroke 1","e":{"a":0,"k":[100,0],"ix":5},"g":{"p":3,"k":{"a":0,"k":[0,1,0.8235294117647058,0,0.485,0.5019607843137255,0.8117647058823529,0.5019607843137255,1,0,0.8,1],"ix":8}},"t":1,"a":{"a":0,"k":0},"h":{"a":0,"k":0},"s":{"a":0,"k":[0,0],"ix":4},"lc":2,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":9},"w":{"a":0,"k":0.5,"ix":10}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.4118,0.6784,0.7765],"ix":4},"r":1,"o":{"a":0,"k":0,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"tm","bm":0,"hd":false,"mn":"ADBE Vector Filter - Trim","nm":"Trim Paths 1","ix":2,"e":{"a":0,"k":28,"ix":2},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":0},{"s":[360],"t":90.0000036657751}],"ix":3},"s":{"a":0,"k":0,"ix":1},"m":1}],"ind":4},{"ty":4,"nm":"Shape Layer 1","sr":1,"st":0,"op":387.000015762833,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[362.228,362.228,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[250,250,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":8,"ix":11}},"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Shape 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[-2.003,0],[-0.231,-6.654],[-4.034,-2.519],[0,-4.262],[3.615,-2.257],[0.165,-4.753],[6.655,0],[1.814,0.965],[2.33,0],[2.057,-1.094],[2.003,0],[0.231,6.654],[4.034,2.519],[0,4.262],[-3.615,2.257],[-0.165,4.753],[-6.655,0],[-1.814,-0.965],[-2.33,0],[-2.057,1.094]],"o":[[6.655,0],[0.165,4.753],[3.615,2.257],[0,4.262],[-4.034,2.519],[-0.231,6.654],[-2.003,0],[-2.057,-1.094],[-2.33,0],[-1.814,0.965],[-6.655,0],[-0.165,-4.753],[-3.615,-2.257],[0,-4.262],[4.034,-2.519],[0.231,-6.654],[2.003,0],[2.057,1.094],[2.33,0],[1.814,-0.965]],"v":[[12.457,-33.889],[24.737,-22.024],[31.441,-10.411],[37.214,0],[31.442,10.411],[24.737,22.024],[12.457,33.889],[6.705,32.435],[0,30.762],[-6.705,32.435],[-12.457,33.889],[-24.737,22.024],[-31.441,10.411],[-37.214,0],[-31.441,-10.411],[-24.737,-22.024],[-12.457,-33.889],[-6.705,-32.434],[0,-30.762],[6.705,-32.434]]},"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"c":{"a":0,"k":[0.8157,0.8157,0.8157],"ix":3}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.4118,0.6784,0.7765],"ix":4},"r":1,"o":{"a":0,"k":0,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"tm","bm":0,"hd":false,"mn":"ADBE Vector Filter - Trim","nm":"Trim Paths 1","ix":2,"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"s":{"a":0,"k":0,"ix":1},"m":1}],"ind":5}],"v":"5.7.0","fr":29.9700012207031,"op":92,"ip":0,"assets":[{"nm":"Nested Scene 1","id":"precomp_Shape Layer - SVG_9a65cf1e-7dbf-4e06-85a4-5dc45595fbad","fr":29.9700012207031,"layers":[{"ty":4,"nm":"Shape Layer - SVG","sr":1,"st":0,"op":92,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"Group 1","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 1","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[-0.4866000000000028,-2.1432],[0,0],[0,0],[-3.7177000000000007,4.1865999999999985],[0,0],[0,0],[1.5194000000000045,0.4987999999999957],[-0.10560000000000258,-0.7130999999999972]],"o":[[0.36659999999999826,2.4802999999999997],[0,0],[0,0],[3.657899999999998,-0.33209999999999695],[0,0],[0,0],[-4.331400000000002,0.12060000000000315],[-0.2796999999999983,-0.091700000000003],[0,0]],"v":[[37.8934,52.3959],[39.3607,60.3476],[39.9165,62.7956],[40.4902,62.7433],[55.8807,53.3144],[56.5203,52.5945],[53.2122,52.6864],[38.086,51.7085],[37.8934,52.3959]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.4627,0.7686,0.4784]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 2","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 2","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[-0.15738999999999947,-0.445600000000006],[-3.0069,-1.603900000000003],[-5.0671,0.9211000000000027],[0,0],[0,0],[0.38759999999999906,2.5437999999999974],[0,0],[0,0],[5.086,3.870999999999995],[0.07519999999999882,0],[0.6423999999999985,-2.1193000000000026]],"o":[[-1.3162400000000005,4.343699999999998],[0.38225000000000087,1.083199999999998],[5.5031,2.9361999999999995],[0,0],[0,0],[-0.5576000000000008,-2.1340000000000003],[0,0],[0,0],[-6.5581,-2.023600000000002],[-0.9616000000000007,-0.7317999999999998],[-0.07540000000000013,0],[0,0]],"v":[[10.3213,43.5292],[7.99771,53.13],[16.8523,60.1514],[35.6616,63.737],[36.4296,63.5977],[36.1118,62.3806],[34.2685,53.2599],[33.8928,50.7962],[31.2208,49.9716],[13.5113,41.0065],[11.6262,39.6758],[10.3213,43.5292]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.0275,0.451,0.7333]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 3","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 3","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[-3.384233,-5.6932000000000045],[-0.3778100000000002,1.6189999999999998],[-0.9020400000000004,2.518900000000002],[0.00140000000000029,0.06499999999999773],[0.8331499999999998,0.9473999999999947],[1.5369500000000003,2.5269000000000013],[0.06304700000000008,0.08149999999999835],[0.10811399999999999,-0.7799000000000014]],"o":[[-0.9809669999999999,7.0760000000000005],[0.6531199999999995,1.0985000000000014],[0.51349,-2.1987000000000023],[0.4948000000000006,-1.3810000000000002],[-0.0014099999999999113,-0.06500000000000483],[-2.0102700000000002,-2.286300000000004],[-0.6974799999999999,-1.1469999999999985],[-0.06281399999999993,-0.08170000000000144],[0,0]],"v":[[0.299989,27.289],[4.16338,47.8151],[5.20085,47.2916],[7.55905,39.4327],[8.45619,36.8034],[6.93862,34.9626],[1.99385,28.2532],[0.611022,26.0196],[0.299989,27.289]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.1333,0.698,0.298]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 4","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 4","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[-0.5378000000000043,-0.5847000000000051],[-2.363100000000003,-0.1734000000000009],[-0.9472999999999985,0.6635999999999953],[0,5.052500000000002],[0,0],[0,0],[7.270699999999998,2.3749000000000002],[0.05420000000000158,-0.2012999999999998]],"o":[[-0.5215999999999994,1.9314],[0.12959999999999638,0.14089999999999492],[4.624599999999994,0.33910000000000196],[1.7532000000000068,-1.2280000000000015],[0,0],[0,0],[-6.882399999999997,1.0878000000000014],[-0.17260000000000275,-0.056400000000000006],[0,0]],"v":[[37.2526,29.286],[37.3708,47.7764],[45.1527,48.7571],[59.6112,47.9155],[64,32.1945],[64,31.0465],[62.3739,31.3035],[37.6188,29.0519],[37.2526,29.286]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.1569,0.6745,0.8863]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 5","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 5","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[1.3225999999999996,-3.450000000000003],[0,0],[0,0],[-5.736600000000003,-1.8301000000000016],[-0.17549999999999955,-0.05740000000000123],[-0.17549999999999955,0.1676000000000002],[0.06490000000000151,0.7081000000000017],[0,0],[0.01559999999999917,0.19009999999999394],[-0.46280000000000143,2.0993999999999993],[1.0622000000000007,0.5338999999999992],[2.0352999999999994,1.5974000000000004],[0.1661999999999999,0.1280000000000001],[1.0383999999999993,-1.343]],"o":[[-2.991699999999998,3.869200000000003],[0,0],[0,0],[4.527899999999999,3.899700000000003],[0.2134999999999998,0.06810000000000116],[0.9068999999999967,0.2961999999999989],[0.15290000000000248,-0.14610000000000412],[0,0],[-0.014400000000001967,-0.15669999999999362],[-0.3248000000000033,-3.954800000000006],[0.017600000000001614,-0.07900000000000063],[-2.2921000000000014,-1.1525999999999996],[-0.8074000000000012,-0.6334000000000017],[-0.27069999999999794,-0.2079000000000022],[0,0]],"v":[[21.1862,21.7642],[13.3941,34.984],[13.1426,35.6405],[14.5119,36.82],[31.2098,46.141],[31.7925,46.3298],[33.2972,46.5964],[33.3505,45.364],[33.3504,45.3627],[33.3046,44.843],[33.6959,27.6811],[31.7966,26.5666],[24.4205,21.8794],[22.6501,20.4948],[21.1862,21.7642]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.0275,0.451,0.7333]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 6","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 6","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[1.6971300000000005,-3.8272999999999993],[0,0],[0,0],[-2.9274900000000006,-3.475900000000003],[0,0],[0,0],[-2.8207000000000004,3.688200000000002],[0,0],[0,0],[1.5810999999999993,2.36097],[0.11470000000000091,0.17483000000000004],[0.2865000000000002,0.03232999999999997],[0.6156000000000006,-0.6015199999999998],[0.1765699999999999,-0.16886000000000045]],"o":[[-2.967649999999999,2.8382400000000008],[0,0],[0,0],[2.05276,3.5570999999999984],[0,0],[0,0],[1.872300000000001,-3.720200000000002],[0,0],[0,0],[-1.805299999999999,-1.9300999999999995],[-0.14390000000000036,-0.21494999999999997],[-0.5118999999999989,-0.7804499999999992],[-0.27949999999999875,-0.03155000000000019],[-0.14119999999999955,0.13804999999999978],[0,0]],"v":[[9.77953,8.90166],[2.72246,18.985],[1.80664,21.05],[2.237,21.7956],[9.2392,31.6839],[10.2195,32.8482],[11.2128,30.8746],[18.2271,19.8019],[19.7674,17.788],[18.4287,16.3567],[12.8987,9.34923],[12.5124,8.7648],[11.4613,7.56662],[10.2546,8.44107],[9.77953,8.90166]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.1569,0.6745,0.8863]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 7","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 7","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[1.3486999999999938,-6.994300000000001],[0.03300000000000125,-0.1656000000000013],[-0.17560000000000286,-0.32069999999999865],[-1.0641999999999996,-0.28910000000000124],[-0.1992000000000047,-0.05529999999999902],[-6.824000000000005,0.6976000000000013],[-0.19290000000000163,0.01770000000000138],[-0.12379999999999569,0.20589999999999975],[0.113900000000001,0.5217999999999989],[0.038699999999998624,0.19280000000000186],[8.160399999999996,4.55716],[0,0],[0,0]],"o":[[-2.9772999999999996,4.436579999999999],[-0.03750000000000142,0.1944999999999979],[-0.1910000000000025,0.9592999999999989],[0.1897999999999982,0.34690000000000154],[0.17009999999999792,0.04619999999999891],[5.970399999999998,1.6580000000000013],[0.2458999999999989,-0.025099999999998346],[0.811399999999999,-0.07430000000000092],[0.10180000000000433,-0.16949999999999932],[-0.03289999999999793,-0.15060000000000073],[-1.8084999999999951,-8.997599999999998],[0,0],[0,0],[0,0]],"v":[[45.3084,3.81117],[38.2214,22.5377],[38.1152,23.0771],[38.0005,24.8556],[39.7658,25.6305],[40.3192,25.7821],[61.5626,27.3769],[62.2194,27.3141],[63.5209,27.0004],[63.4463,26.0229],[63.3375,25.5092],[47.4917,3.96349],[45.8291,3.0351],[45.3084,3.81117]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.1333,0.698,0.298]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 8","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 8","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[1.8596000000000004,-2.0977000000000015],[0,0],[0,0],[-0.2785999999999973,0.34830000000000183],[-0.30169999999999675,1.3917000000000002],[-2.1699999999999946,4.5597199999999996],[0.06470000000000198,-0.0043199999999998795],[1.8033999999999963,-1.3320400000000001]],"o":[[-4.951500000000003,3.65771],[0,0],[0,0],[2.5614999999999988,2.0771000000000015],[0.06560000000000343,-0.08200000000000074],[1.0449000000000055,-4.822699999999999],[0.7236000000000047,-1.5209200000000003],[-0.06459999999999866,0.0043199999999998795],[0,0]],"v":[[37.4538,6.75399],[25.594,16.7754],[24.9658,17.4839],[26.1738,18.4636],[34.3064,23.4148],[34.974,20.7352],[39.6523,7.08148],[40.8504,4.32423],[37.4538,6.75399]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.0235,0.451,0.7294]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 9","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 9","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,-1.0842699999999996],[-2.1457000000000015,-2.427200000000001],[0,0],[0,0],[-5.013399999999997,3.6241999999999996],[0,0],[0,0],[3.0864999999999974,-0.32317300000000004]],"o":[[-4.299600000000002,0.450614],[0,0.5206800000000005],[0,0],[0,0],[3.7058,-3.7046599999999987],[0,0],[0,0],[-2.9074000000000026,-0.5727236099999999],[0,0]],"v":[[28.4216,0.166472],[14.792,5.03107],[20.7359,13.1972],[22.26,14.9212],[23.9051,13.2769],[36.2065,2.93718],[39.1905,0.780068],[38.1246,0.57012],[28.4216,0.166472]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.4627,0.7686,0.4784]},"r":2,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"ind":1}]}]} \ No newline at end of file diff --git a/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosProgressIndicator.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosProgressIndicator.kt index 5c00802c94..f69d7027b0 100644 --- a/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosProgressIndicator.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosProgressIndicator.kt @@ -9,53 +9,94 @@ */ package org.mifos.mobile.core.ui.component +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import org.mifos.mobile.core.designsystem.theme.DesignToken -import org.mifos.mobile.core.ui.utils.DevicePreview +import io.github.alexzhirkevich.compottie.LottieCompositionSpec +import io.github.alexzhirkevich.compottie.animateLottieCompositionAsState +import io.github.alexzhirkevich.compottie.rememberLottieComposition +import io.github.alexzhirkevich.compottie.rememberLottiePainter +import mifos_mobile.core.ui.generated.resources.Res +import org.jetbrains.compose.ui.tooling.preview.Preview +import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme -@DevicePreview @Composable fun MifosProgressIndicator( modifier: Modifier = Modifier.fillMaxSize(), ) { - Column( + val composition by rememberLottieComposition { + LottieCompositionSpec.JsonString( + Res.readBytes("files/loading_animation.json").decodeToString(), + ) + } + val progress by animateLottieCompositionAsState(composition) + + Box( modifier = modifier, - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, + contentAlignment = Alignment.Center, ) { - CircularProgressIndicator() + Image( + painter = rememberLottiePainter( + composition = composition, + progress = { progress }, + ), + contentDescription = "Lottie animation", + ) } } -@DevicePreview @Composable fun MifosProgressIndicatorOverlay( modifier: Modifier = Modifier.fillMaxSize(), ) { - Column( + val composition by rememberLottieComposition { + LottieCompositionSpec.JsonString( + Res.readBytes("files/loading_animation.json").decodeToString(), + ) + } + val progress by animateLottieCompositionAsState(composition) + + Box( modifier = modifier - .padding(DesignToken.padding.large) .background(MaterialTheme.colorScheme.background.copy(alpha = 0.7f)) .clickable( enabled = false, indication = null, interactionSource = remember { MutableInteractionSource() }, ) { }, - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, + contentAlignment = Alignment.Center, ) { - CircularProgressIndicator() + Image( + painter = rememberLottiePainter( + composition = composition, + progress = { progress }, + ), + contentDescription = "Loading animation", + ) + } +} + +@Preview +@Composable +private fun Loading_Preview() { + MifosMobileTheme { + MifosProgressIndicator() + } +} + +@Preview +@Composable +private fun Overlay_Loading_Preview() { + MifosMobileTheme { + MifosProgressIndicatorOverlay() } } diff --git a/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/PresentOrFutureSelectableDates.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/PresentOrFutureSelectableDates.kt index 5489dcaf9e..83a7b8505e 100644 --- a/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/PresentOrFutureSelectableDates.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/PresentOrFutureSelectableDates.kt @@ -11,19 +11,22 @@ package org.mifos.mobile.core.ui.utils import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.SelectableDates -import kotlinx.datetime.Clock import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime +import kotlin.time.Clock +import kotlin.time.ExperimentalTime @OptIn(ExperimentalMaterial3Api::class) object PresentOrFutureSelectableDates : SelectableDates { + @OptIn(ExperimentalTime::class) @ExperimentalMaterial3Api override fun isSelectableDate(utcTimeMillis: Long): Boolean { val currentTimeMillis = Clock.System.now().toEpochMilliseconds() return utcTimeMillis >= currentTimeMillis } + @OptIn(ExperimentalTime::class) override fun isSelectableYear(year: Int): Boolean { val currentYear = Clock.System.now() .toLocalDateTime(TimeZone.currentSystemDefault()) diff --git a/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/ScreenUiState.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/ScreenUiState.kt new file mode 100644 index 0000000000..4c97098938 --- /dev/null +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/ScreenUiState.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md + */ +package org.mifos.mobile.core.ui.utils + +import org.jetbrains.compose.resources.StringResource + +/** + * A sealed interface representing the different types of full-screen UI states + * that can be used across various screens in an application. + * + * This generic state model provides a consistent way to handle common UI states + * such as loading, errors, and success, which helps to standardize UI logic + * and reduce boilerplate in ViewModels and UI layers. + */ +sealed interface ScreenUiState { + /** * Represents a full-screen loading state. + * * Use this state when the entire screen is busy fetching initial data, + * and a loading indicator (e.g., a progress bar) should be displayed + * over the whole screen. + */ + data object Loading : ScreenUiState + + /** * Represents an empty state where no data is available to display. + * * This state is typically used when a data fetch operation is successful + * but returns an empty list or collection. The UI should show a message + * or a graphic indicating that there is no content. + */ + data object Empty : ScreenUiState + + /** + * Represents a full-screen error state with a user-facing message. + * * This state should be used when an unrecoverable error occurs, and the + * entire screen needs to show an error message. + * + * @property message The [StringResource] for the error message to be displayed. + */ + data class Error(val message: StringResource) : ScreenUiState + + /** * Represents a successful state where content can be displayed. + * * This is the final state after a successful data fetch or operation. + * The UI can now display the main content of the screen. + */ + data object Success : ScreenUiState + + /** * Represents a state where there is a network connectivity issue. + * * This state is useful for displaying a dedicated UI screen or a message + * indicating that the device is offline or has lost its connection. + */ + data object Network : ScreenUiState + + /** * Represents a state where an overlay loading spinner should be shown. + * * Use this state when a background operation is in progress (e.g., a form + * submission or a data refresh), but the existing content of the screen + * should remain visible underneath a loading indicator. + */ +// data object OverlayLoading : ScreenUiState +} diff --git a/feature/accounts/src/commonMain/kotlin/org/mifos/mobile/feature/accounts/accountTransactions/TransactionViewModel.kt b/feature/accounts/src/commonMain/kotlin/org/mifos/mobile/feature/accounts/accountTransactions/TransactionViewModel.kt index fb67edfa99..d4938fc00e 100644 --- a/feature/accounts/src/commonMain/kotlin/org/mifos/mobile/feature/accounts/accountTransactions/TransactionViewModel.kt +++ b/feature/accounts/src/commonMain/kotlin/org/mifos/mobile/feature/accounts/accountTransactions/TransactionViewModel.kt @@ -17,7 +17,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import kotlinx.datetime.Clock import kotlinx.datetime.DateTimeUnit import kotlinx.datetime.LocalDate import kotlinx.datetime.TimeZone @@ -51,6 +50,9 @@ import org.mifos.mobile.feature.accounts.model.TransactionCheckboxStatus import org.mifos.mobile.feature.accounts.model.TransactionFilterType import org.mifos.mobile.feature.accounts.utils.StatusUtils import kotlin.collections.map +import kotlin.time.Clock +import kotlin.time.ExperimentalTime + /** * ViewModel for managing the state and logic of the account transactions screen. * @@ -451,6 +453,7 @@ internal class AccountsTransactionViewModel( * @param selectedFilters A list of [TransactionCheckboxStatus] representing the active checkbox filters. * @return A map of filtered transactions grouped by date. */ + @OptIn(ExperimentalTime::class) internal fun applyTransactionFilters( selectedFilters: List, ): Map> { diff --git a/feature/accounts/src/commonMain/kotlin/org/mifos/mobile/feature/accounts/accounts/AccountsViewModel.kt b/feature/accounts/src/commonMain/kotlin/org/mifos/mobile/feature/accounts/accounts/AccountsViewModel.kt index ea75b95f0c..e09578877b 100644 --- a/feature/accounts/src/commonMain/kotlin/org/mifos/mobile/feature/accounts/accounts/AccountsViewModel.kt +++ b/feature/accounts/src/commonMain/kotlin/org/mifos/mobile/feature/accounts/accounts/AccountsViewModel.kt @@ -12,7 +12,6 @@ package org.mifos.mobile.feature.accounts.accounts import androidx.lifecycle.SavedStateHandle import androidx.navigation.toRoute import kotlinx.coroutines.flow.update -import kotlinx.datetime.Clock import org.jetbrains.compose.resources.StringResource import org.mifos.mobile.core.common.Constants import org.mifos.mobile.core.model.enums.AccountType @@ -20,6 +19,8 @@ import org.mifos.mobile.core.ui.utils.BaseViewModel import org.mifos.mobile.feature.accounts.model.CheckboxStatus import org.mifos.mobile.feature.accounts.model.FilterType import org.mifos.mobile.feature.accounts.utils.StatusUtils +import kotlin.time.Clock +import kotlin.time.ExperimentalTime /** * ViewModel responsible for managing the account screen state, @@ -40,6 +41,7 @@ internal class AccountsViewModel( observeAccountTypeAndInitCheckboxes() } + @OptIn(ExperimentalTime::class) override fun handleAction(action: AccountsAction) { when (action) { is AccountsAction.SetCheckboxFilterList -> { @@ -112,6 +114,7 @@ internal class AccountsViewModel( * Applies the selected checkboxes as filters, sets refresh signal, * and dismisses the filter dialog. */ + @OptIn(ExperimentalTime::class) private fun handleConfirmFilterDialog() { val selectedFilters = state.checkboxOptions.filter { it.isChecked } @@ -202,7 +205,9 @@ internal class AccountsViewModel( * UI state for the Accounts screen, containing filter options, dialog visibility, * current account type, and refresh signals. */ -internal data class AccountsState( +internal data class AccountsState +@OptIn(ExperimentalTime::class) +constructor( val isRefreshing: Boolean = false, /** Current filter checkboxes shown in the dialog */ diff --git a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/login/LoginScreen.kt b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/login/LoginScreen.kt index 0d7edb2e1f..05bd98bae0 100644 --- a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/login/LoginScreen.kt +++ b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/login/LoginScreen.kt @@ -26,7 +26,6 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -56,10 +55,8 @@ import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.ui.tooling.preview.Preview import org.koin.compose.viewmodel.koinViewModel import org.mifos.mobile.core.designsystem.component.BasicDialogState -import org.mifos.mobile.core.designsystem.component.LoadingDialogState import org.mifos.mobile.core.designsystem.component.MifosBasicDialog import org.mifos.mobile.core.designsystem.component.MifosButton -import org.mifos.mobile.core.designsystem.component.MifosLoadingDialog import org.mifos.mobile.core.designsystem.component.MifosOutlinedTextField import org.mifos.mobile.core.designsystem.component.MifosPasswordField import org.mifos.mobile.core.designsystem.component.MifosScaffold @@ -70,7 +67,9 @@ import org.mifos.mobile.core.designsystem.theme.DesignToken import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.designsystem.theme.MifosTypography import org.mifos.mobile.core.ui.component.MifosPoweredCard +import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay import org.mifos.mobile.core.ui.utils.EventsEffect +import org.mifos.mobile.core.ui.utils.ScreenUiState @Composable internal fun LoginScreen( @@ -132,11 +131,21 @@ private fun LoginScreen( } }, ) { - LoginScreenContent( - modifier = modifier, - state = state, - onAction = onAction, - ) + when (state.uiState) { + ScreenUiState.Success -> { + LoginScreenContent( + modifier = modifier, + state = state, + onAction = onAction, + ) + + if (state.showOverlay) { + MifosProgressIndicatorOverlay() + } + } + + else -> {} + } } } @@ -153,10 +162,6 @@ private fun LoginDialogs( onDismissRequest = onDismissRequest, ) - is LoginState.DialogState.Loading -> MifosLoadingDialog( - visibilityState = LoadingDialogState.Shown, - ) - null -> Unit } } @@ -243,11 +248,6 @@ fun InputBox( label = stringResource(Res.string.feature_sign_in_username_label), shape = DesignToken.shapes.medium, textStyle = MifosTypography.bodyLarge, - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - unfocusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - errorBorderColor = MaterialTheme.colorScheme.error, - ), config = MifosTextFieldConfig( isError = state.isError, errorText = state.userNameError.takeIf { state.isError }?.let { stringResource(it) }, @@ -277,11 +277,6 @@ fun InputBox( showPasswordChange = { onAction(LoginAction.TogglePasswordVisibility) }, - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - unfocusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - errorBorderColor = MaterialTheme.colorScheme.error, - ), isError = state.isError, hint = state.passwordError.takeIf { state.isError }?.let { stringResource(it) }, ) @@ -342,7 +337,7 @@ fun InputBox( private fun LoanScreenPreview() { MifosMobileTheme { LoginScreen( - state = LoginState(dialogState = null), + state = LoginState(uiState = ScreenUiState.Success), onAction = {}, ) } diff --git a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/login/LoginViewModel.kt b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/login/LoginViewModel.kt index 116b747246..07c8df0744 100644 --- a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/login/LoginViewModel.kt +++ b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/login/LoginViewModel.kt @@ -23,18 +23,16 @@ import org.mifos.mobile.core.common.DataState import org.mifos.mobile.core.data.repository.UserAuthRepository import org.mifos.mobile.core.datastore.UserPreferencesRepository import org.mifos.mobile.core.datastore.model.UserData -import org.mifos.mobile.core.model.IgnoredOnParcel -import org.mifos.mobile.core.model.Parcelable -import org.mifos.mobile.core.model.Parcelize import org.mifos.mobile.core.model.entity.User import org.mifos.mobile.core.ui.utils.BaseViewModel +import org.mifos.mobile.core.ui.utils.ScreenUiState class LoginViewModel( private val userAuthRepositoryImpl: UserAuthRepository, private val userPreferencesRepositoryImpl: UserPreferencesRepository, savedStateHandle: SavedStateHandle, ) : BaseViewModel( - initialState = LoginState(dialogState = null), + initialState = LoginState(uiState = ScreenUiState.Success), ) { private var loginJob: Job? = null @@ -92,12 +90,12 @@ class LoginViewModel( private fun handleLoginResult(action: LoginAction.Internal.ReceiveLoginResult) { when (action.loginResult) { is DataState.Error -> { - val message = action.loginResult.exception.message ?: "Error logging in" updateState { it.copy( - dialogState = null, isError = true, - errorMsg = message, + uiState = ScreenUiState.Success, + showOverlay = false, + dialogState = LoginState.DialogState.Error(action.loginResult.message), userNameError = Res.string.feature_sign_in_username_error, passwordError = Res.string.feature_sign_in_password_error, ) @@ -105,11 +103,11 @@ class LoginViewModel( } is DataState.Loading -> { - updateState { it.copy(dialogState = LoginState.DialogState.Loading) } + updateState { it.copy(showOverlay = true) } } is DataState.Success -> { - updateState { it.copy(dialogState = null) } + updateState { it.copy(showOverlay = false) } val user = action.loginResult.data val userData = UserData( userId = user.userId, @@ -139,7 +137,7 @@ class LoginViewModel( ) { loginJob?.cancel() - updateState { it.copy(dialogState = LoginState.DialogState.Loading) } + updateState { it.copy(showOverlay = true) } loginJob = viewModelScope.launch { delay(300) @@ -150,27 +148,20 @@ class LoginViewModel( } } -@Parcelize data class LoginState( val username: String = "", - @IgnoredOnParcel val password: String = "", val isPasswordVisible: Boolean = false, val clientName: String = "", val isError: Boolean = false, - @IgnoredOnParcel val userNameError: StringResource? = null, - @IgnoredOnParcel val passwordError: StringResource? = null, - val errorMsg: String = "", - val dialogState: DialogState?, -) : Parcelable { - sealed interface DialogState : Parcelable { - @Parcelize + val dialogState: DialogState? = null, + val uiState: ScreenUiState?, + val showOverlay: Boolean = false, +) { + sealed interface DialogState { data class Error(val message: String) : DialogState - - @Parcelize - data object Loading : DialogState } val isLoginButtonEnabled: Boolean diff --git a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/otpAuthentication/OtpAuthenticationScreen.kt b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/otpAuthentication/OtpAuthenticationScreen.kt index 9586530a70..73547d1132 100644 --- a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/otpAuthentication/OtpAuthenticationScreen.kt +++ b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/otpAuthentication/OtpAuthenticationScreen.kt @@ -25,7 +25,6 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -51,10 +50,8 @@ import org.jetbrains.compose.ui.tooling.preview.Preview import org.koin.compose.viewmodel.koinViewModel import org.mifos.mobile.core.common.Constants import org.mifos.mobile.core.designsystem.component.BasicDialogState -import org.mifos.mobile.core.designsystem.component.LoadingDialogState import org.mifos.mobile.core.designsystem.component.MifosBasicDialog import org.mifos.mobile.core.designsystem.component.MifosButton -import org.mifos.mobile.core.designsystem.component.MifosLoadingDialog import org.mifos.mobile.core.designsystem.component.MifosOutlinedButton import org.mifos.mobile.core.designsystem.component.MifosOutlinedTextField import org.mifos.mobile.core.designsystem.component.MifosScaffold @@ -64,7 +61,9 @@ import org.mifos.mobile.core.designsystem.theme.DesignToken import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.designsystem.theme.MifosTypography import org.mifos.mobile.core.ui.component.MifosPoweredCard +import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay import org.mifos.mobile.core.ui.utils.EventsEffect +import org.mifos.mobile.core.ui.utils.ScreenUiState @Composable internal fun OtpAuthenticationScreen( @@ -127,10 +126,6 @@ private fun OtpAuthDialogs( onDismissRequest = onDismissRequest, ) - is OtpAuthState.DialogState.Loading -> MifosLoadingDialog( - visibilityState = LoadingDialogState.Shown, - ) - null -> Unit } } @@ -150,41 +145,51 @@ internal fun OptAuthScreenContent( } }, ) { - Column( - modifier = Modifier.fillMaxSize() - .padding(DesignToken.padding.large) - .padding(top = DesignToken.padding.large) - .statusBarsPadding(), + when (state.uiState) { + ScreenUiState.Success -> { + Column( + modifier = Modifier.fillMaxSize() + .padding(DesignToken.padding.large) + .padding(top = DesignToken.padding.large) + .statusBarsPadding(), - ) { - Text( - text = stringResource(Res.string.feature_otp_title), - color = MaterialTheme.colorScheme.onBackground, - style = MifosTypography.headlineMedium, - ) + ) { + Text( + text = stringResource(Res.string.feature_otp_title), + color = MaterialTheme.colorScheme.onBackground, + style = MifosTypography.headlineMedium, + ) - Spacer(modifier = Modifier.height(DesignToken.spacing.medium)) + Spacer(modifier = Modifier.height(DesignToken.spacing.medium)) - Text( - text = stringResource(Res.string.feature_otp_subtitle), - color = MaterialTheme.colorScheme.onBackground, - style = MifosTypography.titleSmallEmphasized, - ) + Text( + text = stringResource(Res.string.feature_otp_subtitle), + color = MaterialTheme.colorScheme.onBackground, + style = MifosTypography.titleSmallEmphasized, + ) - Spacer(modifier = Modifier.height(DesignToken.spacing.medium)) + Spacer(modifier = Modifier.height(DesignToken.spacing.medium)) - Text( - text = stringResource(Res.string.feature_otp_message), - color = MaterialTheme.colorScheme.secondary, - style = MifosTypography.bodySmall, - ) + Text( + text = stringResource(Res.string.feature_otp_message), + color = MaterialTheme.colorScheme.secondary, + style = MifosTypography.bodySmall, + ) - Spacer(modifier = Modifier.height(24.dp)) + Spacer(modifier = Modifier.height(24.dp)) - OtpInputForm( - state = state, - onAction = onAction, - ) + OtpInputForm( + state = state, + onAction = onAction, + ) + } + + if (state.showOverlay) { + MifosProgressIndicatorOverlay() + } + } + + else -> { } } } } @@ -208,11 +213,6 @@ internal fun OtpInputForm( label = stringResource(Res.string.feature_otp_request_id_label), shape = DesignToken.shapes.medium, textStyle = MifosTypography.bodyLarge, - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - unfocusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - errorBorderColor = MaterialTheme.colorScheme.error, - ), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, @@ -242,11 +242,6 @@ internal fun OtpInputForm( label = stringResource(Res.string.feature_otp_authentication_code_label), shape = DesignToken.shapes.medium, textStyle = MifosTypography.bodyLarge, - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - unfocusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - errorBorderColor = MaterialTheme.colorScheme.error, - ), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, diff --git a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/otpAuthentication/OtpAuthenticationViewModel.kt b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/otpAuthentication/OtpAuthenticationViewModel.kt index ea0a657643..fafaa78445 100644 --- a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/otpAuthentication/OtpAuthenticationViewModel.kt +++ b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/otpAuthentication/OtpAuthenticationViewModel.kt @@ -34,6 +34,7 @@ import org.mifos.mobile.core.common.DataState import org.mifos.mobile.core.data.repository.UserAuthRepository import org.mifos.mobile.core.model.EventType import org.mifos.mobile.core.ui.utils.BaseViewModel +import org.mifos.mobile.core.ui.utils.ScreenUiState import org.mifos.mobile.feature.auth.login.LoginRoute internal class OtpAuthenticationViewModel( @@ -145,7 +146,7 @@ internal class OtpAuthenticationViewModel( viewModelScope.launch { mutableStateFlow.update { it.copy( - dialogState = OtpAuthState.DialogState.Loading, + showOverlay = true, ) } delay(3000) @@ -174,7 +175,7 @@ internal class OtpAuthenticationViewModel( // ), // ) // } - mutableStateFlow.update { it.copy(dialogState = OtpAuthState.DialogState.Loading) } + mutableStateFlow.update { it.copy(showOverlay = true) } viewModelScope.launch { val result = userAuthRepositoryImpl.verifyUser(state.otp, state.requestId) sendAction(OtpAuthAction.Internal.ReceiveOtpResult(result)) @@ -187,6 +188,7 @@ internal class OtpAuthenticationViewModel( is DataState.Error -> { mutableStateFlow.update { it.copy( + showOverlay = false, dialogState = OtpAuthState.DialogState.Error(action.message), ) } @@ -204,7 +206,7 @@ internal class OtpAuthenticationViewModel( DataState.Loading -> { mutableStateFlow.update { it.copy( - dialogState = OtpAuthState.DialogState.Loading, + showOverlay = true, ) } } @@ -252,17 +254,17 @@ internal data class OtpAuthState( val requestId: String = "", val requestIdError: StringResource? = null, - val dialogState: DialogState?, + val dialogState: DialogState? = null, + val uiState: ScreenUiState? = ScreenUiState.Success, + val showOverlay: Boolean = false, ) { sealed interface DialogState { data class Error(val message: String) : DialogState - - data object Loading : DialogState } val isNextButtonEnabled - get() = otp.isNotBlank() && requestId.isNotBlank() + get() = otp.isNotBlank() } internal sealed interface OtpAuthAction { diff --git a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/recoverPassword/RecoverPasswordScreen.kt b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/recoverPassword/RecoverPasswordScreen.kt index 73f438c13d..3051083eb4 100644 --- a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/recoverPassword/RecoverPasswordScreen.kt +++ b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/recoverPassword/RecoverPasswordScreen.kt @@ -26,7 +26,6 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -47,10 +46,8 @@ import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.ui.tooling.preview.Preview import org.koin.compose.viewmodel.koinViewModel import org.mifos.mobile.core.designsystem.component.BasicDialogState -import org.mifos.mobile.core.designsystem.component.LoadingDialogState import org.mifos.mobile.core.designsystem.component.MifosBasicDialog import org.mifos.mobile.core.designsystem.component.MifosButton -import org.mifos.mobile.core.designsystem.component.MifosLoadingDialog import org.mifos.mobile.core.designsystem.component.MifosOutlinedTextField import org.mifos.mobile.core.designsystem.component.MifosScaffold import org.mifos.mobile.core.designsystem.component.MifosTextFieldConfig @@ -58,7 +55,9 @@ import org.mifos.mobile.core.designsystem.icon.MifosIcons import org.mifos.mobile.core.designsystem.theme.DesignToken import org.mifos.mobile.core.designsystem.theme.MifosTypography import org.mifos.mobile.core.ui.component.MifosPoweredCard +import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay import org.mifos.mobile.core.ui.utils.EventsEffect +import org.mifos.mobile.core.ui.utils.ScreenUiState @Composable internal fun RecoverPasswordScreen( @@ -115,34 +114,44 @@ internal fun RecoverPasswordScreen( } }, ) { - Column( - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - .padding(DesignToken.padding.large) - .padding(top = DesignToken.padding.large) - .statusBarsPadding(), - ) { - Text( - text = stringResource(Res.string.feature_recover_now_title), - style = MifosTypography.headlineMedium, - color = MaterialTheme.colorScheme.onBackground, - ) + when (state.uiState) { + ScreenUiState.Success -> { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(DesignToken.padding.large) + .padding(top = DesignToken.padding.large) + .statusBarsPadding(), + ) { + Text( + text = stringResource(Res.string.feature_recover_now_title), + style = MifosTypography.headlineMedium, + color = MaterialTheme.colorScheme.onBackground, + ) - Spacer(modifier = Modifier.height(12.dp)) + Spacer(modifier = Modifier.height(12.dp)) - Text( - text = stringResource(Res.string.feature_recover_now_message), - style = MifosTypography.bodySmall, - color = MaterialTheme.colorScheme.secondary, - ) + Text( + text = stringResource(Res.string.feature_recover_now_message), + style = MifosTypography.bodySmall, + color = MaterialTheme.colorScheme.secondary, + ) - Spacer(modifier = Modifier.height(24.dp)) + Spacer(modifier = Modifier.height(24.dp)) - ForgotPasswordInputBox( - state = state, - onAction = onAction, - ) + ForgotPasswordInputBox( + state = state, + onAction = onAction, + ) + } + + if (state.showOverlay) { + MifosProgressIndicatorOverlay() + } + } + + else -> { } } } } @@ -163,11 +172,6 @@ internal fun ForgotPasswordInputBox( label = stringResource(Res.string.feature_recover_now_phone_number_label), shape = DesignToken.shapes.medium, textStyle = MifosTypography.bodyLarge, - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - unfocusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - errorBorderColor = MaterialTheme.colorScheme.error, - ), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Phone, @@ -196,11 +200,6 @@ internal fun ForgotPasswordInputBox( label = stringResource(Res.string.feature_recover_now_email_label), shape = DesignToken.shapes.medium, textStyle = MifosTypography.bodyLarge, - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - unfocusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - errorBorderColor = MaterialTheme.colorScheme.error, - ), config = MifosTextFieldConfig( isError = state.emailError != null, errorText = state.emailError?.let { stringResource(it) }, @@ -260,12 +259,6 @@ private fun RecoverPasswordDialogs( onDismissRequest: () -> Unit, ) { when (dialogState) { - is RecoverPasswordState.DialogState.Loading -> { - MifosLoadingDialog( - visibilityState = LoadingDialogState.Shown, - ) - } - is RecoverPasswordState.DialogState.Error -> { MifosBasicDialog( visibilityState = BasicDialogState.Shown( diff --git a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/recoverPassword/RecoverPasswordViewModel.kt b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/recoverPassword/RecoverPasswordViewModel.kt index 584f659865..02543cb8bf 100644 --- a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/recoverPassword/RecoverPasswordViewModel.kt +++ b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/recoverPassword/RecoverPasswordViewModel.kt @@ -25,6 +25,7 @@ import mifos_mobile.feature.auth.generated.resources.feature_recover_now_phone_n import mifos_mobile.feature.auth.generated.resources.feature_recover_now_phone_number_required import org.jetbrains.compose.resources.StringResource import org.mifos.mobile.core.ui.utils.BaseViewModel +import org.mifos.mobile.core.ui.utils.ScreenUiState import org.mifos.mobile.core.ui.utils.ValidationHelper import org.mifos.mobile.feature.auth.setNewPassword.SetPasswordRoute @@ -138,9 +139,9 @@ internal class RecoverPasswordViewModel : @OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class) private fun requestRecoveryCode() { viewModelScope.launch { - mutableStateFlow.update { it.copy(dialogState = RecoverPasswordState.DialogState.Loading) } + mutableStateFlow.update { it.copy(showOverlay = true) } delay(3000) - dismissDialog() + mutableStateFlow.update { it.copy(showOverlay = false) } sendEvent( RecoverPasswordEvent.NavigateToOtpAuth( nextRoute = SetPasswordRoute::class.serializer().descriptor.serialName, @@ -166,10 +167,10 @@ data class RecoverPasswordState( var emailError: StringResource? = null, val dialogState: DialogState? = null, + val uiState: ScreenUiState? = ScreenUiState.Success, + val showOverlay: Boolean = false, ) { sealed interface DialogState { - data object Loading : DialogState - data class Error(val message: String) : DialogState } diff --git a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/registration/RegistrationScreen.kt b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/registration/RegistrationScreen.kt index 1391dde001..c3a8dbf941 100644 --- a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/registration/RegistrationScreen.kt +++ b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/registration/RegistrationScreen.kt @@ -29,7 +29,6 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -40,7 +39,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.text.input.ImeAction @@ -70,23 +68,22 @@ import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.ui.tooling.preview.Preview import org.koin.compose.viewmodel.koinViewModel import org.mifos.mobile.core.designsystem.component.BasicDialogState -import org.mifos.mobile.core.designsystem.component.LoadingDialogState import org.mifos.mobile.core.designsystem.component.MifosBasicDialog import org.mifos.mobile.core.designsystem.component.MifosButton -import org.mifos.mobile.core.designsystem.component.MifosLoadingDialog import org.mifos.mobile.core.designsystem.component.MifosOutlinedTextField import org.mifos.mobile.core.designsystem.component.MifosPasswordField import org.mifos.mobile.core.designsystem.component.MifosScaffold import org.mifos.mobile.core.designsystem.component.MifosTextFieldConfig import org.mifos.mobile.core.designsystem.icon.MifosIcons -import org.mifos.mobile.core.designsystem.theme.AppColors import org.mifos.mobile.core.designsystem.theme.DesignToken import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.designsystem.theme.MifosTypography import org.mifos.mobile.core.ui.CombinedPasswordErrorCard import org.mifos.mobile.core.ui.PasswordStrengthIndicator import org.mifos.mobile.core.ui.component.MifosPoweredCard +import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay import org.mifos.mobile.core.ui.utils.EventsEffect +import org.mifos.mobile.core.ui.utils.ScreenUiState @Composable internal fun RegistrationScreen( @@ -143,10 +140,6 @@ private fun SignUpDialog( onDismissRequest = onDismissRequest, ) - is SignUpState.SignUpDialog.Loading -> MifosLoadingDialog( - visibilityState = LoadingDialogState.Shown, - ) - null -> Unit } } @@ -166,11 +159,21 @@ private fun RegistrationScreen( } }, ) { - RegistrationScreenContent( - state = state, - onAction = onAction, - modifier = modifier, - ) + when (state.uiState) { + ScreenUiState.Success -> { + RegistrationScreenContent( + state = state, + onAction = onAction, + modifier = modifier, + ) + + if (state.showOverlay) { + MifosProgressIndicatorOverlay() + } + } + + else -> {} + } } } @@ -208,7 +211,7 @@ private fun RegistrationScreenContent( Text( text = stringResource(Res.string.feature_signup_title), style = MifosTypography.headlineMedium, - color = AppColors.customBlack, + color = MaterialTheme.colorScheme.onSurface, ) } @@ -306,7 +309,7 @@ fun MifosInputField( tint = if (config.errorText != null) { MaterialTheme.colorScheme.error } else { - Color.Unspecified + MaterialTheme.colorScheme.onSurface }, ) } @@ -341,11 +344,6 @@ fun MifosInputField( showPasswordChange = { config.onTogglePasswordVisibility?.invoke() }, - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - unfocusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - errorBorderColor = MaterialTheme.colorScheme.error, - ), isError = config.errorText != null, hint = config.errorText?.let { stringResource(it) }, ) @@ -379,11 +377,6 @@ fun MifosInputField( label = stringResource(config.labelRes), shape = DesignToken.shapes.medium, textStyle = MifosTypography.bodyLarge, - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - unfocusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - errorBorderColor = MaterialTheme.colorScheme.error, - ), config = MifosTextFieldConfig( isError = config.errorText != null, errorText = config.errorText?.let { stringResource(it) }, diff --git a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/registration/RegistrationViewModel.kt b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/registration/RegistrationViewModel.kt index a6ccb31f11..2c2dc235f3 100644 --- a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/registration/RegistrationViewModel.kt +++ b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/registration/RegistrationViewModel.kt @@ -34,6 +34,7 @@ import org.mifos.mobile.core.ui.utils.BaseViewModel import org.mifos.mobile.core.ui.utils.PasswordChecker import org.mifos.mobile.core.ui.utils.PasswordStrength import org.mifos.mobile.core.ui.utils.PasswordStrengthResult +import org.mifos.mobile.core.ui.utils.ScreenUiState import org.mifos.mobile.core.ui.utils.ValidationHelper /** @@ -523,7 +524,7 @@ class RegistrationViewModel( // // sendEvent(SignUpEvent.NavigateToUploadDocuments) // } - updateState { it.copy(dialogState = SignUpState.SignUpDialog.Loading) } + updateState { it.copy(showOverlay = true) } viewModelScope.launch { val response = userAuthRepositoryImpl.registerUser( accountNumber = state.customerAccount, @@ -549,7 +550,7 @@ class RegistrationViewModel( private fun handleRegisterResult(action: SignUpAction.Internal.ReceiveRegisterResult) { when (val result = action.registerResult) { is DataState.Success -> { - updateState { it.copy(dialogState = null) } + updateState { it.copy(dialogState = null, showOverlay = false) } sendEvent( SignUpEvent.NavigateToUploadDocuments, ) @@ -558,17 +559,13 @@ class RegistrationViewModel( is DataState.Error -> { updateState { it.copy( - dialogState = null, - ) - } - updateState { - it.copy( + showOverlay = false, dialogState = SignUpState.SignUpDialog.Error(result.message), ) } } - DataState.Loading -> updateState { it.copy(dialogState = SignUpState.SignUpDialog.Loading) } + DataState.Loading -> updateState { it.copy(showOverlay = true) } } } @@ -599,6 +596,8 @@ data class SignUpState( val mobileNumber: String = "", val dialogState: SignUpDialog? = null, + val uiState: ScreenUiState = ScreenUiState.Success, + val showOverlay: Boolean = false, val isPasswordChanged: Boolean = false, val isPasswordVisible: Boolean = false, @@ -620,8 +619,6 @@ data class SignUpState( * Dialogs to show loading or error states during sign-up. */ sealed interface SignUpDialog { - data object Loading : SignUpDialog - data class Error(val message: String) : SignUpDialog } diff --git a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/setNewPassword/SetPasswordScreen.kt b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/setNewPassword/SetPasswordScreen.kt index 28067c29cb..695c4035a2 100644 --- a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/setNewPassword/SetPasswordScreen.kt +++ b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/setNewPassword/SetPasswordScreen.kt @@ -24,7 +24,6 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -46,10 +45,8 @@ import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.ui.tooling.preview.Preview import org.koin.compose.viewmodel.koinViewModel import org.mifos.mobile.core.designsystem.component.BasicDialogState -import org.mifos.mobile.core.designsystem.component.LoadingDialogState import org.mifos.mobile.core.designsystem.component.MifosBasicDialog import org.mifos.mobile.core.designsystem.component.MifosButton -import org.mifos.mobile.core.designsystem.component.MifosLoadingDialog import org.mifos.mobile.core.designsystem.component.MifosPasswordField import org.mifos.mobile.core.designsystem.component.MifosScaffold import org.mifos.mobile.core.designsystem.theme.DesignToken @@ -58,7 +55,9 @@ import org.mifos.mobile.core.designsystem.theme.MifosTypography import org.mifos.mobile.core.ui.CombinedPasswordErrorCard import org.mifos.mobile.core.ui.PasswordStrengthIndicator import org.mifos.mobile.core.ui.component.MifosPoweredCard +import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay import org.mifos.mobile.core.ui.utils.EventsEffect +import org.mifos.mobile.core.ui.utils.ScreenUiState @Composable internal fun SetPasswordScreen( @@ -112,10 +111,6 @@ private fun SetPasswordDialogs( onDismissRequest = onDismissRequest, ) - is SetPasswordState.DialogState.Loading -> MifosLoadingDialog( - visibilityState = LoadingDialogState.Shown, - ) - null -> Unit } } @@ -136,34 +131,44 @@ internal fun SetPasswordScreen( } }, ) { - Column( - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - .padding(top = DesignToken.padding.large) - .padding(DesignToken.padding.large) - .statusBarsPadding(), - ) { - Text( - text = stringResource(Res.string.feature_set_new_password_title), - style = MifosTypography.headlineMedium, - color = MaterialTheme.colorScheme.onBackground, - ) + when (state.uiState) { + ScreenUiState.Success -> { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(top = DesignToken.padding.large) + .padding(DesignToken.padding.large) + .statusBarsPadding(), + ) { + Text( + text = stringResource(Res.string.feature_set_new_password_title), + style = MifosTypography.headlineMedium, + color = MaterialTheme.colorScheme.onBackground, + ) - Spacer(modifier = Modifier.height(12.dp)) + Spacer(modifier = Modifier.height(12.dp)) - Text( - text = stringResource(Res.string.feature_set_new_password_message), - style = MifosTypography.bodySmall, - color = MaterialTheme.colorScheme.secondary, - ) + Text( + text = stringResource(Res.string.feature_set_new_password_message), + style = MifosTypography.bodySmall, + color = MaterialTheme.colorScheme.secondary, + ) - Spacer(modifier = Modifier.height(24.dp)) + Spacer(modifier = Modifier.height(24.dp)) - SetPasswordInputBox( - state = state, - onAction = onAction, - ) + SetPasswordInputBox( + state = state, + onAction = onAction, + ) + } + + if (state.showOverlay) { + MifosProgressIndicatorOverlay() + } + } + + else -> { } } } } @@ -193,11 +198,6 @@ internal fun SetPasswordInputBox( showPasswordChange = { onAction(SetPasswordAction.OnTogglePassword) }, - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - unfocusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - errorBorderColor = MaterialTheme.colorScheme.error, - ), isError = state.passwordError != null, hint = state.passwordError?.let { stringResource(it) }, ) @@ -234,11 +234,6 @@ internal fun SetPasswordInputBox( showPasswordChange = { onAction(SetPasswordAction.OnToggleConfirmPassword) }, - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - unfocusedBorderColor = MaterialTheme.colorScheme.secondaryContainer, - errorBorderColor = MaterialTheme.colorScheme.error, - ), isError = state.confirmPasswordError != null, hint = state.confirmPasswordError?.let { stringResource(it) }, keyboardType = KeyboardType.Password, diff --git a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/setNewPassword/SetPasswordViewModel.kt b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/setNewPassword/SetPasswordViewModel.kt index db9c3f02a0..4c70da8ad7 100644 --- a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/setNewPassword/SetPasswordViewModel.kt +++ b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/setNewPassword/SetPasswordViewModel.kt @@ -31,6 +31,7 @@ import org.mifos.mobile.core.ui.utils.BaseViewModel import org.mifos.mobile.core.ui.utils.PasswordChecker import org.mifos.mobile.core.ui.utils.PasswordStrength import org.mifos.mobile.core.ui.utils.PasswordStrengthResult +import org.mifos.mobile.core.ui.utils.ScreenUiState import org.mifos.mobile.feature.auth.login.LoginRoute internal class SetPasswordViewModel : BaseViewModel( @@ -181,9 +182,9 @@ internal class SetPasswordViewModel : BaseViewModel = emptyList(), val passwordStrengthState: PasswordStrengthState = PasswordStrengthState.NONE, - val dialogState: DialogState?, + val dialogState: DialogState? = null, + val uiState: ScreenUiState? = ScreenUiState.Success, + val showOverlay: Boolean = false, ) { sealed interface DialogState { data class Error(val message: String) : DialogState - - data object Loading : DialogState } val isSubmitButtonEnabled: Boolean diff --git a/feature/loan-account/src/commonMain/kotlin/org/mifos/mobile/feature/loanaccount/component/RepaymentPeriodCard.kt b/feature/loan-account/src/commonMain/kotlin/org/mifos/mobile/feature/loanaccount/component/RepaymentPeriodCard.kt index 15c125c603..d32248e88e 100644 --- a/feature/loan-account/src/commonMain/kotlin/org/mifos/mobile/feature/loanaccount/component/RepaymentPeriodCard.kt +++ b/feature/loan-account/src/commonMain/kotlin/org/mifos/mobile/feature/loanaccount/component/RepaymentPeriodCard.kt @@ -31,7 +31,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import kotlinx.datetime.Clock import mifos_mobile.feature.loan_account.generated.resources.Res import mifos_mobile.feature.loan_account.generated.resources.feature_loan_due import mifos_mobile.feature.loan_account.generated.resources.feature_loan_installment_number @@ -47,7 +46,10 @@ import org.mifos.mobile.core.designsystem.theme.AppColors import org.mifos.mobile.core.designsystem.theme.DesignToken import org.mifos.mobile.core.designsystem.theme.MifosTypography import org.mifos.mobile.core.model.entity.accounts.loan.Periods +import kotlin.time.Clock +import kotlin.time.ExperimentalTime +@OptIn(ExperimentalTime::class) @Composable fun RepaymentScheduleItem( period: Periods, diff --git a/feature/loan-application/src/commonMain/kotlin/org/mifos/mobile/feature/loan/application/loanApplication/LoanApplyViewModel.kt b/feature/loan-application/src/commonMain/kotlin/org/mifos/mobile/feature/loan/application/loanApplication/LoanApplyViewModel.kt index b6a92f51f4..8084b99c1c 100644 --- a/feature/loan-application/src/commonMain/kotlin/org/mifos/mobile/feature/loan/application/loanApplication/LoanApplyViewModel.kt +++ b/feature/loan-application/src/commonMain/kotlin/org/mifos/mobile/feature/loan/application/loanApplication/LoanApplyViewModel.kt @@ -19,7 +19,6 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import kotlinx.datetime.Clock import mifos_mobile.feature.loan_application.generated.resources.Res import mifos_mobile.feature.loan_application.generated.resources.feature_apply_loan_error_amount_too_large import mifos_mobile.feature.loan_application.generated.resources.feature_apply_loan_error_amount_too_small @@ -44,6 +43,8 @@ import org.mifos.mobile.core.model.entity.templates.loans.LoanTemplate import org.mifos.mobile.core.ui.utils.AmountValidationResult import org.mifos.mobile.core.ui.utils.BaseViewModel import org.mifos.mobile.core.ui.utils.ValidationHelper +import kotlin.time.Clock +import kotlin.time.ExperimentalTime import org.mifos.mobile.core.model.entity.Currency as ModelCurrency /** @@ -696,6 +697,7 @@ internal data class LoanApplicationState( /** * The current time in milliseconds, used for date pickers. */ + @OptIn(ExperimentalTime::class) val currentDate: Long get() = Clock.System.now().toEpochMilliseconds() @@ -735,6 +737,7 @@ internal data class LoanApplicationState( * The effective submission date, which is the later of today's date or the * client's activation date. */ + @OptIn(ExperimentalTime::class) val submittedOnDate: String get() { val todayMillis = Clock.System.now().toEpochMilliseconds() diff --git a/feature/savings-application/src/commonMain/kotlin/org/mifos/mobile/feature/savings/application/fillApplication/FillApplicationViewModel.kt b/feature/savings-application/src/commonMain/kotlin/org/mifos/mobile/feature/savings/application/fillApplication/FillApplicationViewModel.kt index 0b97469c55..a9fa30d0bd 100644 --- a/feature/savings-application/src/commonMain/kotlin/org/mifos/mobile/feature/savings/application/fillApplication/FillApplicationViewModel.kt +++ b/feature/savings-application/src/commonMain/kotlin/org/mifos/mobile/feature/savings/application/fillApplication/FillApplicationViewModel.kt @@ -18,7 +18,6 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import kotlinx.datetime.Clock import mifos_mobile.feature.savings_application.generated.resources.Res import mifos_mobile.feature.savings_application.generated.resources.feature_apply_savings_error_amount_too_large import mifos_mobile.feature.savings_application.generated.resources.feature_apply_savings_error_amount_too_small @@ -54,6 +53,8 @@ import org.mifos.mobile.core.ui.utils.ResultNavigator import org.mifos.mobile.core.ui.utils.ValidationHelper import org.mifos.mobile.core.ui.utils.observe import kotlin.String +import kotlin.time.Clock +import kotlin.time.ExperimentalTime import org.mifos.mobile.core.model.entity.Currency as ModelCurrency private const val DEFAULT_DECIMAL_PLACES = 2 @@ -692,6 +693,7 @@ internal data class SavingsApplicationState( /** * The current date as a formatted string. */ + @OptIn(ExperimentalTime::class) val currentDate: String get() = DateHelper.getDateMonthYearString(Clock.System.now().toEpochMilliseconds()) diff --git a/feature/savings-application/src/commonMain/kotlin/org/mifos/mobile/feature/savings/application/savingsApplication/SavingsApplyViewModel.kt b/feature/savings-application/src/commonMain/kotlin/org/mifos/mobile/feature/savings/application/savingsApplication/SavingsApplyViewModel.kt index 4c36bdf674..f9981c5efc 100644 --- a/feature/savings-application/src/commonMain/kotlin/org/mifos/mobile/feature/savings/application/savingsApplication/SavingsApplyViewModel.kt +++ b/feature/savings-application/src/commonMain/kotlin/org/mifos/mobile/feature/savings/application/savingsApplication/SavingsApplyViewModel.kt @@ -17,7 +17,6 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import kotlinx.datetime.Clock import mifos_mobile.feature.savings_application.generated.resources.Res import mifos_mobile.feature.savings_application.generated.resources.feature_apply_savings_error_product_empty import mifos_mobile.feature.savings_application.generated.resources.feature_apply_savings_error_server @@ -34,6 +33,8 @@ import org.mifos.mobile.core.datastore.UserPreferencesRepository import org.mifos.mobile.core.model.entity.templates.savings.SavingsAccountTemplate import org.mifos.mobile.core.model.entity.templates.savings.SavingsProduct import org.mifos.mobile.core.ui.utils.BaseViewModel +import kotlin.time.Clock +import kotlin.time.ExperimentalTime /** * A `ViewModel` for the savings application screen, responsible for handling user input, @@ -528,6 +529,7 @@ internal data class SavingsApplicationState( id to name } + @OptIn(ExperimentalTime::class) val submittedOnDate: String get() { val todayMillis = Clock.System.now().toEpochMilliseconds() diff --git a/feature/share-application/src/commonMain/kotlin/org/mifos/mobile/feature/share/application/fillApplication/FillApplicationViewModel.kt b/feature/share-application/src/commonMain/kotlin/org/mifos/mobile/feature/share/application/fillApplication/FillApplicationViewModel.kt index 4443ac54f1..94f8ef0c91 100644 --- a/feature/share-application/src/commonMain/kotlin/org/mifos/mobile/feature/share/application/fillApplication/FillApplicationViewModel.kt +++ b/feature/share-application/src/commonMain/kotlin/org/mifos/mobile/feature/share/application/fillApplication/FillApplicationViewModel.kt @@ -19,7 +19,6 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import kotlinx.datetime.Clock import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.InternalSerializationApi import mifos_mobile.feature.share_application.generated.resources.Res @@ -62,6 +61,8 @@ import org.mifos.mobile.core.ui.utils.ResultNavigator import org.mifos.mobile.core.ui.utils.ValidationHelper import org.mifos.mobile.core.ui.utils.observe import kotlin.String +import kotlin.time.Clock +import kotlin.time.ExperimentalTime import org.mifos.mobile.core.model.entity.Currency as ModelCurrency private const val DEFAULT_DECIMAL_PLACES = 2 @@ -816,6 +817,7 @@ internal data class ShareApplicationState( /** * The current date formatted as a string. */ + @OptIn(ExperimentalTime::class) val currentDate: String get() = DateHelper.getDateMonthYearString(Clock.System.now().toEpochMilliseconds()) diff --git a/feature/share-application/src/commonMain/kotlin/org/mifos/mobile/feature/share/application/shareApplication/ShareApplyViewModel.kt b/feature/share-application/src/commonMain/kotlin/org/mifos/mobile/feature/share/application/shareApplication/ShareApplyViewModel.kt index 84b7c0ad89..c51c87cc0a 100644 --- a/feature/share-application/src/commonMain/kotlin/org/mifos/mobile/feature/share/application/shareApplication/ShareApplyViewModel.kt +++ b/feature/share-application/src/commonMain/kotlin/org/mifos/mobile/feature/share/application/shareApplication/ShareApplyViewModel.kt @@ -15,7 +15,6 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import kotlinx.datetime.Clock import mifos_mobile.feature.share_application.generated.resources.Res import mifos_mobile.feature.share_application.generated.resources.feature_apply_share_error_server import mifos_mobile.feature.share_application.generated.resources.feature_apply_share_error_submit_failed @@ -31,6 +30,8 @@ import org.mifos.mobile.core.model.entity.Page import org.mifos.mobile.core.model.entity.templates.savings.SavingsAccountTemplate import org.mifos.mobile.core.model.entity.templates.shares.ShareProduct import org.mifos.mobile.core.ui.utils.BaseViewModel +import kotlin.time.Clock +import kotlin.time.ExperimentalTime /** * `ViewModel` for the savings application screen, responsible for handling user input, @@ -393,6 +394,7 @@ internal data class ShareApplicationState( } .toMap() + @OptIn(ExperimentalTime::class) val submittedOnDate: String get() { val todayMillis = Clock.System.now().toEpochMilliseconds() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 77b8622dd7..e8a1b8cbfe 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -52,6 +52,8 @@ uiVersion = "0.1.6" versionCatalogLinterVersion = "1.0.3" zxingVersion = "3.5.3" moduleGraph = "2.7.1" +lottie = "6.6.7" +compottie = "2.0.0-rc05" # Kotlin KMP Dependencies kotlin = "2.1.0" @@ -253,6 +255,12 @@ filekit-core = { group = "io.github.vinceglb", name = "filekit-core", version.re filekit-compose = { group = "io.github.vinceglb", name = "filekit-compose", version.ref = "fileKit" } filekit-dialog-compose = { group = "io.github.vinceglb", name = "filekit-dialogs-compose", version.ref = "fileKitDialog" } qrose = { group = "io.github.alexzhirkevich", name="qrose", version.ref = "qroseVersion" } +lottie-compose = { group = "com.airbnb.android", name = "lottie-compose", version.ref = "lottie" } +compottie = { module = "io.github.alexzhirkevich:compottie", version.ref = "compottie" } +compottie-lite = { module = "io.github.alexzhirkevich:compottie-lite", version.ref = "compottie" } +compottie-dot = { module = "io.github.alexzhirkevich:compottie-dot", version.ref = "compottie" } +compottie-network = { module = "io.github.alexzhirkevich:compottie-network", version.ref = "compottie" } +compottie-resources = { module = "io.github.alexzhirkevich:compottie-resources", version.ref = "compottie" } kermit-logging = { group = "co.touchlab", name = "kermit", version.ref = "kermit" } kermit-simple = { group = "co.touchlab", name = "kermit-simple", version.ref = "kermit" } From 5bf49277209c7d332fa2c0909cc0d034b63fd0b8 Mon Sep 17 00:00:00 2001 From: Nagarjuna Date: Fri, 29 Aug 2025 08:40:06 +0530 Subject: [PATCH 2/3] refactor: update kotlinx date time version & created constant for file --- .../org/mifos/mobile/core/common/DateHelper.kt | 2 ++ .../core/ui/component/MifosProgressIndicator.kt | 3 ++- .../mifos/mobile/core/ui/utils/LottieConstants.kt | 14 ++++++++++++++ .../ui/utils/PresentOrFutureSelectableDates.kt | 5 +---- .../accountTransactions/TransactionViewModel.kt | 4 +--- .../feature/accounts/accounts/AccountsViewModel.kt | 6 +----- .../mobile/feature/auth/uploadId/UploadIdScreen.kt | 5 ++--- .../loanaccount/component/RepaymentPeriodCard.kt | 4 +--- .../confirmDetails/ConfirmDetailsScreen.kt | 3 +-- .../application/loanApplication/LoanApplyScreen.kt | 5 ++--- .../loanApplication/LoanApplyViewModel.kt | 5 +---- .../fillApplication/FillApplicationViewModel.kt | 4 +--- .../savingsApplication/SavingsApplyViewModel.kt | 5 ++--- .../fillApplication/FillApplicationViewModel.kt | 4 +--- .../shareApplication/ShareApplyViewModel.kt | 4 +--- gradle/libs.versions.toml | 2 +- 16 files changed, 34 insertions(+), 41 deletions(-) create mode 100644 core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/LottieConstants.kt diff --git a/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/DateHelper.kt b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/DateHelper.kt index 3ea6fa96f7..d04df5a189 100644 --- a/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/DateHelper.kt +++ b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/DateHelper.kt @@ -25,6 +25,7 @@ import kotlinx.datetime.isoDayNumber import kotlinx.datetime.minus import kotlinx.datetime.toLocalDateTime import kotlin.time.Duration.Companion.days +import kotlin.time.ExperimentalTime @Suppress("TooManyFunctions") @OptIn(FormatStringsInDatetimeFormats::class) @@ -162,6 +163,7 @@ object DateHelper { private val monthNumberToAbbreviation = monthMap.entries.associate { (k, v) -> v to k } + @OptIn(ExperimentalTime::class) fun getDateAsLongFromList(integersOfDate: List?): Long? { if (integersOfDate == null) return null val dateStr = getDateAsString(integersOfDate) diff --git a/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosProgressIndicator.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosProgressIndicator.kt index f69d7027b0..a58eaec539 100644 --- a/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosProgressIndicator.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosProgressIndicator.kt @@ -28,6 +28,7 @@ import io.github.alexzhirkevich.compottie.rememberLottiePainter import mifos_mobile.core.ui.generated.resources.Res import org.jetbrains.compose.ui.tooling.preview.Preview import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme +import org.mifos.mobile.core.ui.utils.LottieConstants @Composable fun MifosProgressIndicator( @@ -35,7 +36,7 @@ fun MifosProgressIndicator( ) { val composition by rememberLottieComposition { LottieCompositionSpec.JsonString( - Res.readBytes("files/loading_animation.json").decodeToString(), + Res.readBytes(LottieConstants.LOADING_ANIMATION).decodeToString(), ) } val progress by animateLottieCompositionAsState(composition) diff --git a/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/LottieConstants.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/LottieConstants.kt new file mode 100644 index 0000000000..f03e1ed8e7 --- /dev/null +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/LottieConstants.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md + */ +package org.mifos.mobile.core.ui.utils + +object LottieConstants { + const val LOADING_ANIMATION = "files/loading_animation.json" +} diff --git a/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/PresentOrFutureSelectableDates.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/PresentOrFutureSelectableDates.kt index 83a7b8505e..5489dcaf9e 100644 --- a/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/PresentOrFutureSelectableDates.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/PresentOrFutureSelectableDates.kt @@ -11,22 +11,19 @@ package org.mifos.mobile.core.ui.utils import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.SelectableDates +import kotlinx.datetime.Clock import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime -import kotlin.time.Clock -import kotlin.time.ExperimentalTime @OptIn(ExperimentalMaterial3Api::class) object PresentOrFutureSelectableDates : SelectableDates { - @OptIn(ExperimentalTime::class) @ExperimentalMaterial3Api override fun isSelectableDate(utcTimeMillis: Long): Boolean { val currentTimeMillis = Clock.System.now().toEpochMilliseconds() return utcTimeMillis >= currentTimeMillis } - @OptIn(ExperimentalTime::class) override fun isSelectableYear(year: Int): Boolean { val currentYear = Clock.System.now() .toLocalDateTime(TimeZone.currentSystemDefault()) diff --git a/feature/accounts/src/commonMain/kotlin/org/mifos/mobile/feature/accounts/accountTransactions/TransactionViewModel.kt b/feature/accounts/src/commonMain/kotlin/org/mifos/mobile/feature/accounts/accountTransactions/TransactionViewModel.kt index d4938fc00e..d48c030699 100644 --- a/feature/accounts/src/commonMain/kotlin/org/mifos/mobile/feature/accounts/accountTransactions/TransactionViewModel.kt +++ b/feature/accounts/src/commonMain/kotlin/org/mifos/mobile/feature/accounts/accountTransactions/TransactionViewModel.kt @@ -17,6 +17,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import kotlinx.datetime.Clock import kotlinx.datetime.DateTimeUnit import kotlinx.datetime.LocalDate import kotlinx.datetime.TimeZone @@ -50,8 +51,6 @@ import org.mifos.mobile.feature.accounts.model.TransactionCheckboxStatus import org.mifos.mobile.feature.accounts.model.TransactionFilterType import org.mifos.mobile.feature.accounts.utils.StatusUtils import kotlin.collections.map -import kotlin.time.Clock -import kotlin.time.ExperimentalTime /** * ViewModel for managing the state and logic of the account transactions screen. @@ -453,7 +452,6 @@ internal class AccountsTransactionViewModel( * @param selectedFilters A list of [TransactionCheckboxStatus] representing the active checkbox filters. * @return A map of filtered transactions grouped by date. */ - @OptIn(ExperimentalTime::class) internal fun applyTransactionFilters( selectedFilters: List, ): Map> { diff --git a/feature/accounts/src/commonMain/kotlin/org/mifos/mobile/feature/accounts/accounts/AccountsViewModel.kt b/feature/accounts/src/commonMain/kotlin/org/mifos/mobile/feature/accounts/accounts/AccountsViewModel.kt index e09578877b..cc29f89f02 100644 --- a/feature/accounts/src/commonMain/kotlin/org/mifos/mobile/feature/accounts/accounts/AccountsViewModel.kt +++ b/feature/accounts/src/commonMain/kotlin/org/mifos/mobile/feature/accounts/accounts/AccountsViewModel.kt @@ -12,6 +12,7 @@ package org.mifos.mobile.feature.accounts.accounts import androidx.lifecycle.SavedStateHandle import androidx.navigation.toRoute import kotlinx.coroutines.flow.update +import kotlinx.datetime.Clock import org.jetbrains.compose.resources.StringResource import org.mifos.mobile.core.common.Constants import org.mifos.mobile.core.model.enums.AccountType @@ -19,8 +20,6 @@ import org.mifos.mobile.core.ui.utils.BaseViewModel import org.mifos.mobile.feature.accounts.model.CheckboxStatus import org.mifos.mobile.feature.accounts.model.FilterType import org.mifos.mobile.feature.accounts.utils.StatusUtils -import kotlin.time.Clock -import kotlin.time.ExperimentalTime /** * ViewModel responsible for managing the account screen state, @@ -41,7 +40,6 @@ internal class AccountsViewModel( observeAccountTypeAndInitCheckboxes() } - @OptIn(ExperimentalTime::class) override fun handleAction(action: AccountsAction) { when (action) { is AccountsAction.SetCheckboxFilterList -> { @@ -114,7 +112,6 @@ internal class AccountsViewModel( * Applies the selected checkboxes as filters, sets refresh signal, * and dismisses the filter dialog. */ - @OptIn(ExperimentalTime::class) private fun handleConfirmFilterDialog() { val selectedFilters = state.checkboxOptions.filter { it.isChecked } @@ -206,7 +203,6 @@ internal class AccountsViewModel( * current account type, and refresh signals. */ internal data class AccountsState -@OptIn(ExperimentalTime::class) constructor( val isRefreshing: Boolean = false, diff --git a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/uploadId/UploadIdScreen.kt b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/uploadId/UploadIdScreen.kt index 9eb2a632b4..c1ac2a9c17 100644 --- a/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/uploadId/UploadIdScreen.kt +++ b/feature/auth/src/commonMain/kotlin/org/mifos/mobile/feature/auth/uploadId/UploadIdScreen.kt @@ -46,6 +46,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.lifecycle.compose.collectAsStateWithLifecycle +import kotlinx.datetime.Clock import mifos_mobile.feature.auth.generated.resources.Res import mifos_mobile.feature.auth.generated.resources.feature_common_submit import mifos_mobile.feature.auth.generated.resources.feature_upload_id_cancel @@ -75,8 +76,6 @@ import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.designsystem.theme.MifosTypography import org.mifos.mobile.core.ui.component.MifosPoweredCard import org.mifos.mobile.core.ui.utils.EventsEffect -import kotlin.time.Clock -import kotlin.time.ExperimentalTime @Composable internal fun UploadIdScreen( @@ -181,7 +180,7 @@ internal fun UploadIdScreenContent( } } -@OptIn(ExperimentalMaterial3Api::class, ExperimentalTime::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable internal fun InputForm( state: UploadIdUiState, diff --git a/feature/loan-account/src/commonMain/kotlin/org/mifos/mobile/feature/loanaccount/component/RepaymentPeriodCard.kt b/feature/loan-account/src/commonMain/kotlin/org/mifos/mobile/feature/loanaccount/component/RepaymentPeriodCard.kt index d32248e88e..15c125c603 100644 --- a/feature/loan-account/src/commonMain/kotlin/org/mifos/mobile/feature/loanaccount/component/RepaymentPeriodCard.kt +++ b/feature/loan-account/src/commonMain/kotlin/org/mifos/mobile/feature/loanaccount/component/RepaymentPeriodCard.kt @@ -31,6 +31,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp +import kotlinx.datetime.Clock import mifos_mobile.feature.loan_account.generated.resources.Res import mifos_mobile.feature.loan_account.generated.resources.feature_loan_due import mifos_mobile.feature.loan_account.generated.resources.feature_loan_installment_number @@ -46,10 +47,7 @@ import org.mifos.mobile.core.designsystem.theme.AppColors import org.mifos.mobile.core.designsystem.theme.DesignToken import org.mifos.mobile.core.designsystem.theme.MifosTypography import org.mifos.mobile.core.model.entity.accounts.loan.Periods -import kotlin.time.Clock -import kotlin.time.ExperimentalTime -@OptIn(ExperimentalTime::class) @Composable fun RepaymentScheduleItem( period: Periods, diff --git a/feature/loan-application/src/commonMain/kotlin/org/mifos/mobile/feature/loan/application/confirmDetails/ConfirmDetailsScreen.kt b/feature/loan-application/src/commonMain/kotlin/org/mifos/mobile/feature/loan/application/confirmDetails/ConfirmDetailsScreen.kt index 110ac33667..60d6619cba 100644 --- a/feature/loan-application/src/commonMain/kotlin/org/mifos/mobile/feature/loan/application/confirmDetails/ConfirmDetailsScreen.kt +++ b/feature/loan-application/src/commonMain/kotlin/org/mifos/mobile/feature/loan/application/confirmDetails/ConfirmDetailsScreen.kt @@ -41,7 +41,6 @@ import org.mifos.mobile.core.ui.component.MifosPoweredCard import org.mifos.mobile.core.ui.component.MifosProgressIndicator import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay import org.mifos.mobile.core.ui.utils.EventsEffect -import kotlin.time.ExperimentalTime @Composable internal fun ConfirmDetailsScreen( @@ -84,7 +83,7 @@ internal fun ConfirmDetailsScreen( ) } -@OptIn(ExperimentalMaterial3Api::class, ExperimentalTime::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable internal fun ConfirmDetailsDialog( dialogState: ConfirmDetailsDialogState?, diff --git a/feature/loan-application/src/commonMain/kotlin/org/mifos/mobile/feature/loan/application/loanApplication/LoanApplyScreen.kt b/feature/loan-application/src/commonMain/kotlin/org/mifos/mobile/feature/loan/application/loanApplication/LoanApplyScreen.kt index fa831baf94..596ad69d2f 100644 --- a/feature/loan-application/src/commonMain/kotlin/org/mifos/mobile/feature/loan/application/loanApplication/LoanApplyScreen.kt +++ b/feature/loan-application/src/commonMain/kotlin/org/mifos/mobile/feature/loan/application/loanApplication/LoanApplyScreen.kt @@ -36,6 +36,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.lifecycle.compose.collectAsStateWithLifecycle +import kotlinx.datetime.Clock import mifos_mobile.feature.loan_application.generated.resources.Res import mifos_mobile.feature.loan_application.generated.resources.feature_apply_loan_button_cancel import mifos_mobile.feature.loan_application.generated.resources.feature_apply_loan_button_continue @@ -63,8 +64,6 @@ import org.mifos.mobile.core.ui.component.MifosOutlineDropdown import org.mifos.mobile.core.ui.component.MifosPoweredCard import org.mifos.mobile.core.ui.component.MifosProgressIndicator import org.mifos.mobile.core.ui.utils.EventsEffect -import kotlin.time.Clock -import kotlin.time.ExperimentalTime @Composable internal fun LoanApplyScreen( @@ -107,7 +106,7 @@ internal fun LoanApplyScreen( ) } -@OptIn(ExperimentalMaterial3Api::class, ExperimentalTime::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable internal fun LoanAccountDialog( state: LoanApplicationState, diff --git a/feature/loan-application/src/commonMain/kotlin/org/mifos/mobile/feature/loan/application/loanApplication/LoanApplyViewModel.kt b/feature/loan-application/src/commonMain/kotlin/org/mifos/mobile/feature/loan/application/loanApplication/LoanApplyViewModel.kt index 8084b99c1c..b6a92f51f4 100644 --- a/feature/loan-application/src/commonMain/kotlin/org/mifos/mobile/feature/loan/application/loanApplication/LoanApplyViewModel.kt +++ b/feature/loan-application/src/commonMain/kotlin/org/mifos/mobile/feature/loan/application/loanApplication/LoanApplyViewModel.kt @@ -19,6 +19,7 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import kotlinx.datetime.Clock import mifos_mobile.feature.loan_application.generated.resources.Res import mifos_mobile.feature.loan_application.generated.resources.feature_apply_loan_error_amount_too_large import mifos_mobile.feature.loan_application.generated.resources.feature_apply_loan_error_amount_too_small @@ -43,8 +44,6 @@ import org.mifos.mobile.core.model.entity.templates.loans.LoanTemplate import org.mifos.mobile.core.ui.utils.AmountValidationResult import org.mifos.mobile.core.ui.utils.BaseViewModel import org.mifos.mobile.core.ui.utils.ValidationHelper -import kotlin.time.Clock -import kotlin.time.ExperimentalTime import org.mifos.mobile.core.model.entity.Currency as ModelCurrency /** @@ -697,7 +696,6 @@ internal data class LoanApplicationState( /** * The current time in milliseconds, used for date pickers. */ - @OptIn(ExperimentalTime::class) val currentDate: Long get() = Clock.System.now().toEpochMilliseconds() @@ -737,7 +735,6 @@ internal data class LoanApplicationState( * The effective submission date, which is the later of today's date or the * client's activation date. */ - @OptIn(ExperimentalTime::class) val submittedOnDate: String get() { val todayMillis = Clock.System.now().toEpochMilliseconds() diff --git a/feature/savings-application/src/commonMain/kotlin/org/mifos/mobile/feature/savings/application/fillApplication/FillApplicationViewModel.kt b/feature/savings-application/src/commonMain/kotlin/org/mifos/mobile/feature/savings/application/fillApplication/FillApplicationViewModel.kt index a9fa30d0bd..0b97469c55 100644 --- a/feature/savings-application/src/commonMain/kotlin/org/mifos/mobile/feature/savings/application/fillApplication/FillApplicationViewModel.kt +++ b/feature/savings-application/src/commonMain/kotlin/org/mifos/mobile/feature/savings/application/fillApplication/FillApplicationViewModel.kt @@ -18,6 +18,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import kotlinx.datetime.Clock import mifos_mobile.feature.savings_application.generated.resources.Res import mifos_mobile.feature.savings_application.generated.resources.feature_apply_savings_error_amount_too_large import mifos_mobile.feature.savings_application.generated.resources.feature_apply_savings_error_amount_too_small @@ -53,8 +54,6 @@ import org.mifos.mobile.core.ui.utils.ResultNavigator import org.mifos.mobile.core.ui.utils.ValidationHelper import org.mifos.mobile.core.ui.utils.observe import kotlin.String -import kotlin.time.Clock -import kotlin.time.ExperimentalTime import org.mifos.mobile.core.model.entity.Currency as ModelCurrency private const val DEFAULT_DECIMAL_PLACES = 2 @@ -693,7 +692,6 @@ internal data class SavingsApplicationState( /** * The current date as a formatted string. */ - @OptIn(ExperimentalTime::class) val currentDate: String get() = DateHelper.getDateMonthYearString(Clock.System.now().toEpochMilliseconds()) diff --git a/feature/savings-application/src/commonMain/kotlin/org/mifos/mobile/feature/savings/application/savingsApplication/SavingsApplyViewModel.kt b/feature/savings-application/src/commonMain/kotlin/org/mifos/mobile/feature/savings/application/savingsApplication/SavingsApplyViewModel.kt index f9981c5efc..6e4a9c060a 100644 --- a/feature/savings-application/src/commonMain/kotlin/org/mifos/mobile/feature/savings/application/savingsApplication/SavingsApplyViewModel.kt +++ b/feature/savings-application/src/commonMain/kotlin/org/mifos/mobile/feature/savings/application/savingsApplication/SavingsApplyViewModel.kt @@ -17,6 +17,7 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import kotlinx.datetime.Clock import mifos_mobile.feature.savings_application.generated.resources.Res import mifos_mobile.feature.savings_application.generated.resources.feature_apply_savings_error_product_empty import mifos_mobile.feature.savings_application.generated.resources.feature_apply_savings_error_server @@ -33,8 +34,6 @@ import org.mifos.mobile.core.datastore.UserPreferencesRepository import org.mifos.mobile.core.model.entity.templates.savings.SavingsAccountTemplate import org.mifos.mobile.core.model.entity.templates.savings.SavingsProduct import org.mifos.mobile.core.ui.utils.BaseViewModel -import kotlin.time.Clock -import kotlin.time.ExperimentalTime /** * A `ViewModel` for the savings application screen, responsible for handling user input, @@ -529,10 +528,10 @@ internal data class SavingsApplicationState( id to name } - @OptIn(ExperimentalTime::class) val submittedOnDate: String get() { val todayMillis = Clock.System.now().toEpochMilliseconds() + println(todayMillis) return DateHelper.getDateMonthYearString(todayMillis) } } diff --git a/feature/share-application/src/commonMain/kotlin/org/mifos/mobile/feature/share/application/fillApplication/FillApplicationViewModel.kt b/feature/share-application/src/commonMain/kotlin/org/mifos/mobile/feature/share/application/fillApplication/FillApplicationViewModel.kt index 94f8ef0c91..4443ac54f1 100644 --- a/feature/share-application/src/commonMain/kotlin/org/mifos/mobile/feature/share/application/fillApplication/FillApplicationViewModel.kt +++ b/feature/share-application/src/commonMain/kotlin/org/mifos/mobile/feature/share/application/fillApplication/FillApplicationViewModel.kt @@ -19,6 +19,7 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import kotlinx.datetime.Clock import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.InternalSerializationApi import mifos_mobile.feature.share_application.generated.resources.Res @@ -61,8 +62,6 @@ import org.mifos.mobile.core.ui.utils.ResultNavigator import org.mifos.mobile.core.ui.utils.ValidationHelper import org.mifos.mobile.core.ui.utils.observe import kotlin.String -import kotlin.time.Clock -import kotlin.time.ExperimentalTime import org.mifos.mobile.core.model.entity.Currency as ModelCurrency private const val DEFAULT_DECIMAL_PLACES = 2 @@ -817,7 +816,6 @@ internal data class ShareApplicationState( /** * The current date formatted as a string. */ - @OptIn(ExperimentalTime::class) val currentDate: String get() = DateHelper.getDateMonthYearString(Clock.System.now().toEpochMilliseconds()) diff --git a/feature/share-application/src/commonMain/kotlin/org/mifos/mobile/feature/share/application/shareApplication/ShareApplyViewModel.kt b/feature/share-application/src/commonMain/kotlin/org/mifos/mobile/feature/share/application/shareApplication/ShareApplyViewModel.kt index c51c87cc0a..84b7c0ad89 100644 --- a/feature/share-application/src/commonMain/kotlin/org/mifos/mobile/feature/share/application/shareApplication/ShareApplyViewModel.kt +++ b/feature/share-application/src/commonMain/kotlin/org/mifos/mobile/feature/share/application/shareApplication/ShareApplyViewModel.kt @@ -15,6 +15,7 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import kotlinx.datetime.Clock import mifos_mobile.feature.share_application.generated.resources.Res import mifos_mobile.feature.share_application.generated.resources.feature_apply_share_error_server import mifos_mobile.feature.share_application.generated.resources.feature_apply_share_error_submit_failed @@ -30,8 +31,6 @@ import org.mifos.mobile.core.model.entity.Page import org.mifos.mobile.core.model.entity.templates.savings.SavingsAccountTemplate import org.mifos.mobile.core.model.entity.templates.shares.ShareProduct import org.mifos.mobile.core.ui.utils.BaseViewModel -import kotlin.time.Clock -import kotlin.time.ExperimentalTime /** * `ViewModel` for the savings application screen, responsible for handling user input, @@ -394,7 +393,6 @@ internal data class ShareApplicationState( } .toMap() - @OptIn(ExperimentalTime::class) val submittedOnDate: String get() { val todayMillis = Clock.System.now().toEpochMilliseconds() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e8a1b8cbfe..d94b65518b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -59,7 +59,7 @@ compottie = "2.0.0-rc05" kotlin = "2.1.0" kotlinInject = "0.7.2" kotlinxCoroutines = "1.10.1" -kotlinxDatetime = "0.6.1" +kotlinxDatetime = "0.7.1-0.6.x-compat" kotlinxImmutable = "0.3.8" kotlinxSerializationJson = "1.7.3" ksp = "2.1.0-1.0.29" From 8ab8d422bb45313b5f0a4c2df52f71ed0e629267 Mon Sep 17 00:00:00 2001 From: Nagarjuna Date: Fri, 29 Aug 2025 08:51:34 +0530 Subject: [PATCH 3/3] fix: checks --- .../dependencies/demoDebugRuntimeClasspath.txt | 18 ++++++++++++++---- .../demoReleaseRuntimeClasspath.txt | 18 ++++++++++++++---- .../dependencies/prodDebugRuntimeClasspath.txt | 18 ++++++++++++++---- .../prodReleaseRuntimeClasspath.txt | 18 ++++++++++++++---- 4 files changed, 56 insertions(+), 16 deletions(-) diff --git a/cmp-android/dependencies/demoDebugRuntimeClasspath.txt b/cmp-android/dependencies/demoDebugRuntimeClasspath.txt index a7482731a6..26685212c2 100644 --- a/cmp-android/dependencies/demoDebugRuntimeClasspath.txt +++ b/cmp-android/dependencies/demoDebugRuntimeClasspath.txt @@ -186,8 +186,8 @@ com.russhwolf:multiplatform-settings-serialization:1.2.0 com.russhwolf:multiplatform-settings:1.2.0 com.squareup.okhttp3:okhttp-sse:4.12.0 com.squareup.okhttp3:okhttp:4.12.0 -com.squareup.okio:okio-jvm:3.11.0 -com.squareup.okio:okio:3.11.0 +com.squareup.okio:okio-jvm:3.15.0 +com.squareup.okio:okio:3.15.0 de.jensklingenberg.ktorfit:ktorfit-annotations-android-debug:2.2.0 de.jensklingenberg.ktorfit:ktorfit-annotations:2.2.0 de.jensklingenberg.ktorfit:ktorfit-lib-android-debug:2.2.0 @@ -211,6 +211,14 @@ io.coil-kt.coil3:coil-network-ktor3:3.0.4 io.coil-kt.coil3:coil-svg-android:3.0.4 io.coil-kt.coil3:coil-svg:3.0.4 io.coil-kt.coil3:coil:3.2.0 +io.github.alexzhirkevich:compottie-core-android:2.0.0-rc05 +io.github.alexzhirkevich:compottie-core:2.0.0-rc05 +io.github.alexzhirkevich:compottie-lite-android:2.0.0-rc05 +io.github.alexzhirkevich:compottie-lite:2.0.0-rc05 +io.github.alexzhirkevich:compottie-resources-android:2.0.0-rc05 +io.github.alexzhirkevich:compottie-resources:2.0.0-rc05 +io.github.alexzhirkevich:keight-core-android:0.0.02 +io.github.alexzhirkevich:keight-core:0.0.02 io.github.alexzhirkevich:qrose-android:1.0.1 io.github.alexzhirkevich:qrose-core-android:1.0.1 io.github.alexzhirkevich:qrose-core:1.0.1 @@ -333,6 +341,8 @@ org.jetbrains.kotlin:kotlin-reflect:2.1.0 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.23 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.20 org.jetbrains.kotlin:kotlin-stdlib:2.1.21 +org.jetbrains.kotlinx:atomicfu-jvm:0.23.2 +org.jetbrains.kotlinx:atomicfu:0.23.2 org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.3.8 org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.8 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2 @@ -340,8 +350,8 @@ org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.2 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.2 org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2 org.jetbrains.kotlinx:kotlinx-coroutines-slf4j:1.10.2 -org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.2 -org.jetbrains.kotlinx:kotlinx-datetime:0.6.2 +org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.7.1-0.6.x-compat +org.jetbrains.kotlinx:kotlinx-datetime:0.7.1-0.6.x-compat org.jetbrains.kotlinx:kotlinx-io-bytestring-jvm:0.7.0 org.jetbrains.kotlinx:kotlinx-io-bytestring:0.7.0 org.jetbrains.kotlinx:kotlinx-io-core-jvm:0.7.0 diff --git a/cmp-android/dependencies/demoReleaseRuntimeClasspath.txt b/cmp-android/dependencies/demoReleaseRuntimeClasspath.txt index 38c3bfea57..c286488a0b 100644 --- a/cmp-android/dependencies/demoReleaseRuntimeClasspath.txt +++ b/cmp-android/dependencies/demoReleaseRuntimeClasspath.txt @@ -186,8 +186,8 @@ com.russhwolf:multiplatform-settings-serialization:1.2.0 com.russhwolf:multiplatform-settings:1.2.0 com.squareup.okhttp3:okhttp-sse:4.12.0 com.squareup.okhttp3:okhttp:4.12.0 -com.squareup.okio:okio-jvm:3.11.0 -com.squareup.okio:okio:3.11.0 +com.squareup.okio:okio-jvm:3.15.0 +com.squareup.okio:okio:3.15.0 de.jensklingenberg.ktorfit:ktorfit-annotations-android:2.2.0 de.jensklingenberg.ktorfit:ktorfit-annotations:2.2.0 de.jensklingenberg.ktorfit:ktorfit-lib-android:2.2.0 @@ -211,6 +211,14 @@ io.coil-kt.coil3:coil-network-ktor3:3.0.4 io.coil-kt.coil3:coil-svg-android:3.0.4 io.coil-kt.coil3:coil-svg:3.0.4 io.coil-kt.coil3:coil:3.2.0 +io.github.alexzhirkevich:compottie-core-android:2.0.0-rc05 +io.github.alexzhirkevich:compottie-core:2.0.0-rc05 +io.github.alexzhirkevich:compottie-lite-android:2.0.0-rc05 +io.github.alexzhirkevich:compottie-lite:2.0.0-rc05 +io.github.alexzhirkevich:compottie-resources-android:2.0.0-rc05 +io.github.alexzhirkevich:compottie-resources:2.0.0-rc05 +io.github.alexzhirkevich:keight-core-android:0.0.02 +io.github.alexzhirkevich:keight-core:0.0.02 io.github.alexzhirkevich:qrose-android:1.0.1 io.github.alexzhirkevich:qrose-core-android:1.0.1 io.github.alexzhirkevich:qrose-core:1.0.1 @@ -330,6 +338,8 @@ org.jetbrains.kotlin:kotlin-reflect:2.1.0 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.23 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.20 org.jetbrains.kotlin:kotlin-stdlib:2.1.21 +org.jetbrains.kotlinx:atomicfu-jvm:0.23.2 +org.jetbrains.kotlinx:atomicfu:0.23.2 org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.3.8 org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.8 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2 @@ -337,8 +347,8 @@ org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.2 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.2 org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2 org.jetbrains.kotlinx:kotlinx-coroutines-slf4j:1.10.2 -org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.2 -org.jetbrains.kotlinx:kotlinx-datetime:0.6.2 +org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.7.1-0.6.x-compat +org.jetbrains.kotlinx:kotlinx-datetime:0.7.1-0.6.x-compat org.jetbrains.kotlinx:kotlinx-io-bytestring-jvm:0.7.0 org.jetbrains.kotlinx:kotlinx-io-bytestring:0.7.0 org.jetbrains.kotlinx:kotlinx-io-core-jvm:0.7.0 diff --git a/cmp-android/dependencies/prodDebugRuntimeClasspath.txt b/cmp-android/dependencies/prodDebugRuntimeClasspath.txt index a7482731a6..26685212c2 100644 --- a/cmp-android/dependencies/prodDebugRuntimeClasspath.txt +++ b/cmp-android/dependencies/prodDebugRuntimeClasspath.txt @@ -186,8 +186,8 @@ com.russhwolf:multiplatform-settings-serialization:1.2.0 com.russhwolf:multiplatform-settings:1.2.0 com.squareup.okhttp3:okhttp-sse:4.12.0 com.squareup.okhttp3:okhttp:4.12.0 -com.squareup.okio:okio-jvm:3.11.0 -com.squareup.okio:okio:3.11.0 +com.squareup.okio:okio-jvm:3.15.0 +com.squareup.okio:okio:3.15.0 de.jensklingenberg.ktorfit:ktorfit-annotations-android-debug:2.2.0 de.jensklingenberg.ktorfit:ktorfit-annotations:2.2.0 de.jensklingenberg.ktorfit:ktorfit-lib-android-debug:2.2.0 @@ -211,6 +211,14 @@ io.coil-kt.coil3:coil-network-ktor3:3.0.4 io.coil-kt.coil3:coil-svg-android:3.0.4 io.coil-kt.coil3:coil-svg:3.0.4 io.coil-kt.coil3:coil:3.2.0 +io.github.alexzhirkevich:compottie-core-android:2.0.0-rc05 +io.github.alexzhirkevich:compottie-core:2.0.0-rc05 +io.github.alexzhirkevich:compottie-lite-android:2.0.0-rc05 +io.github.alexzhirkevich:compottie-lite:2.0.0-rc05 +io.github.alexzhirkevich:compottie-resources-android:2.0.0-rc05 +io.github.alexzhirkevich:compottie-resources:2.0.0-rc05 +io.github.alexzhirkevich:keight-core-android:0.0.02 +io.github.alexzhirkevich:keight-core:0.0.02 io.github.alexzhirkevich:qrose-android:1.0.1 io.github.alexzhirkevich:qrose-core-android:1.0.1 io.github.alexzhirkevich:qrose-core:1.0.1 @@ -333,6 +341,8 @@ org.jetbrains.kotlin:kotlin-reflect:2.1.0 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.23 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.20 org.jetbrains.kotlin:kotlin-stdlib:2.1.21 +org.jetbrains.kotlinx:atomicfu-jvm:0.23.2 +org.jetbrains.kotlinx:atomicfu:0.23.2 org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.3.8 org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.8 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2 @@ -340,8 +350,8 @@ org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.2 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.2 org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2 org.jetbrains.kotlinx:kotlinx-coroutines-slf4j:1.10.2 -org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.2 -org.jetbrains.kotlinx:kotlinx-datetime:0.6.2 +org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.7.1-0.6.x-compat +org.jetbrains.kotlinx:kotlinx-datetime:0.7.1-0.6.x-compat org.jetbrains.kotlinx:kotlinx-io-bytestring-jvm:0.7.0 org.jetbrains.kotlinx:kotlinx-io-bytestring:0.7.0 org.jetbrains.kotlinx:kotlinx-io-core-jvm:0.7.0 diff --git a/cmp-android/dependencies/prodReleaseRuntimeClasspath.txt b/cmp-android/dependencies/prodReleaseRuntimeClasspath.txt index 38c3bfea57..c286488a0b 100644 --- a/cmp-android/dependencies/prodReleaseRuntimeClasspath.txt +++ b/cmp-android/dependencies/prodReleaseRuntimeClasspath.txt @@ -186,8 +186,8 @@ com.russhwolf:multiplatform-settings-serialization:1.2.0 com.russhwolf:multiplatform-settings:1.2.0 com.squareup.okhttp3:okhttp-sse:4.12.0 com.squareup.okhttp3:okhttp:4.12.0 -com.squareup.okio:okio-jvm:3.11.0 -com.squareup.okio:okio:3.11.0 +com.squareup.okio:okio-jvm:3.15.0 +com.squareup.okio:okio:3.15.0 de.jensklingenberg.ktorfit:ktorfit-annotations-android:2.2.0 de.jensklingenberg.ktorfit:ktorfit-annotations:2.2.0 de.jensklingenberg.ktorfit:ktorfit-lib-android:2.2.0 @@ -211,6 +211,14 @@ io.coil-kt.coil3:coil-network-ktor3:3.0.4 io.coil-kt.coil3:coil-svg-android:3.0.4 io.coil-kt.coil3:coil-svg:3.0.4 io.coil-kt.coil3:coil:3.2.0 +io.github.alexzhirkevich:compottie-core-android:2.0.0-rc05 +io.github.alexzhirkevich:compottie-core:2.0.0-rc05 +io.github.alexzhirkevich:compottie-lite-android:2.0.0-rc05 +io.github.alexzhirkevich:compottie-lite:2.0.0-rc05 +io.github.alexzhirkevich:compottie-resources-android:2.0.0-rc05 +io.github.alexzhirkevich:compottie-resources:2.0.0-rc05 +io.github.alexzhirkevich:keight-core-android:0.0.02 +io.github.alexzhirkevich:keight-core:0.0.02 io.github.alexzhirkevich:qrose-android:1.0.1 io.github.alexzhirkevich:qrose-core-android:1.0.1 io.github.alexzhirkevich:qrose-core:1.0.1 @@ -330,6 +338,8 @@ org.jetbrains.kotlin:kotlin-reflect:2.1.0 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.23 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.20 org.jetbrains.kotlin:kotlin-stdlib:2.1.21 +org.jetbrains.kotlinx:atomicfu-jvm:0.23.2 +org.jetbrains.kotlinx:atomicfu:0.23.2 org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.3.8 org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.8 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2 @@ -337,8 +347,8 @@ org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.2 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.2 org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2 org.jetbrains.kotlinx:kotlinx-coroutines-slf4j:1.10.2 -org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.2 -org.jetbrains.kotlinx:kotlinx-datetime:0.6.2 +org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.7.1-0.6.x-compat +org.jetbrains.kotlinx:kotlinx-datetime:0.7.1-0.6.x-compat org.jetbrains.kotlinx:kotlinx-io-bytestring-jvm:0.7.0 org.jetbrains.kotlinx:kotlinx-io-bytestring:0.7.0 org.jetbrains.kotlinx:kotlinx-io-core-jvm:0.7.0