From e45c9b6f595a0fa50f2cd7dc1cabbe9af8ea315b Mon Sep 17 00:00:00 2001 From: Sk Niyaj Ali Date: Sun, 11 Aug 2024 23:06:53 +0530 Subject: [PATCH] Refactor - [:feature:make-transfer] Applied Spotless & Detekt Compose Rules (#1724) * Refactor: Applied Spotless & Detekt Compose Rules - :feature:accounts - :feature:auth - :feature:editpassword - :feature:finance * Refactor - [:feature:history] Applied Spotless & Detekt Compose Rules * Refactor - [:feature:home] Applied Spotless & Detekt Compose Rules * Refactor - [:feature:invoices] Applied Spotless & Detekt Compose Rules * Refactor - [:feature:kyc] Applied Spotless & Detekt Compose Rules * Refactor - [:feature:make-transfer] Applied Spotless & Detekt Compose Rules --- .editorconfig | 7 + .github/workflows/master_dev_ci.yml | 29 +-- build.gradle.kts | 41 +++- config/detekt/detekt.yml | 50 +++- .../main/kotlin/org/mifospay/common/Utils.kt | 8 +- .../domain/usecase/history/HistoryContract.kt | 18 ++ .../usecase/history}/TransactionsHistory.kt | 24 +- .../domain/usecase/invoice/FetchInvoice.kt | 1 - .../domain/usecase/invoice/FetchInvoices.kt | 2 + .../designsystem/component/MifosScaffold.kt | 13 +- core/ui/build.gradle.kts | 3 - .../org/mifospay/core/ui/ScrollableTabRow.kt | 15 +- feature/accounts/build.gradle.kts | 9 + .../bank/accounts/ExampleInstrumentedTest.kt | 24 -- feature/accounts/src/main/AndroidManifest.xml | 9 + .../feature/bank/accounts/AccountViewModel.kt | 60 +++-- .../feature/bank/accounts/AccountsItem.kt | 31 ++- .../feature/bank/accounts/AccountsScreen.kt | 79 ++++--- .../choose/sim/ChooseSimDialogSheet.kt | 53 +++-- .../details/BankAccountDetailScreen.kt | 122 ++++++---- .../accounts/link/LinkBankAccountScreen.kt | 116 ++++++---- .../accounts/link/LinkBankAccountViewModel.kt | 36 ++- ...LinkBankUiStatePreviewParameterProvider.kt | 13 +- .../navigation/BankAccountDetailNavigation.kt | 19 +- .../navigation/LinkBankAccountNavigation.kt | 15 +- .../res/drawable/feature_accounts_ic_bank.xml | 9 + .../feature_accounts_sim_card_selected.xml | 9 + .../feature_accounts_sim_card_unselected.xml | 9 + .../accounts/src/main/res/values/colors.xml | 9 + .../accounts/src/main/res/values/strings.xml | 9 + .../feature/bank/accounts/ExampleUnitTest.kt | 16 -- feature/auth/build.gradle.kts | 9 + .../feature/auth/ExampleInstrumentedTest.kt | 24 -- feature/auth/src/main/AndroidManifest.xml | 9 + .../feature/auth/login/LoginActivity.kt | 9 + .../feature/auth/login/LoginScreen.kt | 63 +++-- .../feature/auth/login/LoginViewModel.kt | 33 ++- .../mobileVerify/MobileVerificationScreen.kt | 102 ++++---- .../MobileVerificationViewModel.kt | 19 +- .../auth/navigation/LoginScreenNavigation.kt | 13 +- .../MobileVerificationScreenNavigation.kt | 45 +++- .../auth/navigation/SignupScreenNavigation.kt | 58 +++-- .../feature/auth/signup/SignupScreen.kt | 168 ++++++++------ .../feature/auth/signup/SignupViewModel.kt | 87 ++++--- .../socialSignup/SocialSignupMethodScreen.kt | 76 +++--- .../feature/auth/utils/ValidateUtil.kt | 13 +- feature/auth/src/main/res/values/strings.xml | 9 + .../mifospay/auth/ExampleUnitTest.kt | 16 -- feature/editpassword/build.gradle.kts | 9 + .../editpassword/src/main/AndroidManifest.xml | 9 + .../editpassword/EditPasswordScreen.kt | 70 +++--- .../editpassword/EditPasswordViewModel.kt | 39 ++-- .../EditPasswordScreenNavigation.kt | 15 +- .../src/main/res/values/strings.xml | 9 + .../feature/editpassword/ExampleUnitTest.kt | 16 -- feature/faq/build.gradle.kts | 9 + feature/faq/src/main/AndroidManifest.xml | 9 + .../kotlin/org/mifospay/feature/faq/FAQ.kt | 11 +- .../org/mifospay/feature/faq/FAQViewModel.kt | 15 +- .../org/mifospay/feature/faq/FaqScreen.kt | 46 ++-- .../feature/faq/navigation/FAQNavigation.kt | 15 +- feature/faq/src/main/res/values/strings.xml | 9 + .../java/com/example/faq/ExampleUnitTest.kt | 16 -- feature/finance/build.gradle.kts | 14 +- .../finance/ExampleInstrumentedTest.kt | 24 -- feature/finance/src/main/AndroidManifest.xml | 9 + .../mifospay/feature/finance/FinanceScreen.kt | 65 ++---- .../finance/navigation/FinanceNavigation.kt | 27 +-- .../feature/finance/ExampleUnitTest.kt | 16 -- feature/history/build.gradle.kts | 10 +- .../history/ExampleInstrumentedTest.kt | 24 -- feature/history/src/main/AndroidManifest.xml | 9 + .../org/mifospay/feature/HistoryContract.kt | 12 - .../mifospay/feature/history/HistoryScreen.kt | 62 +++-- .../feature/history/HistoryViewModel.kt | 31 ++- .../SpecificTransactionsScreen.kt | 92 +++++--- .../SpecificTransactionsViewModel.kt | 23 +- .../SpecificTransactionsNavigation.kt | 28 ++- .../detail/TransactionDetailScreen.kt | 68 +++--- .../detail/TransactionDetailViewModel.kt | 18 +- .../res/drawable/feature_history_ic_send.xml | 10 + .../history/src/main/res/values/strings.xml | 14 ++ .../org/mifospay/history/ExampleUnitTest.kt | 16 -- feature/home/build.gradle.kts | 10 +- .../feature/home/ExampleInstrumentedTest.kt | 24 -- feature/home/src/main/AndroidManifest.xml | 9 + .../org/mifospay/feature/home/HomeScreen.kt | 146 +++++++----- .../mifospay/feature/home/HomeViewModel.kt | 29 ++- .../feature/home/navigation/HomeNavigation.kt | 13 +- .../feature_home_ic_arrow_back_black_24dp.xml | 10 + feature/home/src/main/res/values/colors.xml | 9 + feature/home/src/main/res/values/strings.xml | 9 + .../mifospay/feature/home/ExampleUnitTest.kt | 16 -- feature/invoices/build.gradle.kts | 10 +- .../invoices/ExampleInstrumentedTest.kt | 24 -- feature/invoices/src/main/AndroidManifest.xml | 9 + .../feature/invoices/InvoiceDetailScreen.kt | 135 ++++++----- .../invoices/InvoiceDetailViewModel.kt | 40 +++- .../mifospay/feature/invoices/InvoiceItem.kt | 58 +++-- .../feature/invoices/InvoiceScreen.kt | 39 ++-- .../feature/invoices/InvoicesViewModel.kt | 30 ++- .../invoices/navigation/InvoiceNavigation.kt | 23 +- ...ure_invoices_ic_check_round_black_24dp.xml | 10 + ...es_ic_remove_circle_outline_black_24dp.xml | 10 + .../invoices/src/main/res/values/colors.xml | 9 + .../invoices/src/main/res/values/strings.xml | 9 + .../org/mifospay/invoices/ExampleUnitTest.kt | 16 -- feature/kyc/build.gradle.kts | 9 + .../mifospay/kyc/ExampleInstrumentedTest.kt | 24 -- feature/kyc/src/main/AndroidManifest.xml | 9 + .../feature/kyc/KYCDescriptionScreen.kt | 124 ++++++---- .../feature/kyc/KYCDescriptionViewModel.kt | 45 ++-- .../mifospay/feature/kyc/KYCLevel1Screen.kt | 139 +++++------ .../feature/kyc/KYCLevel1ViewModel.kt | 79 ++++--- .../mifospay/feature/kyc/KYCLevel2Screen.kt | 171 +++++++------- .../feature/kyc/KYCLevel2ViewModel.kt | 34 ++- .../mifospay/feature/kyc/KYCLevel3Screen.kt | 52 +++-- .../feature/kyc/KYCLevel3ViewModel.kt | 22 +- .../kyc/navigation/KYCLevel1Navigation.kt | 15 +- .../kyc/navigation/KYCLevel2Navigation.kt | 15 +- .../kyc/navigation/KYCLevel3Navigation.kt | 11 +- .../feature/kyc/navigation/KYCNavigation.kt | 16 +- .../drawable/feature_kyc_ic_error_state.xml | 10 + .../res/layout/feature_kyc_fragment_kyc.xml | 9 + .../layout/feature_kyc_placeholder_state.xml | 9 + feature/kyc/src/main/res/values/colors.xml | 9 + feature/kyc/src/main/res/values/dimens.xml | 9 + feature/kyc/src/main/res/values/strings.xml | 9 + .../java/org/mifospay/kyc/ExampleUnitTest.kt | 16 -- feature/make-transfer/build.gradle.kts | 9 + .../src/main/AndroidManifest.xml | 9 + .../make/transfer/MakeTransferScreen.kt | 217 ++++++++---------- .../make/transfer/MakeTransferViewModel.kt | 56 +++-- .../navigation/MakeTransferNavigation.kt | 19 +- .../feature_make_transfer_ic_close.xml | 10 + .../feature_make_transfer_ic_done.xml | 10 + ...feature_make_transfer_transfer_failure.xml | 9 + ...feature_make_transfer_transfer_success.xml | 9 + .../src/main/res/values/colors.xml | 9 + .../src/main/res/values/strings.xml | 10 + .../feature/make/transfer/ExampleUnitTest.kt | 16 -- .../merchants/MerchantTransferViewModel.kt | 4 +- .../feature/payments/PaymentsScreen.kt | 24 +- .../feature/profile/edit/EditProfileScreen.kt | 99 ++++---- gradle/libs.versions.toml | 84 +++---- gradle/wrapper/gradle-wrapper.properties | 2 +- .../prodReleaseRuntimeClasspath.txt | 170 +++++++------- .../org/mifospay/navigation/MifosNavHost.kt | 96 ++++---- spotless/copyright.kt | 9 + spotless/copyright.kts | 9 + spotless/copyright.xml | 10 + 151 files changed, 2981 insertions(+), 1922 deletions(-) create mode 100644 .editorconfig create mode 100644 core/data/src/main/java/org/mifospay/core/data/domain/usecase/history/HistoryContract.kt rename {feature/history/src/main/kotlin/org/mifospay/feature => core/data/src/main/java/org/mifospay/core/data/domain/usecase/history}/TransactionsHistory.kt (71%) delete mode 100644 feature/accounts/src/androidTest/java/org/mifospay/feature/bank/accounts/ExampleInstrumentedTest.kt delete mode 100644 feature/accounts/src/test/java/org/mifospay/feature/bank/accounts/ExampleUnitTest.kt delete mode 100644 feature/auth/src/androidTest/java/org/mifospay/mobilewallet/mifospay/feature/auth/ExampleInstrumentedTest.kt delete mode 100644 feature/auth/src/test/java/org/mifospay/mobilewallet/mifospay/auth/ExampleUnitTest.kt delete mode 100644 feature/editpassword/src/test/java/org/mifospay/feature/editpassword/ExampleUnitTest.kt delete mode 100644 feature/faq/src/test/java/com/example/faq/ExampleUnitTest.kt delete mode 100644 feature/finance/src/androidTest/java/org/mifospay/feature/finance/ExampleInstrumentedTest.kt delete mode 100644 feature/finance/src/test/java/org/mifospay/feature/finance/ExampleUnitTest.kt delete mode 100644 feature/history/src/androidTest/java/org/mifospay/history/ExampleInstrumentedTest.kt delete mode 100644 feature/history/src/main/kotlin/org/mifospay/feature/HistoryContract.kt delete mode 100644 feature/history/src/test/java/org/mifospay/history/ExampleUnitTest.kt delete mode 100644 feature/home/src/androidTest/java/org/mifospay/feature/home/ExampleInstrumentedTest.kt delete mode 100644 feature/home/src/test/java/org/mifospay/feature/home/ExampleUnitTest.kt delete mode 100644 feature/invoices/src/androidTest/java/org/mifospay/invoices/ExampleInstrumentedTest.kt delete mode 100644 feature/invoices/src/test/java/org/mifospay/invoices/ExampleUnitTest.kt delete mode 100644 feature/kyc/src/androidTest/java/org/mifospay/kyc/ExampleInstrumentedTest.kt delete mode 100644 feature/kyc/src/test/java/org/mifospay/kyc/ExampleUnitTest.kt delete mode 100644 feature/make-transfer/src/test/java/org/mifospay/feature/make/transfer/ExampleUnitTest.kt create mode 100644 spotless/copyright.kt create mode 100644 spotless/copyright.kts create mode 100644 spotless/copyright.xml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..06c4d26a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +# https://editorconfig.org/ +# This configuration is used by ktlint when spotless invokes it + +[*.{kt,kts}] +ij_kotlin_allow_trailing_comma=true +ij_kotlin_allow_trailing_comma_on_call_site=true +ktlint_function_naming_ignore_when_annotated_with=Composable, Test, Preview \ No newline at end of file diff --git a/.github/workflows/master_dev_ci.yml b/.github/workflows/master_dev_ci.yml index 8a0b67b7..e227cb43 100644 --- a/.github/workflows/master_dev_ci.yml +++ b/.github/workflows/master_dev_ci.yml @@ -52,20 +52,21 @@ jobs: name: mobile-wallet path: mifospay/build/outputs/apk/debug/ - lintCheck: - name: Static Analysis - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - # Setup JDK 17 - - name: Setup JDK 17 - uses: actions/setup-java@v1 - with: - java-version: 17 - - - name: Detekt For All Modules - run: ./gradlew detekt +# Turing off detekt check until migration finished +# lintCheck: +# name: Static Analysis +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v2 +# +# # Setup JDK 17 +# - name: Setup JDK 17 +# uses: actions/setup-java@v1 +# with: +# java-version: 17 +# +# - name: Detekt For All Modules +# run: ./gradlew detekt pmd: name: PMD diff --git a/build.gradle.kts b/build.gradle.kts index 9d0508d9..c480a08d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,11 +1,10 @@ -import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask - // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { dependencies { classpath(libs.google.oss.licenses.plugin) { exclude(group = "com.google.protobuf") } + classpath(libs.spotless.gradle) } } @@ -28,11 +27,14 @@ plugins { alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.detekt) alias(libs.plugins.detekt.compiler) - alias(libs.plugins.module.graph) apply true // Plugin applied to allow module graph generation + // Plugin applied to allow module graph generation + alias(libs.plugins.module.graph) apply true + alias(libs.plugins.spotless) } val detektFormatting = libs.detekt.formatting val twitterComposeRules = libs.twitter.detekt.compose +val ktlintVersion = "1.0.1" val reportMerge by tasks.registering(io.gitlab.arturbosch.detekt.report.ReportMergeTask::class) { output.set(rootProject.layout.buildDirectory.file("reports/detekt/merge.html")) // or "reports/detekt/merge.sarif" @@ -41,17 +43,40 @@ val reportMerge by tasks.registering(io.gitlab.arturbosch.detekt.report.ReportMe subprojects { apply { plugin("io.gitlab.arturbosch.detekt") - } - - detekt { - config.from(rootProject.files("config/detekt/detekt.yml")) - reports.xml.required.set(true) + plugin("com.diffplug.spotless") } tasks.withType().configureEach { + config.from(rootProject.files("config/detekt/detekt.yml")) + reports.xml.required.set(true) finalizedBy(reportMerge) } + extensions.configure { + kotlin { + target("**/*.kt") + targetExclude("**/build/**/*.kt") + ktlint(ktlintVersion).editorConfigOverride( + mapOf( + "android" to "true", + ), + ) + licenseHeaderFile(rootProject.file("spotless/copyright.kt")) + } + format("kts") { + target("**/*.kts") + targetExclude("**/build/**/*.kts") + // Look for the first line that doesn't have a block comment (assumed to be the license) + licenseHeaderFile(rootProject.file("spotless/copyright.kts"), "(^(?![\\/ ]\\*).*$)") + } + format("xml") { + target("**/*.xml") + targetExclude("**/build/**/*.xml") + // Look for the first XML tag that isn't a comment ( \ No newline at end of file diff --git a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/AccountViewModel.kt b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/AccountViewModel.kt index ba981a2e..2010c9ed 100644 --- a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/AccountViewModel.kt +++ b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/AccountViewModel.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.bank.accounts import androidx.lifecycle.ViewModel @@ -55,33 +64,54 @@ class AccountViewModel @Inject constructor() : ViewModel() { private fun fetchSampleLinkedAccounts(): List { return listOf( BankAccountDetails( - "SBI", "Ankur Sharma", "New Delhi", - mRandom.nextInt().toString() + " ", "Savings" + "SBI", + "Ankur Sharma", + "New Delhi", + mRandom.nextInt().toString() + " ", + "Savings", ), BankAccountDetails( - "HDFC", "Mandeep Singh", "Uttar Pradesh", - mRandom.nextInt().toString() + " ", "Savings" + "HDFC", + "Mandeep Singh", + "Uttar Pradesh", + mRandom.nextInt().toString() + " ", + "Savings", ), BankAccountDetails( - "ANDHRA", "Rakesh anna", "Telegana", - mRandom.nextInt().toString() + " ", "Savings" + "ANDHRA", + "Rakesh anna", + "Telegana", + mRandom.nextInt().toString() + " ", + "Savings", ), BankAccountDetails( - "PNB", "luv Pro", "Gujrat", - mRandom.nextInt().toString() + " ", "Savings" + "PNB", + "luv Pro", + "Gujrat", + mRandom.nextInt().toString() + " ", + "Savings", ), BankAccountDetails( - "HDF", "Harry potter", "Hogwarts", - mRandom.nextInt().toString() + " ", "Savings" + "HDF", + "Harry potter", + "Hogwarts", + mRandom.nextInt().toString() + " ", + "Savings", ), BankAccountDetails( - "GCI", "JIGME", "JAMMU", - mRandom.nextInt().toString() + " ", "Savings" + "GCI", + "JIGME", + "JAMMU", + mRandom.nextInt().toString() + " ", + "Savings", ), BankAccountDetails( - "FCI", "NISHU BOII", "ASSAM", - mRandom.nextInt().toString() + " ", "Savings" - ) + "FCI", + "NISHU BOII", + "ASSAM", + mRandom.nextInt().toString() + " ", + "Savings", + ), ) } diff --git a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/AccountsItem.kt b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/AccountsItem.kt index 9f6efe83..fdc951c2 100644 --- a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/AccountsItem.kt +++ b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/AccountsItem.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.bank.accounts import androidx.compose.foundation.layout.Column @@ -20,19 +29,21 @@ import com.mifospay.core.model.domain.BankAccountDetails import org.mifospay.core.designsystem.component.MifosCard @Composable -fun AccountsItem( +internal fun AccountsItem( bankAccountDetails: BankAccountDetails, - onAccountClicked: () -> Unit + onAccountClicked: () -> Unit, + modifier: Modifier = Modifier, ) { MifosCard( + modifier = modifier, onClick = { onAccountClicked.invoke() }, - colors = CardDefaults.cardColors(MaterialTheme.colorScheme.surface) + colors = CardDefaults.cardColors(MaterialTheme.colorScheme.surface), ) { Column { Row( modifier = Modifier .fillMaxWidth() - .padding(top = 16.dp) + .padding(top = 16.dp), ) { Icon( painter = painterResource(id = R.drawable.feature_accounts_ic_bank), @@ -40,7 +51,7 @@ fun AccountsItem( modifier = Modifier .align(Alignment.CenterVertically) .padding(start = 16.dp, end = 16.dp) - .size(39.dp) + .size(39.dp), ) Column { @@ -52,18 +63,18 @@ fun AccountsItem( text = bankAccountDetails.bankName.toString(), modifier = Modifier.padding(top = 4.dp), style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurface + color = MaterialTheme.colorScheme.onSurface, ) } Column( horizontalAlignment = Alignment.End, - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) { Text( text = bankAccountDetails.branch.toString(), modifier = Modifier.padding(16.dp), fontSize = 12.sp, - color = MaterialTheme.colorScheme.onSurface + color = MaterialTheme.colorScheme.onSurface, ) } } @@ -76,6 +87,6 @@ fun AccountsItem( private fun AccountsItemPreview() { AccountsItem( bankAccountDetails = BankAccountDetails("A", "B", "C"), - onAccountClicked = {} + onAccountClicked = {}, ) -} \ No newline at end of file +} diff --git a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/AccountsScreen.kt b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/AccountsScreen.kt index 355cdd86..659500a8 100644 --- a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/AccountsScreen.kt +++ b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/AccountsScreen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.bank.accounts import androidx.compose.foundation.background @@ -34,15 +43,17 @@ import org.mifospay.core.ui.utility.AddCardChip @Composable fun AccountsScreen( + navigateToBankAccountDetailScreen: (BankAccountDetails, Int) -> Unit, + navigateToLinkBankAccountScreen: () -> Unit, + modifier: Modifier = Modifier, viewModel: AccountViewModel = hiltViewModel(), - navigateToBankAccountDetailScreen: (BankAccountDetails,Int) -> Unit, - navigateToLinkBankAccountScreen: () -> Unit ) { val accountsUiState by viewModel.accountsUiState.collectAsStateWithLifecycle() val isRefreshing by viewModel.isRefreshing.collectAsStateWithLifecycle() val bankAccountDetailsList by viewModel.bankAccountDetailsList.collectAsStateWithLifecycle() AccountScreen( + modifier = modifier, accountsUiState = accountsUiState, onAddAccount = { navigateToLinkBankAccountScreen.invoke() @@ -54,27 +65,30 @@ fun AccountsScreen( }, onUpdateAccount = { bankAccountDetails, index -> viewModel.updateBankAccount(index, bankAccountDetails) - navigateToBankAccountDetailScreen.invoke(bankAccountDetails,index) - } + navigateToBankAccountDetailScreen.invoke(bankAccountDetails, index) + }, ) } @OptIn(ExperimentalMaterialApi::class) @Composable -fun AccountScreen( +private fun AccountScreen( accountsUiState: AccountsUiState, onAddAccount: () -> Unit, bankAccountDetailsList: List, isRefreshing: Boolean, onRefresh: () -> Unit, - onUpdateAccount: (BankAccountDetails, Int) -> Unit + onUpdateAccount: (BankAccountDetails, Int) -> Unit, + modifier: Modifier = Modifier, ) { val pullRefreshState = rememberPullRefreshState(isRefreshing, onRefresh) - Box(Modifier.pullRefresh(pullRefreshState)) { + Box(modifier.pullRefresh(pullRefreshState)) { Column(modifier = Modifier.fillMaxSize()) { when (accountsUiState) { AccountsUiState.Empty -> { - NoLinkedAccountsScreen { onAddAccount.invoke() } + NoLinkedAccountsScreen( + onAddBtn = onAddAccount, + ) } AccountsUiState.Error -> { @@ -83,7 +97,7 @@ fun AccountScreen( title = stringResource(id = R.string.feature_accounts_error_oops), subTitle = stringResource(id = R.string.feature_accounts_unexpected_error_subtitle), iconTint = MaterialTheme.colorScheme.onSurface, - iconImageVector = Icons.Rounded.Info + iconImageVector = Icons.Rounded.Info, ) } @@ -91,14 +105,14 @@ fun AccountScreen( LazyColumn( modifier = Modifier .weight(1f) - .fillMaxSize() + .fillMaxSize(), ) { item { Text( text = stringResource(id = R.string.feature_accounts_linked_bank_account), fontSize = 16.sp, color = MaterialTheme.colorScheme.primary, - modifier = Modifier.padding(top = 48.dp, start = 24.dp) + modifier = Modifier.padding(top = 48.dp, start = 24.dp), ) } items(bankAccountDetailsList) { bankAccountDetails -> @@ -107,10 +121,10 @@ fun AccountScreen( bankAccountDetails = bankAccountDetails, onAccountClicked = { onUpdateAccount(bankAccountDetails, index) - } + }, ) HorizontalDivider( - modifier = Modifier.padding(8.dp) + modifier = Modifier.padding(8.dp), ) } item { @@ -118,13 +132,13 @@ fun AccountScreen( modifier = Modifier .fillMaxWidth() .padding(16.dp) - .background(MaterialTheme.colorScheme.surface) + .background(MaterialTheme.colorScheme.surface), ) { AddCardChip( modifier = Modifier.align(Alignment.Center), onAddBtn = onAddAccount, text = R.string.feature_accounts_add_account, - btnText = R.string.feature_accounts_add_cards + btnText = R.string.feature_accounts_add_cards, ) } } @@ -134,7 +148,7 @@ fun AccountScreen( AccountsUiState.Loading -> { MfLoadingWheel( contentDesc = stringResource(R.string.feature_accounts_loading), - backgroundColor = MaterialTheme.colorScheme.surface + backgroundColor = MaterialTheme.colorScheme.surface, ) } } @@ -142,26 +156,29 @@ fun AccountScreen( PullRefreshIndicator( refreshing = isRefreshing, state = pullRefreshState, - modifier = Modifier.align(Alignment.TopCenter) + modifier = Modifier.align(Alignment.TopCenter), ) } } @Composable -fun NoLinkedAccountsScreen(onAddBtn: () -> Unit) { +private fun NoLinkedAccountsScreen( + onAddBtn: () -> Unit, + modifier: Modifier = Modifier, +) { Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center + modifier = modifier.fillMaxSize(), + contentAlignment = Alignment.Center, ) { Column( - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, ) { Text(text = stringResource(R.string.feature_accounts_no_linked_bank_accounts)) AddCardChip( modifier = Modifier, onAddBtn = onAddBtn, text = R.string.feature_accounts_add_account, - btnText = R.string.feature_accounts_add_cards + btnText = R.string.feature_accounts_add_cards, ) } } @@ -170,7 +187,14 @@ fun NoLinkedAccountsScreen(onAddBtn: () -> Unit) { @Preview(showBackground = true) @Composable private fun AccountScreenLoadingPreview() { - AccountScreen(accountsUiState = AccountsUiState.Loading, {}, emptyList(), false, {}, { _, _ -> }) + AccountScreen( + accountsUiState = AccountsUiState.Loading, + {}, + emptyList(), + false, + {}, + { _, _ -> }, + ) } @Preview(showBackground = true) @@ -188,7 +212,7 @@ private fun AccountListScreenPreview() { sampleLinkedAccount, false, {}, - { _, _ -> } + { _, _ -> }, ) } @@ -200,7 +224,10 @@ private fun AccountErrorScreenPreview() { val sampleLinkedAccount = List(10) { BankAccountDetails( - "SBI", "Ankur Sharma", "New Delhi", - "XXXXXXXX9990XXX " + " ", "Savings" + "SBI", + "Ankur Sharma", + "New Delhi", + "XXXXXXXX9990XXX " + " ", + "Savings", ) } diff --git a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/choose/sim/ChooseSimDialogSheet.kt b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/choose/sim/ChooseSimDialogSheet.kt index 37d43de0..97bb1679 100644 --- a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/choose/sim/ChooseSimDialogSheet.kt +++ b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/choose/sim/ChooseSimDialogSheet.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.bank.accounts.choose.sim import androidx.compose.animation.AnimatedVisibility @@ -40,14 +49,14 @@ import org.mifospay.core.designsystem.theme.MifosTheme import org.mifospay.feature.bank.accounts.R @Composable -fun ChooseSimDialogSheet( +internal fun ChooseSimDialogSheet( onSimSelected: (Int) -> Unit, modifier: Modifier = Modifier, ) { MifosBottomSheet( content = { ChooseSimDialogSheetContent( - onSimSelected = onSimSelected + onSimSelected = onSimSelected, ) }, onDismiss = { @@ -63,7 +72,7 @@ fun ChooseSimDialogSheet( */ @Composable @Suppress("LongMethod") -fun ChooseSimDialogSheetContent( +private fun ChooseSimDialogSheetContent( onSimSelected: (Int) -> Unit, modifier: Modifier = Modifier, ) { @@ -82,67 +91,67 @@ fun ChooseSimDialogSheetContent( horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier .fillMaxSize() - .padding(8.dp) + .padding(8.dp), ) { Text( text = stringResource(id = R.string.feature_accounts_verify_mobile_number), style = MaterialTheme.typography.titleLarge, - color = MaterialTheme.colorScheme.onSurface + color = MaterialTheme.colorScheme.onSurface, ) Spacer(modifier = Modifier.height(16.dp)) Text( text = stringResource(id = R.string.feature_accounts_confirm_mobile_number_message), style = MaterialTheme.typography.bodySmall.copy( - textAlign = TextAlign.Center + textAlign = TextAlign.Center, ), - color = MaterialTheme.colorScheme.onSurface + color = MaterialTheme.colorScheme.onSurface, ) Spacer(modifier = Modifier.height(16.dp)) Text( modifier = Modifier.padding(horizontal = 24.dp), text = stringResource(id = R.string.feature_accounts_bank_account_mobile_verification_conditions), style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurface + color = MaterialTheme.colorScheme.onSurface, ) Spacer(modifier = Modifier.height(20.dp)) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { SimCard( simNumber = 1, isSelected = selectedSim == 1, - onSimSelected = { selectedSim = 1 } + onSimSelected = { selectedSim = 1 }, ) Spacer(modifier = Modifier.width(24.dp)) Text( text = stringResource(id = R.string.feature_accounts_or), - color = MaterialTheme.colorScheme.onSurface + color = MaterialTheme.colorScheme.onSurface, ) Spacer(modifier = Modifier.width(24.dp)) SimCard( simNumber = 2, isSelected = selectedSim == 2, - onSimSelected = { selectedSim = 2 } + onSimSelected = { selectedSim = 2 }, ) } Spacer(modifier = Modifier.height(16.dp)) Text( text = stringResource(id = R.string.feature_accounts_regular_charges_will_apply), color = MaterialTheme.colorScheme.onSurface, - style = MaterialTheme.typography.bodySmall + style = MaterialTheme.typography.bodySmall, ) AnimatedVisibility( - visible = showMessage + visible = showMessage, ) { Text( text = message, color = MaterialTheme.colorScheme.error, style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(vertical = 4.dp) + modifier = Modifier.padding(vertical = 4.dp), ) } @@ -156,7 +165,7 @@ fun ChooseSimDialogSheetContent( } else { onSimSelected(selectedSim) } - } + }, ) { Text(text = stringResource(id = R.string.feature_accounts_confirm)) } @@ -165,7 +174,7 @@ fun ChooseSimDialogSheetContent( } @Composable -fun SimCard( +private fun SimCard( simNumber: Int, isSelected: Boolean, onSimSelected: () -> Unit, @@ -174,7 +183,9 @@ fun SimCard( val drawable: Painter = painterResource( id = if (isSelected) { R.drawable.feature_accounts_sim_card_selected - } else R.drawable.feature_accounts_sim_card_unselected + } else { + R.drawable.feature_accounts_sim_card_unselected + }, ) Image( painter = drawable, @@ -182,17 +193,17 @@ fun SimCard( contentScale = ContentScale.Fit, modifier = modifier .size(50.dp) - .clickable { onSimSelected() } + .clickable { onSimSelected() }, ) } @Preview @Composable -fun SimSelectionPreview() { +private fun SimSelectionPreview() { MifosTheme { Surface { ChooseSimDialogSheetContent( - onSimSelected = {} + onSimSelected = {}, ) } } diff --git a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/details/BankAccountDetailScreen.kt b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/details/BankAccountDetailScreen.kt index 669c35b0..56589e89 100644 --- a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/details/BankAccountDetailScreen.kt +++ b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/details/BankAccountDetailScreen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.bank.accounts.details import androidx.compose.foundation.border @@ -26,12 +35,12 @@ import org.mifospay.core.designsystem.component.MifosTopBar import org.mifospay.feature.bank.accounts.R @Composable -fun BankAccountDetailScreen( +internal fun BankAccountDetailScreen( bankAccountDetails: BankAccountDetails, onSetupUpiPin: () -> Unit, onChangeUpiPin: () -> Unit, onForgotUpiPin: () -> Unit, - navigateBack: () -> Unit + navigateBack: () -> Unit, ) { BankAccountDetailScreen( bankName = bankAccountDetails.bankName.toString(), @@ -43,12 +52,12 @@ fun BankAccountDetailScreen( onSetupUpiPin = onSetupUpiPin, onChangeUpiPin = onChangeUpiPin, onForgotUpiPin = onForgotUpiPin, - navigateBack = navigateBack + navigateBack = navigateBack, ) } @Composable -fun BankAccountDetailScreen( +private fun BankAccountDetailScreen( bankName: String, accountHolderName: String, branchName: String, @@ -58,109 +67,131 @@ fun BankAccountDetailScreen( onSetupUpiPin: () -> Unit, onChangeUpiPin: () -> Unit, onForgotUpiPin: () -> Unit, - navigateBack: () -> Unit + navigateBack: () -> Unit, + modifier: Modifier = Modifier, ) { - Column(modifier = Modifier.fillMaxSize()) { - MifosTopBar(topBarTitle = R.string.feature_accounts_bank_account_details) { navigateBack.invoke() } + Column( + modifier = modifier + .fillMaxSize(), + ) { + MifosTopBar( + topBarTitle = R.string.feature_accounts_bank_account_details, + backPress = navigateBack, + ) + Column( modifier = Modifier .padding(20.dp) .border(2.dp, MaterialTheme.colorScheme.onSurface) - .padding(20.dp) + .padding(20.dp), ) { BankAccountDetailRows( modifier = Modifier.fillMaxWidth(), detail = R.string.feature_accounts_bank_name, - detailValue = bankName + detailValue = bankName, ) BankAccountDetailRows( - modifier = Modifier.fillMaxWidth().padding(top = 10.dp), + modifier = Modifier + .fillMaxWidth() + .padding(top = 10.dp), detail = R.string.feature_accounts_ac_holder_name, - detailValue = accountHolderName + detailValue = accountHolderName, ) BankAccountDetailRows( - modifier = Modifier.fillMaxWidth().padding(top = 10.dp), + modifier = Modifier + .fillMaxWidth() + .padding(top = 10.dp), detail = R.string.feature_accounts_branch_name, - detailValue = branchName + detailValue = branchName, ) BankAccountDetailRows( - modifier = Modifier.fillMaxWidth().padding(top = 10.dp), + modifier = Modifier + .fillMaxWidth() + .padding(top = 10.dp), detail = R.string.feature_accounts_ifsc, - detailValue = ifsc + detailValue = ifsc, ) BankAccountDetailRows( - modifier = Modifier.fillMaxWidth().padding(top = 10.dp), + modifier = Modifier + .fillMaxWidth() + .padding(top = 10.dp), detail = R.string.feature_accounts_type, - detailValue = type + detailValue = type, ) } Row( - modifier = Modifier.fillMaxWidth().padding(20.dp), - horizontalArrangement = Arrangement.SpaceBetween + modifier = Modifier + .fillMaxWidth() + .padding(20.dp), + horizontalArrangement = Arrangement.SpaceBetween, ) { - BankAccountDetailButton( btnText = R.string.feature_accounts_setup_upi, onClick = { onSetupUpiPin.invoke() }, isUpiEnabled = !isUpiEnabled, - hasTrailingIcon = false + hasTrailingIcon = false, ) BankAccountDetailButton( btnText = R.string.feature_accounts_delete_bank, onClick = {}, - isUpiEnabled = !isUpiEnabled + isUpiEnabled = !isUpiEnabled, ) } Column( - modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(20.dp) + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(20.dp), ) { BankAccountDetailButton( btnText = R.string.feature_accounts_change_upi_pin, onClick = { onChangeUpiPin.invoke() }, isUpiEnabled = isUpiEnabled, - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) BankAccountDetailButton( btnText = R.string.feature_accounts_forgot_upi_pin, onClick = { onForgotUpiPin.invoke() }, isUpiEnabled = isUpiEnabled, - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) } } } @Composable -fun BankAccountDetailRows( - modifier: Modifier, detail: Int, detailValue: String +private fun BankAccountDetailRows( + detail: Int, + detailValue: String, + modifier: Modifier = Modifier, ) { Row( modifier = modifier, horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text( text = stringResource(id = detail), modifier = Modifier.padding(end = 10.dp), style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurface + color = MaterialTheme.colorScheme.onSurface, ) - Text(text = detailValue, + Text( + text = detailValue, style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurface) + color = MaterialTheme.colorScheme.onSurface, + ) } } @Composable -fun BankAccountDetailButton( - modifier: Modifier = Modifier, +private fun BankAccountDetailButton( btnText: Int, onClick: () -> Unit, isUpiEnabled: Boolean, - hasTrailingIcon: Boolean = false + modifier: Modifier = Modifier, + hasTrailingIcon: Boolean = false, ) { if (isUpiEnabled) { Button( @@ -168,23 +199,23 @@ fun BankAccountDetailButton( colors = ButtonDefaults.buttonColors(MaterialTheme.colorScheme.primary), modifier = modifier .padding(start = 20.dp, end = 20.dp), - contentPadding = PaddingValues(20.dp) + contentPadding = PaddingValues(20.dp), ) { Row( - modifier = modifier, + modifier = Modifier, horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text( text = stringResource(id = btnText), style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.onPrimary + color = MaterialTheme.colorScheme.onPrimary, ) if (hasTrailingIcon) { Icon( imageVector = Icons.Filled.ChevronRight, contentDescription = null, - tint = MaterialTheme.colorScheme.onPrimary + tint = MaterialTheme.colorScheme.onPrimary, ) } } @@ -192,29 +223,30 @@ fun BankAccountDetailButton( } } - @Preview(showBackground = true) @Composable private fun BankAccountDetailUpiDisabledPreview() { - BankAccountDetailScreen("Mifos Bank", + BankAccountDetailScreen( + "Mifos Bank", "Mifos Account Holder", "Mifos Branch", "IFSC", "type", false, - {}, {}, {}, {} + {}, {}, {}, {}, ) } @Preview(showBackground = true) @Composable private fun BankAccountDetailUpiEnabledPreview() { - BankAccountDetailScreen("Mifos Bank", + BankAccountDetailScreen( + "Mifos Bank", "Mifos Account Holder", "Mifos Branch", "IFSC", "type", true, - {}, {}, {}, {} + {}, {}, {}, {}, ) -} \ No newline at end of file +} diff --git a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/link/LinkBankAccountScreen.kt b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/link/LinkBankAccountScreen.kt index 1b8be231..dd246900 100644 --- a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/link/LinkBankAccountScreen.kt +++ b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/link/LinkBankAccountScreen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.bank.accounts.link import androidx.compose.foundation.Image @@ -58,11 +67,10 @@ import org.mifospay.core.ui.DevicePreviews import org.mifospay.feature.bank.accounts.R import org.mifospay.feature.bank.accounts.choose.sim.ChooseSimDialogSheet - @Composable -fun LinkBankAccountRoute( +internal fun LinkBankAccountRoute( viewModel: LinkBankAccountViewModel = hiltViewModel(), - onBackClick: () -> Unit + onBackClick: () -> Unit, ) { val bankUiState by viewModel.bankListUiState.collectAsStateWithLifecycle() var showSimBottomSheet by rememberSaveable { mutableStateOf(false) } @@ -79,7 +87,7 @@ fun LinkBankAccountRoute( onBackClick() } } - } + }, ) } @@ -93,22 +101,23 @@ fun LinkBankAccountRoute( viewModel.updateSelectedBank(it) showSimBottomSheet = true }, - onBackClick = onBackClick + onBackClick = onBackClick, ) } @OptIn(ExperimentalMaterial3Api::class) @Composable -fun LinkBankAccountScreen( +private fun LinkBankAccountScreen( bankUiState: BankUiState, showOverlyProgressBar: Boolean, onBankSearch: (String) -> Unit, onBankSelected: (Bank) -> Unit, - onBackClick: () -> Unit + onBackClick: () -> Unit, + modifier: Modifier = Modifier, ) { - Scaffold( - modifier = Modifier.background(color = MaterialTheme.colorScheme.surface), + modifier = modifier + .background(color = MaterialTheme.colorScheme.surface), topBar = { MifosTopAppBar( titleRes = R.string.feature_accounts_link_bank_account, @@ -119,15 +128,17 @@ fun LinkBankAccountScreen( containerColor = MaterialTheme.colorScheme.surface, ), ) - }) { paddingValues -> + }, + ) { paddingValues -> Box( - modifier = Modifier.padding(paddingValues) + modifier = Modifier + .padding(paddingValues), ) { when (bankUiState) { is BankUiState.Loading -> { MfLoadingWheel( contentDesc = stringResource(R.string.feature_accounts_loading), - backgroundColor = MaterialTheme.colorScheme.surface + backgroundColor = MaterialTheme.colorScheme.surface, ) } @@ -135,7 +146,7 @@ fun LinkBankAccountScreen( BankListScreenContent( banks = bankUiState.banks, onBankSearch = onBankSearch, - onBankSelected = onBankSelected + onBankSelected = onBankSelected, ) } } @@ -148,21 +159,23 @@ fun LinkBankAccountScreen( } @Composable -fun BankListScreenContent( +private fun BankListScreenContent( banks: List, onBankSearch: (String) -> Unit, - onBankSelected: (Bank) -> Unit + onBankSelected: (Bank) -> Unit, + modifier: Modifier = Modifier, ) { var searchQuery by rememberSaveable { mutableStateOf("") } Column( - modifier = Modifier + modifier = modifier .fillMaxSize() .background(color = MaterialTheme.colorScheme.surface) - .verticalScroll(rememberScrollState()) + .verticalScroll(rememberScrollState()), ) { - MifosOutlinedTextField(modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), + MifosOutlinedTextField( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), value = searchQuery, onValueChange = { searchQuery = it @@ -171,7 +184,8 @@ fun BankListScreenContent( label = R.string.feature_accounts_search, trailingIcon = { Icon(imageVector = Icons.Filled.Search, contentDescription = null) - }) + }, + ) if (searchQuery.isBlank()) { Spacer(modifier = Modifier.height(24.dp)) @@ -179,23 +193,23 @@ fun BankListScreenContent( text = stringResource(id = R.string.feature_accounts_popular_banks), style = TextStyle( MaterialTheme.colorScheme.onSurface, - fontWeight = FontWeight.Medium + fontWeight = FontWeight.Medium, ), - modifier = Modifier.padding(start = 16.dp) + modifier = Modifier.padding(start = 16.dp), ) Spacer(modifier = Modifier.height(12.dp)) PopularBankGridBody( banks = banks.filter { it.bankType == BankType.POPULAR }, - onBankSelected = onBankSelected + onBankSelected = onBankSelected, ) Spacer(modifier = Modifier.height(24.dp)) Text( text = stringResource(id = R.string.feature_accounts_other_banks), style = TextStyle( MaterialTheme.colorScheme.onSurface, - fontWeight = FontWeight.Medium + fontWeight = FontWeight.Medium, ), - modifier = Modifier.padding(start = 16.dp) + modifier = Modifier.padding(start = 16.dp), ) Spacer(modifier = Modifier.height(12.dp)) } @@ -203,34 +217,37 @@ fun BankListScreenContent( BankListBody( banks = if (searchQuery.isBlank()) { banks.filter { it.bankType == BankType.OTHER } - } else banks, - onBankSelected = onBankSelected + } else { + banks + }, + onBankSelected = onBankSelected, ) } } @OptIn(ExperimentalLayoutApi::class) @Composable -fun PopularBankGridBody( +private fun PopularBankGridBody( banks: List, - onBankSelected: (Bank) -> Unit + onBankSelected: (Bank) -> Unit, + modifier: Modifier = Modifier, ) { MifosCard( - modifier = Modifier, + modifier = modifier, shape = RoundedCornerShape(0.dp), elevation = 2.dp, - colors = CardDefaults.cardColors(MaterialTheme.colorScheme.surface) + colors = CardDefaults.cardColors(MaterialTheme.colorScheme.surface), ) { FlowRow( modifier = Modifier, horizontalArrangement = Arrangement.spacedBy(8.dp), - maxItemsInEachRow = 3 + maxItemsInEachRow = 3, ) { banks.forEach { PopularBankItemBody( modifier = Modifier.weight(1f), bank = it, - onBankSelected = onBankSelected + onBankSelected = onBankSelected, ) } } @@ -238,10 +255,10 @@ fun PopularBankGridBody( } @Composable -fun PopularBankItemBody( - modifier: Modifier, +private fun PopularBankItemBody( bank: Bank, - onBankSelected: (Bank) -> Unit + onBankSelected: (Bank) -> Unit, + modifier: Modifier = Modifier, ) { Column( horizontalAlignment = Alignment.CenterHorizontally, @@ -262,18 +279,19 @@ fun PopularBankItemBody( Text( text = bank.name, style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.padding(top = 4.dp, bottom = 16.dp) + modifier = Modifier.padding(top = 4.dp, bottom = 16.dp), ) } } @OptIn(ExperimentalLayoutApi::class) @Composable -fun BankListBody( +private fun BankListBody( banks: List, - onBankSelected: (Bank) -> Unit + onBankSelected: (Bank) -> Unit, + modifier: Modifier = Modifier, ) { - FlowColumn { + FlowColumn(modifier) { banks.forEach { bank -> BankListItemBody(bank = bank, onBankSelected = onBankSelected) } @@ -281,14 +299,15 @@ fun BankListBody( } @Composable -fun BankListItemBody( +private fun BankListItemBody( bank: Bank, - onBankSelected: (Bank) -> Unit + onBankSelected: (Bank) -> Unit, + modifier: Modifier = Modifier, ) { Column( - modifier = Modifier + modifier = modifier .fillMaxSize() - .clickable { onBankSelected(bank) } + .clickable { onBankSelected(bank) }, ) { HorizontalDivider() Row( @@ -296,7 +315,7 @@ fun BankListItemBody( verticalAlignment = Alignment.CenterVertically, modifier = Modifier .fillMaxWidth() - .padding(start = 24.dp, top = 8.dp, bottom = 8.dp) + .padding(start = 24.dp, top = 8.dp, bottom = 8.dp), ) { Image( modifier = Modifier.size(32.dp), @@ -305,7 +324,8 @@ fun BankListItemBody( ) Text( modifier = Modifier.padding(start = 16.dp, end = 16.dp), - text = bank.name, style = TextStyle(fontSize = 14.sp) + text = bank.name, + style = TextStyle(fontSize = 14.sp), ) } } @@ -323,7 +343,7 @@ private fun LinkBankAccountScreenPreview( showOverlyProgressBar = false, onBankSelected = { }, onBankSearch = { }, - onBackClick = { } + onBackClick = { }, ) } } diff --git a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/link/LinkBankAccountViewModel.kt b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/link/LinkBankAccountViewModel.kt index da0b5d8a..9f6c107a 100644 --- a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/link/LinkBankAccountViewModel.kt +++ b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/link/LinkBankAccountViewModel.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.bank.accounts.link import androidx.compose.runtime.getValue @@ -24,17 +33,17 @@ import javax.inject.Inject @HiltViewModel class LinkBankAccountViewModel @Inject constructor( - localAssetRepository: MifosLocalAssetRepository + localAssetRepository: MifosLocalAssetRepository, ) : ViewModel() { - private val _searchQuery = MutableStateFlow("") + private val searchQuery = MutableStateFlow("") private var selectedBank by mutableStateOf(null) - private val _bankAccountDetails: MutableStateFlow = MutableStateFlow(null) - val bankAccountDetails: StateFlow = _bankAccountDetails.asStateFlow() + private val accountDetails: MutableStateFlow = MutableStateFlow(null) + val bankAccountDetails: StateFlow = accountDetails.asStateFlow() fun updateSearchQuery(query: String) { - _searchQuery.update { query } + searchQuery.update { query } } fun updateSelectedBank(bank: Bank) { @@ -42,9 +51,9 @@ class LinkBankAccountViewModel @Inject constructor( } val bankListUiState: StateFlow = combine( - _searchQuery, + searchQuery, localAssetRepository.getBanks(), - ::Pair + ::Pair, ).map { searchQueryAndBanks -> val searchQuery = searchQueryAndBanks.first val localBanks = searchQueryAndBanks.second.map { @@ -55,7 +64,7 @@ class LinkBankAccountViewModel @Inject constructor( addAll(localBanks) }.distinctBy { it.name } BankUiState.Success( - banks.filter { it.name.contains(searchQuery.lowercase(), ignoreCase = true) } + banks.filter { it.name.contains(searchQuery.lowercase(), ignoreCase = true) }, ) }.stateIn( scope = viewModelScope, @@ -70,17 +79,20 @@ class LinkBankAccountViewModel @Inject constructor( Bank("PNB Bank", R.drawable.feature_accounts_logo_pnb, BankType.POPULAR), Bank("HDFC Bank", R.drawable.feature_accounts_logo_hdfc, BankType.POPULAR), Bank("ICICI Bank", R.drawable.feature_accounts_logo_icici, BankType.POPULAR), - Bank("AXIS Bank", R.drawable.feature_accounts_logo_axis, BankType.POPULAR) + Bank("AXIS Bank", R.drawable.feature_accounts_logo_axis, BankType.POPULAR), ) } fun fetchBankAccountDetails(onBankDetailsSuccess: () -> Unit) { // TODO:: UPI API implement, Implement with real API, // It revert back to Account Screen after successful BankAccount Add - _bankAccountDetails.update { + accountDetails.update { BankAccountDetails( - selectedBank?.name, "Ankur Sharma", "New Delhi", - mRandom.nextInt().toString() + " ", "Savings" + selectedBank?.name, + "Ankur Sharma", + "New Delhi", + mRandom.nextInt().toString() + " ", + "Savings", ) } onBankDetailsSuccess.invoke() diff --git a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/link/LinkBankUiStatePreviewParameterProvider.kt b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/link/LinkBankUiStatePreviewParameterProvider.kt index d8a796ee..dc723e98 100644 --- a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/link/LinkBankUiStatePreviewParameterProvider.kt +++ b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/link/LinkBankUiStatePreviewParameterProvider.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.bank.accounts.link import androidx.compose.ui.tooling.preview.PreviewParameterProvider @@ -20,6 +29,6 @@ class LinkBankUiStatePreviewParameterProvider : PreviewParameterProvider = sequenceOf( - BankUiState.Success(banks = banks) + BankUiState.Success(banks = banks), ) -} \ No newline at end of file +} diff --git a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/navigation/BankAccountDetailNavigation.kt b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/navigation/BankAccountDetailNavigation.kt index d67ccee5..369495ee 100644 --- a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/navigation/BankAccountDetailNavigation.kt +++ b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/navigation/BankAccountDetailNavigation.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.bank.accounts.navigation import androidx.navigation.NavController @@ -16,14 +25,14 @@ fun NavGraphBuilder.bankAccountDetailScreen( onSetupUpiPin: (BankAccountDetails, Int) -> Unit, onChangeUpiPin: (BankAccountDetails, Int) -> Unit, onForgotUpiPin: (BankAccountDetails, Int) -> Unit, - onBackClick: (BankAccountDetails, Int) -> Unit + onBackClick: (BankAccountDetails, Int) -> Unit, ) { composable( route = "$BANK_ACCOUNT_DETAIL_ROUTE/{${Constants.BANK_ACCOUNT_DETAILS}}/{${Constants.INDEX}}", arguments = listOf( navArgument(Constants.BANK_ACCOUNT_DETAILS) { type = NavType.StringType }, - navArgument(Constants.INDEX) { type = NavType.IntType } - ) + navArgument(Constants.INDEX) { type = NavType.IntType }, + ), ) { backStackEntry -> val bankAccountDetails = backStackEntry.arguments?.getParcelable(Constants.BANK_ACCOUNT_DETAILS) @@ -47,7 +56,7 @@ fun NavGraphBuilder.bankAccountDetailScreen( // TODO: Use global snackbar } }, - navigateBack = { onBackClick(bankAccountDetails, index) } + navigateBack = { onBackClick(bankAccountDetails, index) }, ) } } @@ -55,7 +64,7 @@ fun NavGraphBuilder.bankAccountDetailScreen( fun NavController.navigateToBankAccountDetail( bankAccountDetails: BankAccountDetails, index: Int, - navOptions: NavOptions? = null + navOptions: NavOptions? = null, ) { this.navigate("$BANK_ACCOUNT_DETAIL_ROUTE/$bankAccountDetails/$index", navOptions) } diff --git a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/navigation/LinkBankAccountNavigation.kt b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/navigation/LinkBankAccountNavigation.kt index 859defe9..d2062eb8 100644 --- a/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/navigation/LinkBankAccountNavigation.kt +++ b/feature/accounts/src/main/kotlin/org/mifospay/feature/bank/accounts/navigation/LinkBankAccountNavigation.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.bank.accounts.navigation import androidx.navigation.NavController @@ -12,11 +21,11 @@ fun NavController.navigateToLinkBankAccount(navOptions: NavOptions? = null) = navigate(LINK_BANK_ACCOUNT_ROUTE, navOptions) fun NavGraphBuilder.linkBankAccountScreen( - onBackClick: () -> Unit + onBackClick: () -> Unit, ) { composable(route = LINK_BANK_ACCOUNT_ROUTE) { LinkBankAccountRoute( - onBackClick = onBackClick + onBackClick = onBackClick, ) } -} \ No newline at end of file +} diff --git a/feature/accounts/src/main/res/drawable/feature_accounts_ic_bank.xml b/feature/accounts/src/main/res/drawable/feature_accounts_ic_bank.xml index f265a8d5..a7856762 100644 --- a/feature/accounts/src/main/res/drawable/feature_accounts_ic_bank.xml +++ b/feature/accounts/src/main/res/drawable/feature_accounts_ic_bank.xml @@ -1,4 +1,13 @@ + + + + #DE000000 @color/feature_accounts_colorBlack87 diff --git a/feature/accounts/src/main/res/values/strings.xml b/feature/accounts/src/main/res/values/strings.xml index dacae57d..60093e18 100644 --- a/feature/accounts/src/main/res/values/strings.xml +++ b/feature/accounts/src/main/res/values/strings.xml @@ -1,4 +1,13 @@ + Bank Account Details Bank Name diff --git a/feature/accounts/src/test/java/org/mifospay/feature/bank/accounts/ExampleUnitTest.kt b/feature/accounts/src/test/java/org/mifospay/feature/bank/accounts/ExampleUnitTest.kt deleted file mode 100644 index f74122aa..00000000 --- a/feature/accounts/src/test/java/org/mifospay/feature/bank/accounts/ExampleUnitTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.mifospay.feature.bank.accounts - -import org.junit.Assert.assertEquals -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/feature/auth/build.gradle.kts b/feature/auth/build.gradle.kts index 41323602..3914d6d0 100644 --- a/feature/auth/build.gradle.kts +++ b/feature/auth/build.gradle.kts @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ plugins { alias(libs.plugins.mifospay.android.feature) alias(libs.plugins.mifospay.android.library.compose) diff --git a/feature/auth/src/androidTest/java/org/mifospay/mobilewallet/mifospay/feature/auth/ExampleInstrumentedTest.kt b/feature/auth/src/androidTest/java/org/mifospay/mobilewallet/mifospay/feature/auth/ExampleInstrumentedTest.kt deleted file mode 100644 index 261ab014..00000000 --- a/feature/auth/src/androidTest/java/org/mifospay/mobilewallet/mifospay/feature/auth/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.mifospay.mobilewallet.mifospay.feature.auth - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("org.mifos.mobilewallet.mifospay.auth.test", appContext.packageName) - } -} \ No newline at end of file diff --git a/feature/auth/src/main/AndroidManifest.xml b/feature/auth/src/main/AndroidManifest.xml index a5918e68..96138981 100644 --- a/feature/auth/src/main/AndroidManifest.xml +++ b/feature/auth/src/main/AndroidManifest.xml @@ -1,4 +1,13 @@ + \ No newline at end of file diff --git a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/login/LoginActivity.kt b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/login/LoginActivity.kt index 1275e707..57e4d4e5 100644 --- a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/login/LoginActivity.kt +++ b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/login/LoginActivity.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.auth.login import android.os.Bundle diff --git a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/login/LoginScreen.kt b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/login/LoginScreen.kt index 3ecafefa..9dc0b73e 100644 --- a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/login/LoginScreen.kt +++ b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/login/LoginScreen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.auth.login import android.content.Context @@ -53,8 +62,9 @@ import org.mifospay.feature.auth.socialSignup.SocialSignupMethodContentScreen import org.mifospay.feature.passcode.PassCodeActivity @Composable -fun LoginScreen( - viewModel: LoginViewModel = hiltViewModel() +internal fun LoginScreen( + modifier: Modifier = Modifier, + viewModel: LoginViewModel = hiltViewModel(), ) { val context = LocalContext.current val showProgress by viewModel.showProgress.collectAsStateWithLifecycle() @@ -65,6 +75,7 @@ fun LoginScreen( } LoginScreenContent( + modifier = modifier, showProgress = showProgress, login = { username, password -> viewModel.loginUser( @@ -72,9 +83,9 @@ fun LoginScreen( password = password, onLoginFailed = { message -> Toast.makeText(context, message, Toast.LENGTH_SHORT).show() - } + }, ) - } + }, ) if (isLoginSuccess) { @@ -84,20 +95,21 @@ fun LoginScreen( @Composable @Suppress("LongMethod") -fun LoginScreenContent( +private fun LoginScreenContent( showProgress: Boolean, login: (username: String, password: String) -> Unit, + modifier: Modifier = Modifier, ) { var showSignUpScreen by rememberSaveable { mutableStateOf(false) } var userName by rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf( - TextFieldValue("") + TextFieldValue(""), ) } var password by rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf( - TextFieldValue("") + TextFieldValue(""), ) } var passwordVisibility: Boolean by remember { mutableStateOf(false) } @@ -108,25 +120,25 @@ fun LoginScreenContent( } } - Box { + Box(modifier) { Column( modifier = Modifier .fillMaxWidth() .background(MaterialTheme.colorScheme.surface) .verticalScroll(rememberScrollState()) .padding(top = 100.dp, start = 48.dp, end = 48.dp), - horizontalAlignment = Alignment.Start + horizontalAlignment = Alignment.Start, ) { Text( text = stringResource(id = R.string.feature_auth_login), style = MaterialTheme.typography.titleLarge, - color = MaterialTheme.colorScheme.primary + color = MaterialTheme.colorScheme.primary, ) Text( modifier = Modifier .padding(top = 32.dp), text = stringResource(id = R.string.feature_auth_welcome_back), - style = styleNormal18sp.copy(color = grey) + style = styleNormal18sp.copy(color = grey), ) Spacer(modifier = Modifier.padding(top = 32.dp)) MifosOutlinedTextField( @@ -135,7 +147,7 @@ fun LoginScreenContent( onValueChange = { userName = it }, - label = R.string.feature_auth_username + label = R.string.feature_auth_username, ) Spacer(modifier = Modifier.padding(top = 16.dp)) MifosOutlinedTextField( @@ -147,15 +159,19 @@ fun LoginScreenContent( label = R.string.feature_auth_password, visualTransformation = if (passwordVisibility) { VisualTransformation.None - } else PasswordVisualTransformation(), + } else { + PasswordVisualTransformation() + }, trailingIcon = { - val image = if (passwordVisibility) + val image = if (passwordVisibility) { Icons.Filled.Visibility - else Icons.Filled.VisibilityOff + } else { + Icons.Filled.VisibilityOff + } IconButton(onClick = { passwordVisibility = !passwordVisibility }) { Icon(imageVector = image, null) } - } + }, ) Button( modifier = Modifier @@ -166,12 +182,12 @@ fun LoginScreenContent( onClick = { login.invoke(userName.text, password.text) }, - contentPadding = PaddingValues(12.dp) + contentPadding = PaddingValues(12.dp), ) { Text( text = stringResource(id = R.string.feature_auth_login).uppercase(), style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.onPrimary + color = MaterialTheme.colorScheme.onPrimary, ) } // Hide reset password for now @@ -197,12 +213,12 @@ fun LoginScreenContent( modifier = Modifier .fillMaxWidth() .padding(top = 24.dp), - horizontalArrangement = Arrangement.Center + horizontalArrangement = Arrangement.Center, ) { Text( text = "Don’t have an account yet? ", style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.onSurface + color = MaterialTheme.colorScheme.onSurface, ) Text( modifier = Modifier.clickable { @@ -218,11 +234,10 @@ fun LoginScreenContent( if (showProgress) { MfOverlayLoadingWheel( - contentDesc = stringResource(id = R.string.feature_auth_logging_in) + contentDesc = stringResource(id = R.string.feature_auth_logging_in), ) } } - } /** @@ -237,11 +252,11 @@ private fun startPassCodeActivity(context: Context) { @Preview(showSystemUi = true, device = "id:pixel_5") @Composable -fun LoanScreenPreview() { +private fun LoanScreenPreview() { MifosTheme { LoginScreenContent( showProgress = false, - login = { _, _ -> } + login = { _, _ -> }, ) } } diff --git a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/login/LoginViewModel.kt b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/login/LoginViewModel.kt index 81ad7ced..509514d0 100644 --- a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/login/LoginViewModel.kt +++ b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/login/LoginViewModel.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.auth.login import android.util.Log @@ -43,7 +52,6 @@ class LoginViewModel @Inject constructor( _isLoginSuccess.update { isLoginSuccess } } - /** * Authenticate User with username and password * @param username @@ -53,14 +61,16 @@ class LoginViewModel @Inject constructor( fun loginUser( username: String, password: String, - onLoginFailed: (String) -> Unit + onLoginFailed: (String) -> Unit, ) { updateProgressState(true) authenticateUserUseCase.walletRequestValues = AuthenticateUser.RequestValues(username, password) val requestValue = authenticateUserUseCase.walletRequestValues - mUsecaseHandler.execute(authenticateUserUseCase, requestValue, + mUsecaseHandler.execute( + authenticateUserUseCase, + requestValue, object : UseCaseCallback { override fun onSuccess(response: AuthenticateUser.ResponseValue) { saveAuthTokenInPref(response.user) @@ -72,16 +82,17 @@ class LoginViewModel @Inject constructor( updateProgressState(false) onLoginFailed(message) } - }) + }, + ) } - /** * Fetch user details return by authenticated user * @param user */ private fun fetchUserDetails(user: User) { - mUsecaseHandler.execute(fetchUserDetailsUseCase, + mUsecaseHandler.execute( + fetchUserDetailsUseCase, FetchUserDetails.RequestValues(user.userId), object : UseCaseCallback { override fun onSuccess(response: FetchUserDetails.ResponseValue) { @@ -92,7 +103,8 @@ class LoginViewModel @Inject constructor( updateProgressState(false) Log.d("Login User Detailed: ", message) } - }) + }, + ) } /** @@ -116,7 +128,8 @@ class LoginViewModel @Inject constructor( override fun onError(message: String) { updateProgressState(false) } - }) + }, + ) } private fun saveAuthTokenInPref(user: User) { @@ -128,7 +141,7 @@ class LoginViewModel @Inject constructor( */ private fun saveUserDetails( user: User, - userWithRole: UserWithRole + userWithRole: UserWithRole, ) { val userName = user.username val userID = user.userId @@ -147,4 +160,4 @@ class LoginViewModel @Inject constructor( preferencesHelper.saveMobile(client.mobileNo) preferencesHelper.client = client } -} \ No newline at end of file +} diff --git a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/mobileVerify/MobileVerificationScreen.kt b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/mobileVerify/MobileVerificationScreen.kt index 41ec67c6..0aff5356 100644 --- a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/mobileVerify/MobileVerificationScreen.kt +++ b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/mobileVerify/MobileVerificationScreen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.auth.mobileVerify import android.widget.Toast @@ -41,16 +50,17 @@ import org.mifospay.core.designsystem.component.MifosOutlinedTextField import org.mifospay.core.designsystem.theme.MifosTheme import org.mifospay.feature.auth.R - @Composable -fun MobileVerificationScreen( +internal fun MobileVerificationScreen( + onOtpVerificationSuccess: (String) -> Unit, + modifier: Modifier = Modifier, viewModel: MobileVerificationViewModel = hiltViewModel(), - onOtpVerificationSuccess: (String) -> Unit ) { val context = LocalContext.current val uiState by viewModel.uiState.collectAsStateWithLifecycle() - MobileVerificationScreen(uiState = uiState, + MobileVerificationScreen( + uiState = uiState, showProgressState = viewModel.showProgress, verifyMobileAndRequestOtp = { phone, fullPhone -> viewModel.verifyMobileAndRequestOtp(fullPhone, phone) { @@ -63,18 +73,19 @@ fun MobileVerificationScreen( viewModel.verifyOTP(validatedOtp) { onOtpVerificationSuccess(fullNumber) } - } + }, + modifier = modifier, ) } @Composable -fun MobileVerificationScreen( +private fun MobileVerificationScreen( uiState: MobileVerificationUiState, - showProgressState: Boolean = false, verifyMobileAndRequestOtp: (String, String) -> Unit, - verifyOtp: (String, String) -> Unit + verifyOtp: (String, String) -> Unit, + modifier: Modifier = Modifier, + showProgressState: Boolean = false, ) { - var phoneNumber by rememberSaveable { mutableStateOf("") } var fullPhoneNumber by rememberSaveable { mutableStateOf("") } var isNumberValid: Boolean by rememberSaveable { mutableStateOf(false) } @@ -90,19 +101,18 @@ fun MobileVerificationScreen( } } - Box { + Box(modifier) { Column( modifier = Modifier .fillMaxSize() .background(color = Color.White) .focusable(!showProgressState), ) { - Column( modifier = Modifier .fillMaxWidth() .background(color = MaterialTheme.colorScheme.primary), - verticalArrangement = Arrangement.Top + verticalArrangement = Arrangement.Top, ) { Text( modifier = Modifier.padding(top = 48.dp, start = 24.dp, end = 24.dp), @@ -111,11 +121,14 @@ fun MobileVerificationScreen( } else { stringResource(id = R.string.feature_auth_enter_otp) }, - style = MaterialTheme.typography.titleLarge.copy(color = MaterialTheme.colorScheme.onPrimary) + style = MaterialTheme.typography.titleLarge.copy(color = MaterialTheme.colorScheme.onPrimary), ) Text( modifier = Modifier.padding( - top = 4.dp, bottom = 32.dp, start = 24.dp, end = 24.dp + top = 4.dp, + bottom = 32.dp, + start = 24.dp, + end = 24.dp, ), text = if (uiState == MobileVerificationUiState.VerifyPhone) { stringResource(id = R.string.feature_auth_enter_mobile_number_description) @@ -123,7 +136,7 @@ fun MobileVerificationScreen( stringResource(id = R.string.feature_auth_enter_otp_received_on_your_registered_device) }, style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onPrimary + color = MaterialTheme.colorScheme.onPrimary, ) } @@ -137,7 +150,7 @@ fun MobileVerificationScreen( phoneNumber = phone fullPhoneNumber = fullPhone isNumberValid = valid - } + }, ) } @@ -145,11 +158,12 @@ fun MobileVerificationScreen( EnterOtpScreen( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 48.dp, vertical = 24.dp) - ) { isValidated, otp -> - isOtpValidated = isValidated - validatedOtp = otp - } + .padding(horizontal = 48.dp, vertical = 24.dp), + onOtpValidated = { isValidated, otp -> + isOtpValidated = isValidated + validatedOtp = otp + }, + ) } } @@ -171,8 +185,9 @@ fun MobileVerificationScreen( stringResource(id = R.string.feature_auth_verify_phone).uppercase() } else { stringResource(id = R.string.feature_auth_verify_otp).uppercase() - }, style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.onPrimary + }, + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.onPrimary, ) } } @@ -184,29 +199,29 @@ fun MobileVerificationScreen( } @Composable -fun EnterPhoneScreen( - modifier: Modifier, - onNumberUpdated: (String, String, Boolean) -> Unit +private fun EnterPhoneScreen( + onNumberUpdated: (String, String, Boolean) -> Unit, + modifier: Modifier = Modifier, ) { val keyboardController = LocalSoftwareKeyboardController.current TogiCountryCodePicker( modifier = modifier, shape = RoundedCornerShape(8.dp), colors = TextFieldDefaults.outlinedTextFieldColors( - focusedBorderColor = MaterialTheme.colorScheme.primary + focusedBorderColor = MaterialTheme.colorScheme.primary, ), onValueChange = { (code, phone), isValid -> onNumberUpdated(phone, code + phone, isValid) }, label = { Text(stringResource(id = R.string.feature_auth_phone_number)) }, - keyboardActions = KeyboardActions { keyboardController?.hide() } + keyboardActions = KeyboardActions { keyboardController?.hide() }, ) } @Composable -fun EnterOtpScreen( - modifier: Modifier, - onOtpValidated: (Boolean, String) -> Unit +private fun EnterOtpScreen( + onOtpValidated: (Boolean, String) -> Unit, + modifier: Modifier = Modifier, ) { val keyboardController = LocalSoftwareKeyboardController.current var otp by rememberSaveable(stateSaver = TextFieldValue.Saver) { @@ -221,20 +236,21 @@ fun EnterOtpScreen( onOtpValidated(otp.text.length == 6, otp.text) }, label = R.string.feature_auth_enter_otp, - keyboardActions = KeyboardActions { keyboardController?.hide() } + keyboardActions = KeyboardActions { keyboardController?.hide() }, ) } @Composable -fun ShowProgressScreen( +private fun ShowProgressScreen( uiState: MobileVerificationUiState, + modifier: Modifier = Modifier, ) { Box( - modifier = Modifier + modifier = modifier .fillMaxSize() .background(color = MaterialTheme.colorScheme.primary.copy(alpha = 0.6f)) .focusable(), - contentAlignment = Alignment.Center + contentAlignment = Alignment.Center, ) { MifosLoadingWheel( modifier = Modifier.wrapContentSize(), @@ -242,31 +258,33 @@ fun ShowProgressScreen( Constants.SENDING_OTP_TO_YOUR_MOBILE_NUMBER } else { Constants.VERIFYING_OTP - } + }, ) } } @Preview @Composable -fun MobileVerificationScreenVerifyPhonePreview() { +private fun MobileVerificationScreenVerifyPhonePreview() { MifosTheme { - MobileVerificationScreen(uiState = MobileVerificationUiState.VerifyPhone, + MobileVerificationScreen( + uiState = MobileVerificationUiState.VerifyPhone, showProgressState = false, verifyMobileAndRequestOtp = { _, _ -> }, - verifyOtp = { _, _ -> } + verifyOtp = { _, _ -> }, ) } } @Preview @Composable -fun MobileVerificationScreenVerifyOtpPreview() { +private fun MobileVerificationScreenVerifyOtpPreview() { MifosTheme { - MobileVerificationScreen(uiState = MobileVerificationUiState.VerifyOtp, + MobileVerificationScreen( + uiState = MobileVerificationUiState.VerifyOtp, showProgressState = false, verifyMobileAndRequestOtp = { _, _ -> }, - verifyOtp = { _, _ -> } + verifyOtp = { _, _ -> }, ) } } diff --git a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/mobileVerify/MobileVerificationViewModel.kt b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/mobileVerify/MobileVerificationViewModel.kt index c8649987..001e8264 100644 --- a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/mobileVerify/MobileVerificationViewModel.kt +++ b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/mobileVerify/MobileVerificationViewModel.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.auth.mobileVerify import androidx.compose.runtime.getValue @@ -20,7 +29,7 @@ import javax.inject.Inject @Suppress("UnusedParameter") class MobileVerificationViewModel @Inject constructor( private val mUseCaseHandler: UseCaseHandler, - private val searchClientUseCase: SearchClient + private val searchClientUseCase: SearchClient, ) : ViewModel() { private val _uiState = @@ -35,10 +44,11 @@ class MobileVerificationViewModel @Inject constructor( fun verifyMobileAndRequestOtp( fullNumber: String, mobileNo: String, - onError: (String?) -> Unit + onError: (String?) -> Unit, ) { showProgress = true - mUseCaseHandler.execute(searchClientUseCase, + mUseCaseHandler.execute( + searchClientUseCase, fullNumber.let { SearchClient.RequestValues(it) }, object : UseCase.UseCaseCallback { override fun onSuccess(response: SearchClient.ResponseValue) { @@ -49,7 +59,8 @@ class MobileVerificationViewModel @Inject constructor( override fun onError(message: String) { requestOtp(fullNumber) } - }) + }, + ) } /** diff --git a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/navigation/LoginScreenNavigation.kt b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/navigation/LoginScreenNavigation.kt index 5c1d3bb9..a4ec2ab3 100644 --- a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/navigation/LoginScreenNavigation.kt +++ b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/navigation/LoginScreenNavigation.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.auth.navigation import androidx.navigation.NavController @@ -10,7 +19,7 @@ const val LOGIN_ROUTE = "login_route" @Suppress("UnusedParameter") fun NavGraphBuilder.loginScreen( onDismissSignUp: () -> Unit, - onNavigateToMobileVerificationScreen:(Int,String,String,String,String,) -> Unit + onNavigateToMobileVerificationScreen: (Int, String, String, String, String) -> Unit, ) { composable(route = LOGIN_ROUTE) { LoginScreen( @@ -22,4 +31,4 @@ fun NavGraphBuilder.loginScreen( fun NavController.navigateToLogin() { this.navigate(LOGIN_ROUTE) -} \ No newline at end of file +} diff --git a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/navigation/MobileVerificationScreenNavigation.kt b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/navigation/MobileVerificationScreenNavigation.kt index 9c96c64d..112dd145 100644 --- a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/navigation/MobileVerificationScreenNavigation.kt +++ b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/navigation/MobileVerificationScreenNavigation.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ @file:Suppress("MaxLineLength") package org.mifospay.feature.auth.navigation @@ -13,17 +22,32 @@ import org.mifospay.feature.auth.mobileVerify.MobileVerificationScreen const val MOBILE_VERIFICATION_ROUTE = "mobile_verification_route" fun NavGraphBuilder.mobileVerificationScreen( - onOtpVerificationSuccess: (String, Map) -> Unit + onOtpVerificationSuccess: (String, Map) -> Unit, ) { composable( route = "$MOBILE_VERIFICATION_ROUTE?mifosSignedUp={mifosSignedUp}&googleDisplayName={googleDisplayName}&googleEmail={googleEmail}&googleFamilyName={googleFamilyName}&googleGivenName={googleGivenName}", arguments = listOf( - navArgument("mifosSignedUp") { type = NavType.IntType; defaultValue = 0 }, - navArgument("googleDisplayName") { type = NavType.StringType; nullable = true }, - navArgument("googleEmail") { type = NavType.StringType; nullable = true }, - navArgument("googleFamilyName") { type = NavType.StringType; nullable = true }, - navArgument("googleGivenName") { type = NavType.StringType; nullable = true } - ) + navArgument("mifosSignedUp") { + type = NavType.IntType + defaultValue = 0 + }, + navArgument("googleDisplayName") { + type = NavType.StringType + nullable = true + }, + navArgument("googleEmail") { + type = NavType.StringType + nullable = true + }, + navArgument("googleFamilyName") { + type = NavType.StringType + nullable = true + }, + navArgument("googleGivenName") { + type = NavType.StringType + nullable = true + }, + ), ) { backStackEntry -> val mifosSignedUp = backStackEntry.arguments?.getInt("mifosSignedUp") ?: 0 val googleDisplayName = backStackEntry.arguments?.getString("googleDisplayName") @@ -40,10 +64,10 @@ fun NavGraphBuilder.mobileVerificationScreen( Constants.GOOGLE_FAMILY_NAME to googleFamilyName, Constants.GOOGLE_GIVEN_NAME to googleGivenName, Constants.COUNTRY to "Canada", - Constants.MOBILE_NUMBER to fullNumber + Constants.MOBILE_NUMBER to fullNumber, ) onOtpVerificationSuccess(fullNumber, extraData) - } + }, ) } } @@ -53,8 +77,7 @@ fun NavController.navigateToMobileVerification( googleDisplayName: String?, googleEmail: String?, googleFamilyName: String?, - googleGivenName: String? + googleGivenName: String?, ) { this.navigate("$MOBILE_VERIFICATION_ROUTE?mifosSignedUp=$mifosSignedUp&googleDisplayName=$googleDisplayName&googleEmail=$googleEmail&googleFamilyName=$googleFamilyName&googleGivenName=$googleGivenName") } - diff --git a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/navigation/SignupScreenNavigation.kt b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/navigation/SignupScreenNavigation.kt index eff3adfc..6c696eba 100644 --- a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/navigation/SignupScreenNavigation.kt +++ b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/navigation/SignupScreenNavigation.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ @file:Suppress("MaxLineLength") package org.mifospay.feature.auth.navigation @@ -14,19 +23,40 @@ const val SIGNUP_ROUTE = "signup_route" @Suppress("UnusedParameter") fun NavGraphBuilder.signupScreen( onLoginSuccess: () -> Unit, - onRegisterSuccess: () -> Unit + onRegisterSuccess: () -> Unit, ) { composable( route = "$SIGNUP_ROUTE?savingProductId={savingProductId}&mobileNumber={mobileNumber}&country={country}&email={email}&firstName={firstName}&lastName={lastName}&businessName={businessName}", arguments = listOf( - navArgument("savingProductId") { type = NavType.IntType; defaultValue = 0 }, - navArgument("mobileNumber") { type = NavType.StringType; defaultValue = "" }, - navArgument("country") { type = NavType.StringType; defaultValue = "" }, - navArgument("email") { type = NavType.StringType; defaultValue = "" }, - navArgument("firstName") { type = NavType.StringType; defaultValue = "" }, - navArgument("lastName") { type = NavType.StringType; defaultValue = "" }, - navArgument("businessName") { type = NavType.StringType; defaultValue = "" } - ) + navArgument("savingProductId") { + type = NavType.IntType + defaultValue = 0 + }, + navArgument("mobileNumber") { + type = NavType.StringType + defaultValue = "" + }, + navArgument("country") { + type = NavType.StringType + defaultValue = "" + }, + navArgument("email") { + type = NavType.StringType + defaultValue = "" + }, + navArgument("firstName") { + type = NavType.StringType + defaultValue = "" + }, + navArgument("lastName") { + type = NavType.StringType + defaultValue = "" + }, + navArgument("businessName") { + type = NavType.StringType + defaultValue = "" + }, + ), ) { backStackEntry -> val savingProductId = backStackEntry.arguments?.getInt("savingProductId") ?: 0 val mobileNumber = backStackEntry.arguments?.getString("mobileNumber") ?: "" @@ -44,7 +74,7 @@ fun NavGraphBuilder.signupScreen( email = email, firstName = firstName, lastName = lastName, - businessName = businessName + businessName = businessName, ) } } @@ -56,12 +86,12 @@ fun NavController.navigateToSignup( email: String = "", firstName: String = "", lastName: String = "", - businessName: String = "" + businessName: String = "", ) { this.navigate( "$SIGNUP_ROUTE?savingProductId=$savingProductId" + - "&mobileNumber=$mobileNumber&country=$country&email=$email" + - "&firstName=$firstName&lastName=$lastName&businessName=$businessName" + "&mobileNumber=$mobileNumber&country=$country&email=$email" + + "&firstName=$firstName&lastName=$lastName&businessName=$businessName", ) } @@ -74,4 +104,4 @@ fun onRegisterSuccess(s: String?) { // Toast.makeText(this, "Registered successfully.", Toast.LENGTH_SHORT).show() // startActivity(Intent(this@SignupActivity, LoginActivity::class.java)) // finish() -} \ No newline at end of file +} diff --git a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/signup/SignupScreen.kt b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/signup/SignupScreen.kt index 43cfa23c..b8c0d15b 100644 --- a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/signup/SignupScreen.kt +++ b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/signup/SignupScreen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.auth.signup import android.widget.Toast @@ -49,18 +58,18 @@ import org.mifospay.feature.auth.R import org.mifospay.feature.auth.utils.ValidateUtil.isValidEmail import java.util.Locale - @Composable -fun SignupScreen( - viewModel: SignupViewModel = hiltViewModel(), +internal fun SignupScreen( + savingProductId: Int, + mobileNumber: String, + country: String, + email: String, + firstName: String, + lastName: String, + businessName: String, onLoginSuccess: () -> Unit, - savingProductId:Int, - mobileNumber:String, - country:String, - email:String, - firstName:String, - lastName:String, - businessName:String + modifier: Modifier = Modifier, + viewModel: SignupViewModel = hiltViewModel(), ) { val context = LocalContext.current @@ -72,9 +81,9 @@ fun SignupScreen( mobileNumber = mobileNumber, countryName = country, email = email, - firstName = firstName, - lastName = lastName, - businessName = businessName + firstName = firstName, + lastName = lastName, + businessName = businessName, ) } LaunchedEffect(viewModel.isLoginSuccess) { @@ -84,6 +93,7 @@ fun SignupScreen( } SignupScreenContent( + modifier = modifier, showProgressState = viewModel.showProgress, data = viewModel.signupData, stateList = stateList, @@ -91,27 +101,28 @@ fun SignupScreen( viewModel.registerUser(it) { message -> Toast.makeText(context, message, Toast.LENGTH_SHORT).show() } - } + }, ) } @Composable @Suppress("LongMethod", "CyclomaticComplexMethod") -fun SignupScreenContent( - showProgressState: Boolean = false, +private fun SignupScreenContent( data: SignupData, stateList: List, - onCompleteRegistration: (SignupData) -> Unit + onCompleteRegistration: (SignupData) -> Unit, + modifier: Modifier = Modifier, + showProgressState: Boolean = false, ) { - val context = LocalContext.current var firstName by rememberSaveable { mutableStateOf(data.firstName ?: "") } var lastName by rememberSaveable { mutableStateOf(data.lastName ?: "") } var email by rememberSaveable { mutableStateOf(data.email ?: "") } var userName by rememberSaveable { - mutableStateOf(data.email?.ifEmpty { "" } - ?: data.email?.let { it.substring(0, it.indexOf('@')) } ?: "" + mutableStateOf( + data.email?.ifEmpty { "" } + ?: data.email?.let { it.substring(0, it.indexOf('@')) } ?: "", ) } var addressLine1 by rememberSaveable { mutableStateOf("") } @@ -127,16 +138,19 @@ fun SignupScreenContent( var selectedState by rememberSaveable { mutableStateOf(null) } fun validateAllFields() { - val isAnyFieldEmpty = firstName.isEmpty() || lastName.isEmpty() || email.isEmpty() - || userName.isEmpty() || addressLine1.isEmpty() || addressLine2.isEmpty() - || pinCode.isEmpty() || password.isEmpty() || confirmPassword.isEmpty() - || selectedState == null - val isNameOfBusinessEmpty = data.mifosSavingsProductId == MIFOS_MERCHANT_SAVINGS_PRODUCT_ID - && nameOfBusiness.isEmpty() + val isAnyFieldEmpty = firstName.isEmpty() || lastName.isEmpty() || email.isEmpty() || + userName.isEmpty() || addressLine1.isEmpty() || addressLine2.isEmpty() || + pinCode.isEmpty() || password.isEmpty() || confirmPassword.isEmpty() || + selectedState == null + + val isNameOfBusinessEmpty = data.mifosSavingsProductId == MIFOS_MERCHANT_SAVINGS_PRODUCT_ID && + nameOfBusiness.isEmpty() if (!email.isValidEmail()) { Toast.makeText( - context, context.getString(R.string.feature_auth_validate_email), Toast.LENGTH_SHORT + context, + context.getString(R.string.feature_auth_validate_email), + Toast.LENGTH_SHORT, ).show() return } @@ -145,7 +159,7 @@ fun SignupScreenContent( Toast.makeText( context, context.getString(R.string.feature_auth_all_fields_are_mandatory), - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() return } @@ -162,12 +176,12 @@ fun SignupScreenContent( pinCode = pinCode, businessName = nameOfBusiness, password = password, - stateId = selectedState?.id + stateId = selectedState?.id, ) onCompleteRegistration.invoke(signUpData) } - Box { + Box(modifier) { Column( modifier = Modifier .fillMaxSize() @@ -179,33 +193,37 @@ fun SignupScreenContent( modifier = Modifier .fillMaxWidth() .background(color = MaterialTheme.colorScheme.primary), - verticalArrangement = Arrangement.Top + verticalArrangement = Arrangement.Top, ) { Text( modifier = Modifier.padding(top = 48.dp, start = 24.dp, end = 24.dp), text = stringResource(id = R.string.feature_auth_complete_your_registration), - style = MaterialTheme.typography.titleLarge.copy(color = MaterialTheme.colorScheme.onPrimary) + style = MaterialTheme.typography.titleLarge.copy(color = MaterialTheme.colorScheme.onPrimary), ) Text( modifier = Modifier.padding( - top = 4.dp, bottom = 32.dp, start = 24.dp, end = 24.dp + top = 4.dp, + bottom = 32.dp, + start = 24.dp, + end = 24.dp, ), text = stringResource(id = R.string.feature_auth_all_fields_are_mandatory), - style = MaterialTheme.typography.bodySmall.copy(color = Color.White) + style = MaterialTheme.typography.bodySmall.copy(color = Color.White), ) } + Column( modifier = Modifier .fillMaxWidth() .padding(horizontal = 32.dp) - .focusable(!showProgressState) + .focusable(!showProgressState), ) { UserInfoTextField( modifier = Modifier .fillMaxWidth() .padding(top = 16.dp), label = stringResource(id = R.string.feature_auth_first_name), - value = firstName + value = firstName, ) { firstName = it } @@ -214,7 +232,7 @@ fun SignupScreenContent( .fillMaxWidth() .padding(top = 8.dp), label = stringResource(id = R.string.feature_auth_last_name), - value = lastName + value = lastName, ) { lastName = it } @@ -223,7 +241,7 @@ fun SignupScreenContent( .fillMaxWidth() .padding(top = 8.dp), label = stringResource(id = R.string.feature_auth_username), - value = userName + value = userName, ) { userName = it } @@ -244,7 +262,7 @@ fun SignupScreenContent( .fillMaxWidth() .padding(top = 8.dp), label = stringResource(id = R.string.feature_auth_email), - value = email + value = email, ) { email = it } @@ -254,7 +272,7 @@ fun SignupScreenContent( .fillMaxWidth() .padding(top = 8.dp), label = stringResource(id = R.string.feature_auth_name_of_business), - value = nameOfBusiness + value = nameOfBusiness, ) { nameOfBusiness = it } @@ -264,7 +282,7 @@ fun SignupScreenContent( .fillMaxWidth() .padding(top = 8.dp), label = stringResource(id = R.string.feature_auth_address_line_1), - value = addressLine1 + value = addressLine1, ) { addressLine1 = it } @@ -273,14 +291,14 @@ fun SignupScreenContent( .fillMaxWidth() .padding(top = 8.dp), label = stringResource(id = R.string.feature_auth_address_line_2), - value = addressLine2 + value = addressLine2, ) { addressLine2 = it } UserInfoTextField( modifier = Modifier.padding(top = 8.dp), label = stringResource(id = R.string.feature_auth_pin_code), - value = pinCode + value = pinCode, ) { pinCode = it } @@ -288,7 +306,7 @@ fun SignupScreenContent( MifosStateDropDownOutlinedTextField( value = selectedState?.name ?: "", label = stringResource(id = R.string.feature_auth_state), - stateList = stateList + stateList = stateList, ) { selectedState = it } @@ -307,7 +325,7 @@ fun SignupScreenContent( ) { Text( text = stringResource(id = R.string.feature_auth_complete), - style = styleMedium16sp.copy(color = MaterialTheme.colorScheme.onPrimary) + style = styleMedium16sp.copy(color = MaterialTheme.colorScheme.onPrimary), ) } } @@ -321,19 +339,19 @@ fun SignupScreenContent( @OptIn(ExperimentalMaterial3Api::class) @Composable -fun MifosStateDropDownOutlinedTextField( - modifier: Modifier = Modifier, +private fun MifosStateDropDownOutlinedTextField( value: String, label: String, stateList: List, - onSelectedState: (State) -> Unit + modifier: Modifier = Modifier, + onSelectedState: (State) -> Unit = {}, ) { var expanded by rememberSaveable { mutableStateOf(false) } ExposedDropdownMenuBox( expanded = expanded, onExpandedChange = { expanded = !expanded - } + }, ) { OutlinedTextField( modifier = modifier.menuAnchor(), @@ -343,21 +361,21 @@ fun MifosStateDropDownOutlinedTextField( label = { Text(label) }, trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) - } + }, ) DropdownMenu( expanded = expanded, onDismissRequest = { expanded = false - }) - { + }, + ) { stateList.forEach { DropdownMenuItem( text = { Text(text = it.name) }, onClick = { expanded = false onSelectedState(it) - } + }, ) } } @@ -365,11 +383,11 @@ fun MifosStateDropDownOutlinedTextField( } @Composable -fun UserInfoTextField( - modifier: Modifier = Modifier, +private fun UserInfoTextField( label: String, value: String, - onValueChange: (String) -> Unit + modifier: Modifier = Modifier, + onValueChange: (String) -> Unit = {}, ) { MfOutlinedTextField( modifier = modifier, @@ -377,12 +395,12 @@ fun UserInfoTextField( label = label, isError = value.isEmpty(), errorMessage = stringResource(id = R.string.feature_auth_mandatory), - onValueChange = onValueChange + onValueChange = onValueChange, ) } @Composable -fun PasswordAndConfirmPassword( +private fun PasswordAndConfirmPassword( password: String, onPasswordChange: (String) -> Unit, confirmPassword: String, @@ -391,8 +409,9 @@ fun PasswordAndConfirmPassword( onTogglePasswordVisibility: () -> Unit, isConfirmPasswordVisible: Boolean, onConfirmTogglePasswordVisibility: () -> Unit, + modifier: Modifier = Modifier, ) { - Column { + Column(modifier) { MfPasswordTextField( modifier = Modifier.fillMaxWidth(), password = password, @@ -402,10 +421,12 @@ fun PasswordAndConfirmPassword( stringResource(id = R.string.feature_auth_password_cannot_be_empty) } else if (password.length < 6) { stringResource(id = R.string.feature_auth_password_must_be_least_6_characters) - } else null, + } else { + null + }, onPasswordChange = onPasswordChange, isPasswordVisible = isPasswordVisible, - onTogglePasswordVisibility = onTogglePasswordVisibility + onTogglePasswordVisibility = onTogglePasswordVisibility, ) MfPasswordTextField( modifier = Modifier.fillMaxWidth(), @@ -416,19 +437,25 @@ fun PasswordAndConfirmPassword( stringResource(id = R.string.feature_auth_confirm_password_cannot_empty) } else if (password != confirmPassword) { stringResource(id = R.string.feature_auth_passwords_do_not_match) - } else null, + } else { + null + }, onPasswordChange = onConfirmPasswordChange, isPasswordVisible = isConfirmPasswordVisible, - onTogglePasswordVisibility = onConfirmTogglePasswordVisibility + onTogglePasswordVisibility = onConfirmTogglePasswordVisibility, ) if (password.length >= 6) { Text( modifier = Modifier.padding(top = 8.dp), text = "${stringResource(id = R.string.feature_auth_password_strength)}${ getPasswordStrength(password).replaceFirstChar { - if (it.isLowerCase()) it.titlecase( - Locale.ENGLISH - ) else it.toString() + if (it.isLowerCase()) { + it.titlecase( + Locale.ENGLISH, + ) + } else { + it.toString() + } } }", color = getPasswordStrengthColor(password), @@ -437,7 +464,6 @@ fun PasswordAndConfirmPassword( } } - private fun getPasswordStrength(password: String): String { val hasUpperCase = password.any { it.isUpperCase() } val hasLowerCase = password.any { it.isLowerCase() } @@ -448,12 +474,12 @@ private fun getPasswordStrength(password: String): String { hasUpperCase.toInt(), hasLowerCase.toInt(), hasNumbers.toInt(), - hasSymbols.toInt() + hasSymbols.toInt(), ).sum() return PasswordStrength.entries[numTypesPresent].name } -fun Boolean.toInt() = if (this) 1 else 0 +private fun Boolean.toInt() = if (this) 1 else 0 private fun getPasswordStrengthColor(password: String): Color { val strength = getPasswordStrength(password) @@ -469,11 +495,11 @@ private fun getPasswordStrengthColor(password: String): Color { @Preview @Composable -fun SignupScreenPreview() { +private fun SignupScreenPreview() { SignupScreenContent( showProgressState = false, data = SignupData(), stateList = listOf(), - onCompleteRegistration = { } + onCompleteRegistration = { }, ) } diff --git a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/signup/SignupViewModel.kt b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/signup/SignupViewModel.kt index c84b77c9..e6f66472 100644 --- a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/signup/SignupViewModel.kt +++ b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/signup/SignupViewModel.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.auth.signup import androidx.compose.runtime.getValue @@ -33,7 +42,6 @@ import org.mifospay.core.data.repository.local.LocalAssetRepository import org.mifospay.core.datastore.PreferencesHelper import javax.inject.Inject - @HiltViewModel class SignupViewModel @Inject constructor( localAssetRepository: LocalAssetRepository, @@ -46,7 +54,7 @@ class SignupViewModel @Inject constructor( private val authenticateUserUseCase: AuthenticateUser, private val fetchClientDataUseCase: FetchClientData, private val deleteUserUseCase: DeleteUser, - private val fetchUserDetailsUseCase: FetchUserDetails + private val fetchUserDetailsUseCase: FetchUserDetails, ) : ViewModel() { var showProgress by mutableStateOf(false) @@ -62,7 +70,7 @@ class SignupViewModel @Inject constructor( email: String?, firstName: String?, lastName: String?, - businessName: String? + businessName: String?, ) { signupData = signupData.copy( mifosSavingsProductId = savingProductId, @@ -71,19 +79,19 @@ class SignupViewModel @Inject constructor( email = email, firstName = firstName!!, lastName = lastName!!, - businessName = businessName + businessName = businessName, ) } val states: StateFlow> = combine( localAssetRepository.getCountries(), localAssetRepository.getStateList(), - ::Pair + ::Pair, ) .map { val countries = it.first signupData = signupData.copy( - countryId = countries.find { it.name == signupData.countryName }?.id ?: "" + countryId = countries.find { it.name == signupData.countryName }?.id ?: "", ) it.second.filter { it.countryId == signupData.countryId } } @@ -101,7 +109,8 @@ class SignupViewModel @Inject constructor( // 2. Create user // 3. Create Client // 4. Update User and connect client with user - useCaseHandler.execute(searchClientUseCase, + useCaseHandler.execute( + searchClientUseCase, SearchClient.RequestValues("${signupData.userName}@mifos"), object : UseCase.UseCaseCallback { override fun onSuccess(response: SearchClient.ResponseValue) { @@ -111,15 +120,21 @@ class SignupViewModel @Inject constructor( override fun onError(message: String) { createUser(showToastMessage) } - }) + }, + ) } private fun createUser(showToastMessage: (String) -> Unit) { val newUser = NewUser( - signupData.userName, signupData.firstName, signupData.lastName, - signupData.email, signupData.password + signupData.userName, + signupData.firstName, + signupData.lastName, + signupData.email, + signupData.password, ) - useCaseHandler.execute(createUserUseCase, CreateUser.RequestValues(newUser), + useCaseHandler.execute( + createUserUseCase, + CreateUser.RequestValues(newUser), object : UseCase.UseCaseCallback { override fun onSuccess(response: CreateUser.ResponseValue) { createClient(response.userId, showToastMessage) @@ -129,16 +144,18 @@ class SignupViewModel @Inject constructor( DebugUtil.log(message) showToastMessage(message) } - }) + }, + ) } private fun createClient(userId: Int, showToastMessage: (String) -> Unit) { val newClient = com.mifospay.core.model.domain.client.NewClient( signupData.businessName, signupData.userName, signupData.addressLine1, signupData.addressLine2, signupData.city, signupData.pinCode, signupData.stateId, - signupData.countryId, signupData.mobileNumber, signupData.mifosSavingsProductId + signupData.countryId, signupData.mobileNumber, signupData.mifosSavingsProductId, ) - useCaseHandler.execute(createClientUseCase, + useCaseHandler.execute( + createClientUseCase, CreateClient.RequestValues(newClient), object : UseCase.UseCaseCallback { override fun onSuccess(response: CreateClient.ResponseValue) { @@ -154,15 +171,17 @@ class SignupViewModel @Inject constructor( showToastMessage(message) deleteUser(userId) } - }) + }, + ) } private fun updateClient( clients: ArrayList, userId: Int, - showToastMessage: (String) -> Unit + showToastMessage: (String) -> Unit, ) { - useCaseHandler.execute(updateUserUseCase, + useCaseHandler.execute( + updateUserUseCase, UpdateUser.RequestValues(UpdateUserEntityClients(clients), userId), object : UseCase.UseCaseCallback { override fun onSuccess(response: UpdateUser.ResponseValue?) { @@ -174,17 +193,20 @@ class SignupViewModel @Inject constructor( DebugUtil.log(message) showToastMessage("update client error") } - }) + }, + ) } private fun loginUser( username: String?, password: String?, - showToastMessage: (String) -> Unit + showToastMessage: (String) -> Unit, ) { authenticateUserUseCase.walletRequestValues = AuthenticateUser.RequestValues(username!!, password!!) val requestValue = authenticateUserUseCase.walletRequestValues - useCaseHandler.execute(authenticateUserUseCase, requestValue, + useCaseHandler.execute( + authenticateUserUseCase, + requestValue, object : UseCase.UseCaseCallback { override fun onSuccess(response: AuthenticateUser.ResponseValue) { createAuthenticatedService(response.user) @@ -195,11 +217,13 @@ class SignupViewModel @Inject constructor( override fun onError(message: String) { showToastMessage("Login Failed") } - }) + }, + ) } private fun fetchUserDetails(user: User) { - useCaseHandler.execute(fetchUserDetailsUseCase, + useCaseHandler.execute( + fetchUserDetailsUseCase, FetchUserDetails.RequestValues(user.userId), object : UseCase.UseCaseCallback { override fun onSuccess(response: FetchUserDetails.ResponseValue) { @@ -209,11 +233,14 @@ class SignupViewModel @Inject constructor( override fun onError(message: String) { DebugUtil.log(message) } - }) + }, + ) } private fun fetchClientData(showToastMessage: (String) -> Unit) { - useCaseHandler.execute(fetchClientDataUseCase, null, + useCaseHandler.execute( + fetchClientDataUseCase, + null, object : UseCase.UseCaseCallback { override fun onSuccess(response: FetchClientData.ResponseValue) { saveClientDetails(response.clientDetails) @@ -225,7 +252,8 @@ class SignupViewModel @Inject constructor( override fun onError(message: String) { showToastMessage("Fetch Client Error") } - }) + }, + ) } private fun createAuthenticatedService(user: User) { @@ -235,7 +263,7 @@ class SignupViewModel @Inject constructor( private fun saveUserDetails( user: User, - userWithRole: UserWithRole + userWithRole: UserWithRole, ) { val userName = user.username val userID = user.userId @@ -251,11 +279,14 @@ class SignupViewModel @Inject constructor( } private fun deleteUser(userId: Int) { - useCaseHandler.execute(deleteUserUseCase, DeleteUser.RequestValues(userId), + useCaseHandler.execute( + deleteUserUseCase, + DeleteUser.RequestValues(userId), object : UseCase.UseCaseCallback { override fun onSuccess(response: DeleteUser.ResponseValue) {} override fun onError(message: String) {} - }) + }, + ) } } diff --git a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/socialSignup/SocialSignupMethodScreen.kt b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/socialSignup/SocialSignupMethodScreen.kt index 106ba0fb..eb2c7643 100644 --- a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/socialSignup/SocialSignupMethodScreen.kt +++ b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/socialSignup/SocialSignupMethodScreen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ @file:Suppress("MaxLineLength") package org.mifospay.feature.auth.socialSignup @@ -61,18 +70,22 @@ const val TAG = "Social Login" // Followed this https://medium.com/@nirmale.ashwin9696/a-comprehensive-guide-to-google-sign-in-integration-with-credential-manager-in-android-apps-05286f8f5848 // Keeping until we fix sign up @Composable -fun SocialSignupMethodContentScreen( - onDismissSignUp: () -> Unit +internal fun SocialSignupMethodContentScreen( + modifier: Modifier = Modifier, + onDismissSignUp: () -> Unit = {}, ) { - SocialSignupMethodScreen(onDismissSignUp = onDismissSignUp) + SocialSignupMethodScreen( + modifier = modifier, + onDismissSignUp = onDismissSignUp, + ) } @Composable @Suppress("NestedBlockDepth") -fun SocialSignupMethodScreen( - onDismissSignUp: () -> Unit +private fun SocialSignupMethodScreen( + modifier: Modifier = Modifier, + onDismissSignUp: () -> Unit = {}, ) { - val context = LocalContext.current var mifosSavingProductId by remember { mutableIntStateOf(0) } var showProgress by remember { mutableStateOf(false) } @@ -91,7 +104,6 @@ fun SocialSignupMethodScreen( .addCredentialOption(googleIdOption) .build() - fun signUpWithMifos() { googleIdTokenCredential.signUpWithMifos(context, mifosSavingProductId) { coroutineScope.launch { @@ -115,7 +127,7 @@ fun SocialSignupMethodScreen( Toast.makeText( context, Constants.GOOGLE_SIGN_IN_FAILED, - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() } } catch (e: GoogleIdTokenParsingException) { @@ -134,7 +146,6 @@ fun SocialSignupMethodScreen( } } - fun signUpCredentialManager() { coroutineScope.launch { try { @@ -161,6 +172,7 @@ fun SocialSignupMethodScreen( } MifosBottomSheet( + modifier = modifier, content = { SignupMethodContentScreen( showProgress = showProgress, @@ -171,37 +183,37 @@ fun SocialSignupMethodScreen( onSignupAsCustomer = { checkedGoogleAccount -> mifosSavingProductId = MIFOS_CONSUMER_SAVINGS_PRODUCT_ID signUp(checkedGoogleAccount) - } + }, ) }, onDismiss = { onDismissSignUp.invoke() - } + }, ) } @Composable @Suppress("LongMethod") -fun SignupMethodContentScreen( +private fun SignupMethodContentScreen( showProgress: Boolean, onSignUpAsMerchant: (Boolean) -> Unit, onSignupAsCustomer: (Boolean) -> Unit, + modifier: Modifier = Modifier, ) { - var checkedGoogleAccountState by remember { mutableStateOf(true) } Box( - modifier = Modifier, + modifier = modifier, ) { Column( modifier = Modifier .fillMaxSize() .background(color = MaterialTheme.colorScheme.surface), - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, ) { Text( modifier = Modifier.padding(top = 16.dp), - text = stringResource(id = R.string.feature_auth_create_an_account) + text = stringResource(id = R.string.feature_auth_create_an_account), ) OutlinedButton( modifier = Modifier.padding(top = 48.dp), @@ -210,36 +222,36 @@ fun SignupMethodContentScreen( }, border = BorderStroke(1.dp, Color.LightGray), shape = RoundedCornerShape(4.dp), - colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.primary) + colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.primary), ) { Text( text = stringResource(id = R.string.feature_auth_sign_up_as_merchant).uppercase(), - style = MaterialTheme.typography.labelMedium + style = MaterialTheme.typography.labelMedium, ) } Row( modifier = Modifier .fillMaxWidth() .padding(top = 24.dp), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { HorizontalDivider( modifier = Modifier .padding(start = 24.dp, end = 8.dp) .weight(.4f), - thickness = 1.dp + thickness = 1.dp, ) Text( modifier = Modifier .wrapContentWidth() .weight(.1f), - text = stringResource(id = R.string.feature_auth_or) + text = stringResource(id = R.string.feature_auth_or), ) HorizontalDivider( modifier = Modifier .padding(start = 8.dp, end = 24.dp) .weight(.4f), - thickness = 1.dp + thickness = 1.dp, ) } OutlinedButton( @@ -249,11 +261,11 @@ fun SignupMethodContentScreen( }, border = BorderStroke(1.dp, Color.LightGray), shape = RoundedCornerShape(4.dp), - colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.primary) + colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.primary), ) { Text( text = stringResource(id = R.string.feature_auth_sign_up_as_customer).uppercase(), - style = MaterialTheme.typography.labelMedium + style = MaterialTheme.typography.labelMedium, ) } Row( @@ -262,18 +274,18 @@ fun SignupMethodContentScreen( .clickable { checkedGoogleAccountState = !checkedGoogleAccountState }, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Checkbox( checked = checkedGoogleAccountState, onCheckedChange = { checkedGoogleAccountState = !checkedGoogleAccountState }, - colors = CheckboxDefaults.colors(MaterialTheme.colorScheme.primary) + colors = CheckboxDefaults.colors(MaterialTheme.colorScheme.primary), ) Text( text = stringResource(id = R.string.feature_auth_ease_my_sign_up_using_google_account), - style = MaterialTheme.typography.labelSmall + style = MaterialTheme.typography.labelSmall, ) } HorizontalDivider(thickness = 48.dp, color = Color.Transparent) @@ -283,7 +295,7 @@ fun SignupMethodContentScreen( modifier = Modifier .fillMaxWidth() .padding(top = 140.dp), - contentAlignment = Alignment.Center + contentAlignment = Alignment.Center, ) { CircularProgressIndicator( modifier = Modifier.size(64.dp), @@ -296,20 +308,20 @@ fun SignupMethodContentScreen( } @Suppress("UnusedParameter") -fun GoogleIdTokenCredential?.signUpWithMifos( +private fun GoogleIdTokenCredential?.signUpWithMifos( context: Context, mifosSavingsProductId: Int, - signOutGoogleClient: () -> Unit + signOutGoogleClient: () -> Unit, ) { val googleIdTokenCredential = this - //Todo:navigate to MobileVerificationScreen with googleIdTokenCredential.givenName,profilePictureUri, + // Todo:navigate to MobileVerificationScreen with googleIdTokenCredential.givenName,profilePictureUri, // familyName,mifosSavingsProductId,displayName,data.getString("com.google.android.libraries.identity.googleid.BUNDLE_KEY_ID") signOutGoogleClient.invoke() } @Preview @Composable -fun SignupMethodContentScreenPreview() { +private fun SignupMethodContentScreenPreview() { MaterialTheme { SignupMethodContentScreen(true, {}, {}) } diff --git a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/utils/ValidateUtil.kt b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/utils/ValidateUtil.kt index f0b9748b..543bead4 100644 --- a/feature/auth/src/main/kotlin/org/mifospay/feature/auth/utils/ValidateUtil.kt +++ b/feature/auth/src/main/kotlin/org/mifospay/feature/auth/utils/ValidateUtil.kt @@ -1,7 +1,16 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.auth.utils import android.util.Patterns object ValidateUtil { - fun String.isValidEmail() = this.isNotEmpty() && Patterns.EMAIL_ADDRESS.matcher(this).matches() -} \ No newline at end of file + fun String.isValidEmail() = this.isNotEmpty() && Patterns.EMAIL_ADDRESS.matcher(this).matches() +} diff --git a/feature/auth/src/main/res/values/strings.xml b/feature/auth/src/main/res/values/strings.xml index 6071208d..13745ed1 100644 --- a/feature/auth/src/main/res/values/strings.xml +++ b/feature/auth/src/main/res/values/strings.xml @@ -1,4 +1,13 @@ + Login Welcome back! diff --git a/feature/auth/src/test/java/org/mifospay/mobilewallet/mifospay/auth/ExampleUnitTest.kt b/feature/auth/src/test/java/org/mifospay/mobilewallet/mifospay/auth/ExampleUnitTest.kt deleted file mode 100644 index f5a6e9b7..00000000 --- a/feature/auth/src/test/java/org/mifospay/mobilewallet/mifospay/auth/ExampleUnitTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.mifospay.mobilewallet.mifospay.auth - -import org.junit.Assert.assertEquals -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/feature/editpassword/build.gradle.kts b/feature/editpassword/build.gradle.kts index 176f6fac..0fd1e480 100644 --- a/feature/editpassword/build.gradle.kts +++ b/feature/editpassword/build.gradle.kts @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ plugins { alias(libs.plugins.mifospay.android.feature) alias(libs.plugins.mifospay.android.library.compose) diff --git a/feature/editpassword/src/main/AndroidManifest.xml b/feature/editpassword/src/main/AndroidManifest.xml index 44008a43..3c84395b 100644 --- a/feature/editpassword/src/main/AndroidManifest.xml +++ b/feature/editpassword/src/main/AndroidManifest.xml @@ -1,4 +1,13 @@ + \ No newline at end of file diff --git a/feature/editpassword/src/main/kotlin/org/mifospay/feature/editpassword/EditPasswordScreen.kt b/feature/editpassword/src/main/kotlin/org/mifospay/feature/editpassword/EditPasswordScreen.kt index 99c21a24..5c351797 100644 --- a/feature/editpassword/src/main/kotlin/org/mifospay/feature/editpassword/EditPasswordScreen.kt +++ b/feature/editpassword/src/main/kotlin/org/mifospay/feature/editpassword/EditPasswordScreen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.editpassword import androidx.compose.foundation.layout.Column @@ -35,13 +44,15 @@ import org.mifospay.core.designsystem.component.MifosScaffold import org.mifospay.core.designsystem.theme.MifosTheme @Composable -fun EditPasswordScreen( - viewModel: EditPasswordViewModel = hiltViewModel(), +internal fun EditPasswordScreen( onBackPress: () -> Unit, - onCancelChanges: () -> Unit + onCancelChanges: () -> Unit, + modifier: Modifier = Modifier, + viewModel: EditPasswordViewModel = hiltViewModel(), ) { val editPasswordUiState by viewModel.editPasswordUiState.collectAsStateWithLifecycle() EditPasswordScreen( + modifier = modifier, editPasswordUiState = editPasswordUiState, onCancelChanges = onCancelChanges, onBackPress = onBackPress, @@ -49,18 +60,19 @@ fun EditPasswordScreen( viewModel.updatePassword( currentPassword = currentPass, newPassword = newPass, - newPasswordRepeat = confirmPass + newPasswordRepeat = confirmPass, ) - } + }, ) } @Composable -fun EditPasswordScreen( +private fun EditPasswordScreen( editPasswordUiState: EditPasswordUiState, onCancelChanges: () -> Unit, onBackPress: () -> Unit, - onSave: (currentPass: String, newPass: String, confirmPass: String) -> Unit + onSave: (currentPass: String, newPass: String, confirmPass: String) -> Unit, + modifier: Modifier = Modifier, ) { val context = LocalContext.current var currentPassword by rememberSaveable { mutableStateOf("") } @@ -88,26 +100,25 @@ fun EditPasswordScreen( EditPasswordUiState.Success -> { coroutineScope.launch { currentSnackbarHostState.showSnackbar( - context.getString(R.string.feature_editpassword_password_changed_successfully) + context.getString(R.string.feature_editpassword_password_changed_successfully), ) } } - } } MifosScaffold( + modifier = modifier, topBarTitle = R.string.feature_editpassword_change_password, snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, backPress = onBackPress, - scaffoldContent = { paddingValues -> Column( modifier = Modifier .fillMaxSize() - .padding(paddingValues) + .padding(paddingValues), ) { MfPasswordTextField( modifier = Modifier @@ -119,7 +130,6 @@ fun EditPasswordScreen( isPasswordVisible = isConfirmPasswordVisible, onTogglePasswordVisibility = { isConfirmPasswordVisible = !isConfirmPasswordVisible - }, onPasswordChange = { currentPassword = it }, ) @@ -131,12 +141,16 @@ fun EditPasswordScreen( password = newPassword, label = stringResource(id = R.string.feature_editpassword_new_password), isError = newPassword.isNotEmpty() && newPassword.length < 6, - errorMessage = if (newPassword.isNotEmpty() && newPassword.length < 6) stringResource( - id = R.string.feature_editpassword_password_length_error - ) else null, + errorMessage = if (newPassword.isNotEmpty() && newPassword.length < 6) { + stringResource( + id = R.string.feature_editpassword_password_length_error, + ) + } else { + null + }, isPasswordVisible = isNewPasswordVisible, onTogglePasswordVisibility = { isNewPasswordVisible = !isNewPasswordVisible }, - onPasswordChange = { newPassword = it } + onPasswordChange = { newPassword = it }, ) MfPasswordTextField( modifier = Modifier @@ -147,21 +161,25 @@ fun EditPasswordScreen( isError = newPassword != confirmNewPassword && confirmNewPassword.isNotEmpty(), errorMessage = if (newPassword != confirmNewPassword && confirmNewPassword.isNotEmpty() - ) stringResource( - id = R.string.feature_editpassword_password_mismatch_error - ) else null, + ) { + stringResource( + id = R.string.feature_editpassword_password_mismatch_error, + ) + } else { + null + }, isPasswordVisible = isConfirmNewPasswordVisible, onTogglePasswordVisibility = { isConfirmNewPasswordVisible = !isConfirmNewPasswordVisible }, - onPasswordChange = { confirmNewPassword = it } + onPasswordChange = { confirmNewPassword = it }, ) Row( modifier = Modifier .fillMaxWidth() .padding(top = 20.dp, start = 16.dp, end = 16.dp), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { MifosButton( onClick = { onCancelChanges.invoke() }, @@ -169,7 +187,7 @@ fun EditPasswordScreen( .weight(1f) .padding(8.dp), contentPadding = PaddingValues(16.dp), - content = { Text(text = stringResource(id = R.string.feature_editpassword_cancel)) } + content = { Text(text = stringResource(id = R.string.feature_editpassword_cancel)) }, ) MifosButton( modifier = Modifier @@ -179,11 +197,11 @@ fun EditPasswordScreen( onSave.invoke(currentPassword, newPassword, confirmNewPassword) }, contentPadding = PaddingValues(16.dp), - content = { Text(text = stringResource(id = R.string.feature_editpassword_save)) } + content = { Text(text = stringResource(id = R.string.feature_editpassword_save)) }, ) } } - } + }, ) } @@ -192,14 +210,14 @@ class EditPasswordUiStateProvider : PreviewParameterProvider }) diff --git a/feature/editpassword/src/main/kotlin/org/mifospay/feature/editpassword/EditPasswordViewModel.kt b/feature/editpassword/src/main/kotlin/org/mifospay/feature/editpassword/EditPasswordViewModel.kt index f34565c9..c2213472 100644 --- a/feature/editpassword/src/main/kotlin/org/mifospay/feature/editpassword/EditPasswordViewModel.kt +++ b/feature/editpassword/src/main/kotlin/org/mifospay/feature/editpassword/EditPasswordViewModel.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.editpassword import androidx.lifecycle.ViewModel @@ -19,7 +28,7 @@ class EditPasswordViewModel @Inject constructor( private val mUseCaseHandler: UseCaseHandler, private val mPreferencesHelper: PreferencesHelper, private val authenticateUserUseCase: AuthenticateUser, - private val updateUserUseCase: UpdateUser + private val updateUserUseCase: UpdateUser, ) : ViewModel() { private val _editPasswordUiState = @@ -29,11 +38,11 @@ class EditPasswordViewModel @Inject constructor( fun updatePassword( currentPassword: String?, newPassword: String?, - newPasswordRepeat: String? + newPasswordRepeat: String?, ) { _editPasswordUiState.value = EditPasswordUiState.Loading - if (isNotEmpty(currentPassword) && isNotEmpty(newPassword) - && isNotEmpty(newPasswordRepeat) + if (isNotEmpty(currentPassword) && isNotEmpty(newPassword) && + isNotEmpty(newPasswordRepeat) ) { when { currentPassword == newPassword -> { @@ -45,7 +54,7 @@ class EditPasswordViewModel @Inject constructor( newPasswordRepeat?.let { it1 -> isNewPasswordValid( it, - it1 + it1, ) } } == true -> { @@ -75,19 +84,21 @@ class EditPasswordViewModel @Inject constructor( private fun updatePassword(currentPassword: String, newPassword: String) { // authenticate and then update - mUseCaseHandler.execute(authenticateUserUseCase, + mUseCaseHandler.execute( + authenticateUserUseCase, AuthenticateUser.RequestValues( mPreferencesHelper.username, - currentPassword + currentPassword, ), object : UseCase.UseCaseCallback { override fun onSuccess(response: AuthenticateUser.ResponseValue) { - mUseCaseHandler.execute(updateUserUseCase, + mUseCaseHandler.execute( + updateUserUseCase, UpdateUser.RequestValues( UpdateUserEntityPassword( - newPassword + newPassword, ), - mPreferencesHelper.userId.toInt() + mPreferencesHelper.userId.toInt(), ), object : UseCase.UseCaseCallback { override fun onSuccess(response: UpdateUser.ResponseValue?) { @@ -97,13 +108,15 @@ class EditPasswordViewModel @Inject constructor( override fun onError(message: String) { _editPasswordUiState.value = EditPasswordUiState.Error(message) } - }) + }, + ) } override fun onError(message: String) { _editPasswordUiState.value = EditPasswordUiState.Error("Wrong Password") } - }) + }, + ) } } @@ -111,4 +124,4 @@ sealed interface EditPasswordUiState { data object Loading : EditPasswordUiState data object Success : EditPasswordUiState data class Error(val message: String) : EditPasswordUiState -} \ No newline at end of file +} diff --git a/feature/editpassword/src/main/kotlin/org/mifospay/feature/editpassword/navigation/EditPasswordScreenNavigation.kt b/feature/editpassword/src/main/kotlin/org/mifospay/feature/editpassword/navigation/EditPasswordScreenNavigation.kt index dadaf476..e9ed6326 100644 --- a/feature/editpassword/src/main/kotlin/org/mifospay/feature/editpassword/navigation/EditPasswordScreenNavigation.kt +++ b/feature/editpassword/src/main/kotlin/org/mifospay/feature/editpassword/navigation/EditPasswordScreenNavigation.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.editpassword.navigation import androidx.navigation.NavController @@ -9,16 +18,16 @@ const val EDIT_PASSWORD_ROUTE = "edit_password_route" fun NavGraphBuilder.editPasswordScreen( onBackPress: () -> Unit, - onCancelChanges: () -> Unit + onCancelChanges: () -> Unit, ) { composable(route = EDIT_PASSWORD_ROUTE) { EditPasswordScreen( onBackPress = onBackPress, - onCancelChanges = onCancelChanges + onCancelChanges = onCancelChanges, ) } } fun NavController.navigateToEditPassword() { this.navigate(EDIT_PASSWORD_ROUTE) -} \ No newline at end of file +} diff --git a/feature/editpassword/src/main/res/values/strings.xml b/feature/editpassword/src/main/res/values/strings.xml index 26b266ea..c1625d1f 100644 --- a/feature/editpassword/src/main/res/values/strings.xml +++ b/feature/editpassword/src/main/res/values/strings.xml @@ -1,4 +1,13 @@ + Password changed successfull Change Password diff --git a/feature/editpassword/src/test/java/org/mifospay/feature/editpassword/ExampleUnitTest.kt b/feature/editpassword/src/test/java/org/mifospay/feature/editpassword/ExampleUnitTest.kt deleted file mode 100644 index d72c43f1..00000000 --- a/feature/editpassword/src/test/java/org/mifospay/feature/editpassword/ExampleUnitTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.mifospay.feature.editpassword - -import org.junit.Assert.assertEquals -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/feature/faq/build.gradle.kts b/feature/faq/build.gradle.kts index bb726e6e..b735d0ec 100644 --- a/feature/faq/build.gradle.kts +++ b/feature/faq/build.gradle.kts @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ plugins { alias(libs.plugins.mifospay.android.feature) alias(libs.plugins.mifospay.android.library.compose) diff --git a/feature/faq/src/main/AndroidManifest.xml b/feature/faq/src/main/AndroidManifest.xml index fa57d87f..dd624ab2 100644 --- a/feature/faq/src/main/AndroidManifest.xml +++ b/feature/faq/src/main/AndroidManifest.xml @@ -1,2 +1,11 @@ + \ No newline at end of file diff --git a/feature/faq/src/main/kotlin/org/mifospay/feature/faq/FAQ.kt b/feature/faq/src/main/kotlin/org/mifospay/feature/faq/FAQ.kt index 3e31300e..9ae8a88d 100644 --- a/feature/faq/src/main/kotlin/org/mifospay/feature/faq/FAQ.kt +++ b/feature/faq/src/main/kotlin/org/mifospay/feature/faq/FAQ.kt @@ -1,6 +1,15 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.faq -data class FAQ( +internal data class FAQ( var question: Int, var answer: Int? = null, ) diff --git a/feature/faq/src/main/kotlin/org/mifospay/feature/faq/FAQViewModel.kt b/feature/faq/src/main/kotlin/org/mifospay/feature/faq/FAQViewModel.kt index 0ea1db76..70c74979 100644 --- a/feature/faq/src/main/kotlin/org/mifospay/feature/faq/FAQViewModel.kt +++ b/feature/faq/src/main/kotlin/org/mifospay/feature/faq/FAQViewModel.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.faq import androidx.lifecycle.ViewModel @@ -5,7 +14,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @HiltViewModel -class FAQViewModel @Inject constructor() : ViewModel() { +internal class FAQViewModel @Inject constructor() : ViewModel() { /** * Retrieves a list of Frequently Asked Questions (FAQs). @@ -22,7 +31,7 @@ class FAQViewModel @Inject constructor() : ViewModel() { FAQ(R.string.feature_faq_question1, R.string.feature_faq_answer1), FAQ(R.string.feature_faq_question2, R.string.feature_faq_answer2), FAQ(R.string.feature_faq_question3, R.string.feature_faq_answer3), - FAQ(R.string.feature_faq_question4, R.string.feature_faq_answer4) + FAQ(R.string.feature_faq_question4, R.string.feature_faq_answer4), ) } -} \ No newline at end of file +} diff --git a/feature/faq/src/main/kotlin/org/mifospay/feature/faq/FaqScreen.kt b/feature/faq/src/main/kotlin/org/mifospay/feature/faq/FaqScreen.kt index 73196577..47675f9b 100644 --- a/feature/faq/src/main/kotlin/org/mifospay/feature/faq/FaqScreen.kt +++ b/feature/faq/src/main/kotlin/org/mifospay/feature/faq/FaqScreen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.faq import androidx.compose.foundation.layout.Column @@ -14,31 +23,41 @@ import org.mifospay.core.designsystem.component.MifosTopBar import org.mifospay.core.ui.FaqItemScreen @Composable -fun FaqScreenRoute( +internal fun FaqScreenRoute( navigateBack: () -> Unit, - faqViewModel: FAQViewModel = hiltViewModel() + modifier: Modifier = Modifier, + faqViewModel: FAQViewModel = hiltViewModel(), ) { - FaqScreen(navigateBack = { navigateBack.invoke() }, faqViewModel.getFAQ()) + FaqScreen( + modifier = modifier, + navigateBack = navigateBack, + faqList = faqViewModel.getFAQ(), + ) } @Composable -fun FaqScreen( +private fun FaqScreen( navigateBack: () -> Unit, - faqList: List + faqList: List, + modifier: Modifier = Modifier, ) { - Column(modifier = Modifier.fillMaxSize()) { + Column( + modifier = modifier + .fillMaxSize(), + ) { MifosTopBar( topBarTitle = R.string.feature_faq_frequently_asked_questions, - backPress = { navigateBack.invoke() }) + backPress = { navigateBack.invoke() }, + ) LazyColumn( modifier = Modifier .weight(1f) - .fillMaxWidth() + .fillMaxWidth(), ) { itemsIndexed(items = faqList) { _, faqItem -> FaqItemScreen( question = stringResource(id = faqItem.question), - answer = faqItem.answer?.let { stringResource(id = it) } + answer = faqItem.answer?.let { stringResource(id = it) }, ) } } @@ -47,13 +66,14 @@ fun FaqScreen( @Preview(showSystemUi = true) @Composable -fun FaqScreenPreview() { +private fun FaqScreenPreview() { FaqScreen( - {}, listOf( + {}, + listOf( FAQ(R.string.feature_faq_question1, R.string.feature_faq_answer1), FAQ(R.string.feature_faq_question2, R.string.feature_faq_answer2), FAQ(R.string.feature_faq_question3, R.string.feature_faq_answer3), - FAQ(R.string.feature_faq_question4, R.string.feature_faq_answer4) - ) + FAQ(R.string.feature_faq_question4, R.string.feature_faq_answer4), + ), ) } diff --git a/feature/faq/src/main/kotlin/org/mifospay/feature/faq/navigation/FAQNavigation.kt b/feature/faq/src/main/kotlin/org/mifospay/feature/faq/navigation/FAQNavigation.kt index 1f72c58e..d395c205 100644 --- a/feature/faq/src/main/kotlin/org/mifospay/feature/faq/navigation/FAQNavigation.kt +++ b/feature/faq/src/main/kotlin/org/mifospay/feature/faq/navigation/FAQNavigation.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.faq.navigation import androidx.navigation.NavController @@ -13,11 +22,11 @@ fun NavController.navigateToFAQ(navOptions: NavOptions? = null) { } fun NavGraphBuilder.faqScreen( - navigateBack: () -> Unit + navigateBack: () -> Unit, ) { composable(route = FAQ_ROUTE) { FaqScreenRoute( - navigateBack = navigateBack + navigateBack = navigateBack, ) } -} \ No newline at end of file +} diff --git a/feature/faq/src/main/res/values/strings.xml b/feature/faq/src/main/res/values/strings.xml index 167c5f14..08fc8c19 100644 --- a/feature/faq/src/main/res/values/strings.xml +++ b/feature/faq/src/main/res/values/strings.xml @@ -1,4 +1,13 @@ + How can I add an Account? How can I access my Payment History? diff --git a/feature/faq/src/test/java/com/example/faq/ExampleUnitTest.kt b/feature/faq/src/test/java/com/example/faq/ExampleUnitTest.kt deleted file mode 100644 index 83b1cbcf..00000000 --- a/feature/faq/src/test/java/com/example/faq/ExampleUnitTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.example.faq - -import org.junit.Assert.assertEquals -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/feature/finance/build.gradle.kts b/feature/finance/build.gradle.kts index daf0dcb0..f1480484 100644 --- a/feature/finance/build.gradle.kts +++ b/feature/finance/build.gradle.kts @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ plugins { alias(libs.plugins.mifospay.android.feature) alias(libs.plugins.mifospay.android.library.compose) @@ -8,10 +17,5 @@ android { } dependencies { - implementation(projects.core.data) - implementation(projects.feature.kyc) - implementation(projects.feature.savedcards) - implementation(projects.feature.accounts) - implementation(projects.feature.merchants) implementation(libs.accompanist.pager) } \ No newline at end of file diff --git a/feature/finance/src/androidTest/java/org/mifospay/feature/finance/ExampleInstrumentedTest.kt b/feature/finance/src/androidTest/java/org/mifospay/feature/finance/ExampleInstrumentedTest.kt deleted file mode 100644 index e8a9b547..00000000 --- a/feature/finance/src/androidTest/java/org/mifospay/feature/finance/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.mifospay.feature.finance - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("org.mifospay.feature.finance.test", appContext.packageName) - } -} \ No newline at end of file diff --git a/feature/finance/src/main/AndroidManifest.xml b/feature/finance/src/main/AndroidManifest.xml index a5918e68..96138981 100644 --- a/feature/finance/src/main/AndroidManifest.xml +++ b/feature/finance/src/main/AndroidManifest.xml @@ -1,4 +1,13 @@ + \ No newline at end of file diff --git a/feature/finance/src/main/kotlin/org/mifospay/feature/finance/FinanceScreen.kt b/feature/finance/src/main/kotlin/org/mifospay/feature/finance/FinanceScreen.kt index 4109afb9..a011bea9 100644 --- a/feature/finance/src/main/kotlin/org/mifospay/feature/finance/FinanceScreen.kt +++ b/feature/finance/src/main/kotlin/org/mifospay/feature/finance/FinanceScreen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.finance import androidx.compose.foundation.layout.Column @@ -6,50 +15,21 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import com.google.accompanist.pager.rememberPagerState -import com.mifospay.core.model.domain.BankAccountDetails import org.mifospay.core.ui.MifosScrollableTabRow import org.mifospay.core.ui.utility.TabContent -import org.mifospay.feature.bank.accounts.AccountsScreen -import org.mifospay.feature.kyc.KYCScreen -import org.mifospay.feature.merchants.ui.MerchantScreen -import org.mifospay.feature.savedcards.CardsScreen -@Suppress("UnusedParameter") @Composable -fun FinanceRoute( - onAddBtn: () -> Unit, - onLevel1Clicked: () -> Unit, - onLevel2Clicked: () -> Unit, - onLevel3Clicked: () -> Unit, - navigateToBankAccountDetailScreen: (BankAccountDetails, Int) -> Unit, - navigateToLinkBankAccountScreen: () -> Unit +internal fun FinanceRoute( + tabContents: List, + modifier: Modifier = Modifier, ) { val pagerState = rememberPagerState(initialPage = 0) - val tabContents = listOf( - TabContent(FinanceScreenContents.ACCOUNTS.name) { - AccountsScreen( - navigateToBankAccountDetailScreen = navigateToBankAccountDetailScreen, - navigateToLinkBankAccountScreen = navigateToLinkBankAccountScreen - ) - }, - TabContent(FinanceScreenContents.CARDS.name) { - CardsScreen(onEditCard = {}) - }, - TabContent(FinanceScreenContents.MERCHANTS.name) { - MerchantScreen() - }, - TabContent(FinanceScreenContents.KYC.name) { - KYCScreen( - onLevel1Clicked = onLevel1Clicked, - onLevel2Clicked = onLevel2Clicked, - onLevel3Clicked = onLevel3Clicked - ) - } - ) - - Column(modifier = Modifier.fillMaxSize()) { - MifosScrollableTabRow(tabContents = tabContents, pagerState = pagerState) + Column(modifier = modifier.fillMaxSize()) { + MifosScrollableTabRow( + tabContents = tabContents, + pagerState = pagerState, + ) } } @@ -57,18 +37,13 @@ enum class FinanceScreenContents { ACCOUNTS, CARDS, MERCHANTS, - KYC + KYC, } @Preview(showBackground = true) @Composable private fun FinanceScreenPreview() { FinanceRoute( - onAddBtn = {}, - onLevel1Clicked = {}, - onLevel2Clicked = {}, - onLevel3Clicked = {}, - navigateToBankAccountDetailScreen = { _, _ -> }, - navigateToLinkBankAccountScreen = {} + tabContents = emptyList(), ) -} \ No newline at end of file +} diff --git a/feature/finance/src/main/kotlin/org/mifospay/feature/finance/navigation/FinanceNavigation.kt b/feature/finance/src/main/kotlin/org/mifospay/feature/finance/navigation/FinanceNavigation.kt index 112e4aa7..b59d4f91 100644 --- a/feature/finance/src/main/kotlin/org/mifospay/feature/finance/navigation/FinanceNavigation.kt +++ b/feature/finance/src/main/kotlin/org/mifospay/feature/finance/navigation/FinanceNavigation.kt @@ -1,10 +1,19 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.finance.navigation import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable -import com.mifospay.core.model.domain.BankAccountDetails +import org.mifospay.core.ui.utility.TabContent import org.mifospay.feature.finance.FinanceRoute const val FINANCE_ROUTE = "finance_route" @@ -12,21 +21,9 @@ const val FINANCE_ROUTE = "finance_route" fun NavController.navigateToFinance(navOptions: NavOptions) = navigate(FINANCE_ROUTE, navOptions) fun NavGraphBuilder.financeScreen( - onAddBtn: () -> Unit, - onLevel1Clicked: () -> Unit, - onLevel2Clicked: () -> Unit, - onLevel3Clicked: () -> Unit, - navigateToBankAccountDetailScreen: (BankAccountDetails,Int) -> Unit, - navigateToLinkBankAccountScreen: () -> Unit + tabContents: List, ) { composable(route = FINANCE_ROUTE) { - FinanceRoute( - onAddBtn = onAddBtn, - onLevel1Clicked = onLevel1Clicked, - onLevel2Clicked = onLevel2Clicked, - onLevel3Clicked = onLevel3Clicked, - navigateToBankAccountDetailScreen = navigateToBankAccountDetailScreen, - navigateToLinkBankAccountScreen = navigateToLinkBankAccountScreen - ) + FinanceRoute(tabContents = tabContents) } } diff --git a/feature/finance/src/test/java/org/mifospay/feature/finance/ExampleUnitTest.kt b/feature/finance/src/test/java/org/mifospay/feature/finance/ExampleUnitTest.kt deleted file mode 100644 index 756a91d3..00000000 --- a/feature/finance/src/test/java/org/mifospay/feature/finance/ExampleUnitTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.mifospay.feature.finance - -import org.junit.Assert.assertEquals -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/feature/history/build.gradle.kts b/feature/history/build.gradle.kts index ce335ae1..6cae0e1c 100644 --- a/feature/history/build.gradle.kts +++ b/feature/history/build.gradle.kts @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ plugins { alias(libs.plugins.mifospay.android.feature) alias(libs.plugins.mifospay.android.library.compose) @@ -8,6 +17,5 @@ android { } dependencies { - implementation(projects.feature.receipt) implementation(projects.core.data) } \ No newline at end of file diff --git a/feature/history/src/androidTest/java/org/mifospay/history/ExampleInstrumentedTest.kt b/feature/history/src/androidTest/java/org/mifospay/history/ExampleInstrumentedTest.kt deleted file mode 100644 index f6cc6cf6..00000000 --- a/feature/history/src/androidTest/java/org/mifospay/history/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.mifospay.history - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("org.mifospay.history.test", appContext.packageName) - } -} \ No newline at end of file diff --git a/feature/history/src/main/AndroidManifest.xml b/feature/history/src/main/AndroidManifest.xml index a5918e68..96138981 100644 --- a/feature/history/src/main/AndroidManifest.xml +++ b/feature/history/src/main/AndroidManifest.xml @@ -1,4 +1,13 @@ + \ No newline at end of file diff --git a/feature/history/src/main/kotlin/org/mifospay/feature/HistoryContract.kt b/feature/history/src/main/kotlin/org/mifospay/feature/HistoryContract.kt deleted file mode 100644 index 930aec0f..00000000 --- a/feature/history/src/main/kotlin/org/mifospay/feature/HistoryContract.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.mifospay.feature - -import com.mifospay.core.model.domain.Transaction - -/** - * Created by naman on 17/8/17. - */ -interface HistoryContract { - interface TransactionsHistoryAsync { - fun onTransactionsFetchCompleted(transactions: List?) - } -} \ No newline at end of file diff --git a/feature/history/src/main/kotlin/org/mifospay/feature/history/HistoryScreen.kt b/feature/history/src/main/kotlin/org/mifospay/feature/history/HistoryScreen.kt index 252d0793..c00a183a 100644 --- a/feature/history/src/main/kotlin/org/mifospay/feature/history/HistoryScreen.kt +++ b/feature/history/src/main/kotlin/org/mifospay/feature/history/HistoryScreen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.history import android.widget.Toast @@ -46,9 +55,10 @@ import org.mifospay.feature.transaction.detail.TransactionDetailScreen @Composable fun HistoryScreen( - viewModel: HistoryViewModel = hiltViewModel(), viewReceipt: (String) -> Unit, accountClicked: (String, ArrayList) -> Unit, + modifier: Modifier = Modifier, + viewModel: HistoryViewModel = hiltViewModel(), ) { val historyUiState by viewModel.historyUiState.collectAsStateWithLifecycle() @@ -56,14 +66,16 @@ fun HistoryScreen( historyUiState = historyUiState, viewReceipt = viewReceipt, accountClicked = accountClicked, + modifier = modifier, ) } @Composable -fun HistoryScreen( +private fun HistoryScreen( historyUiState: HistoryUiState, viewReceipt: (String) -> Unit, accountClicked: (String, ArrayList) -> Unit, + modifier: Modifier = Modifier, ) { var selectedChip by remember { mutableStateOf(TransactionType.OTHER) } var filteredTransactions by remember { mutableStateOf(emptyList()) } @@ -77,7 +89,7 @@ fun HistoryScreen( title = stringResource(id = R.string.feature_history_error_oops), subTitle = stringResource(id = R.string.feature_history_empty_no_transaction_history_title), iconTint = MaterialTheme.colorScheme.primary, - iconImageVector = Icons.Rounded.Info + iconImageVector = Icons.Rounded.Info, ) } @@ -87,7 +99,7 @@ fun HistoryScreen( title = stringResource(id = R.string.feature_history_error_oops), subTitle = stringResource(id = R.string.feature_history_unexpected_error_subtitle), iconTint = MaterialTheme.colorScheme.primary, - iconImageVector = Icons.Rounded.Info + iconImageVector = Icons.Rounded.Info, ) } @@ -104,22 +116,22 @@ fun HistoryScreen( modifier = Modifier .fillMaxWidth() .padding(10.dp), - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, ) { Chip( selected = selectedChip == TransactionType.OTHER, onClick = { selectedChip = TransactionType.OTHER }, - label = stringResource(R.string.feature_history_all) + label = stringResource(R.string.feature_history_all), ) Chip( selected = selectedChip == TransactionType.CREDIT, onClick = { selectedChip = TransactionType.CREDIT }, - label = stringResource(R.string.feature_history_credits) + label = stringResource(R.string.feature_history_credits), ) Chip( selected = selectedChip == TransactionType.DEBIT, onClick = { selectedChip = TransactionType.DEBIT }, - label = stringResource(R.string.feature_history_debits) + label = stringResource(R.string.feature_history_debits), ) } Spacer(modifier = Modifier.height(24.dp)) @@ -130,72 +142,74 @@ fun HistoryScreen( modifier = Modifier .padding(start = 24.dp, end = 24.dp) .clickable { transactionDetailState = it }, - transaction = it + transaction = it, ) Spacer(modifier = Modifier.height(16.dp)) } } } } - } HistoryUiState.Loading -> { MifosLoadingWheel( modifier = Modifier.fillMaxWidth(), - contentDesc = stringResource(R.string.feature_history_loading) + contentDesc = stringResource(R.string.feature_history_loading), ) } } if (transactionDetailState != null) { MifosBottomSheet( + modifier = modifier, content = { TransactionDetailScreen( transaction = transactionDetailState!!, viewReceipt = { transactionDetailState?.transactionId?.let { viewReceipt(it) } }, - accountClicked = { accountClicked(it, ArrayList(transactionsList)) } + accountClicked = { accountClicked(it, ArrayList(transactionsList)) }, ) }, - onDismiss = { transactionDetailState = null } + onDismiss = { transactionDetailState = null }, ) } } @Composable -fun Chip( +private fun Chip( selected: Boolean, onClick: () -> Unit, - label: String + label: String, + modifier: Modifier = Modifier, ) { val context = LocalContext.current val backgroundColor = if (selected) MaterialTheme.colorScheme.primary else lightGrey Button( + modifier = modifier, onClick = { onClick() Toast.makeText(context, label, Toast.LENGTH_SHORT).show() }, - colors = ButtonDefaults.buttonColors(backgroundColor) + colors = ButtonDefaults.buttonColors(backgroundColor), ) { Text( modifier = Modifier.padding(top = 4.dp, bottom = 4.dp, start = 16.dp, end = 16.dp), text = label, - color = if (selected) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurface + color = if (selected) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurface, ) } } -class HistoryPreviewProvider : PreviewParameterProvider { +internal class HistoryPreviewProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( HistoryUiState.Empty, HistoryUiState.Loading, HistoryUiState.Error("Error Screen"), - HistoryUiState.HistoryList(sampleHistoryList) + HistoryUiState.HistoryList(sampleHistoryList), ) } -val sampleHistoryList = List(10) { index -> +internal val sampleHistoryList = List(10) { index -> Transaction( transactionId = "txn_123456789", clientId = 1001L, @@ -206,14 +220,14 @@ val sampleHistoryList = List(10) { index -> transactionType = TransactionType.CREDIT, transferId = 3003L, transferDetail = TransferDetail(), - receiptId = "receipt_123456789" + receiptId = "receipt_123456789", ) } @Preview(showBackground = true) @Composable -fun HistoryScreenPreview( - @PreviewParameter(HistoryPreviewProvider::class) historyUiState: HistoryUiState +private fun HistoryScreenPreview( + @PreviewParameter(HistoryPreviewProvider::class) historyUiState: HistoryUiState, ) { HistoryScreen(historyUiState = historyUiState, viewReceipt = {}, accountClicked = { _, _ -> }) -} \ No newline at end of file +} diff --git a/feature/history/src/main/kotlin/org/mifospay/feature/history/HistoryViewModel.kt b/feature/history/src/main/kotlin/org/mifospay/feature/history/HistoryViewModel.kt index fc924964..1bbb8fad 100644 --- a/feature/history/src/main/kotlin/org/mifospay/feature/history/HistoryViewModel.kt +++ b/feature/history/src/main/kotlin/org/mifospay/feature/history/HistoryViewModel.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.history import androidx.lifecycle.ViewModel @@ -17,7 +26,7 @@ class HistoryViewModel @Inject constructor( private val mUseCaseHandler: UseCaseHandler, private val mLocalRepository: LocalRepository, private val mFetchAccountUseCase: FetchAccount, - private val fetchAccountTransactionsUseCase: FetchAccountTransactions + private val fetchAccountTransactionsUseCase: FetchAccountTransactions, ) : ViewModel() { private val _historyUiState = MutableStateFlow(HistoryUiState.Loading) @@ -25,7 +34,8 @@ class HistoryViewModel @Inject constructor( private fun fetchTransactions() { _historyUiState.value = HistoryUiState.Loading - mUseCaseHandler.execute(mFetchAccountUseCase, + mUseCaseHandler.execute( + mFetchAccountUseCase, FetchAccount.RequestValues(mLocalRepository.clientDetails.clientId), object : UseCase.UseCaseCallback { override fun onSuccess(response: FetchAccount.ResponseValue) { @@ -37,23 +47,28 @@ class HistoryViewModel @Inject constructor( override fun onError(message: String) { _historyUiState.value = HistoryUiState.Error(message) } - }) + }, + ) } fun fetchTransactionsHistory(accountId: Long) { - mUseCaseHandler.execute(fetchAccountTransactionsUseCase, + mUseCaseHandler.execute( + fetchAccountTransactionsUseCase, FetchAccountTransactions.RequestValues(accountId), object : UseCase.UseCaseCallback { override fun onSuccess(response: FetchAccountTransactions.ResponseValue?) { - if (response?.transactions?.isNotEmpty() == true) + if (response?.transactions?.isNotEmpty() == true) { _historyUiState.value = HistoryUiState.HistoryList(response.transactions) - else _historyUiState.value = HistoryUiState.Empty + } else { + _historyUiState.value = HistoryUiState.Empty + } } override fun onError(message: String) { _historyUiState.value = HistoryUiState.Error(message) } - }) + }, + ) } init { @@ -66,4 +81,4 @@ sealed class HistoryUiState { data object Empty : HistoryUiState() data class Error(val message: String) : HistoryUiState() data class HistoryList(val list: List) : HistoryUiState() -} \ No newline at end of file +} diff --git a/feature/history/src/main/kotlin/org/mifospay/feature/specific/transactions/SpecificTransactionsScreen.kt b/feature/history/src/main/kotlin/org/mifospay/feature/specific/transactions/SpecificTransactionsScreen.kt index 18c0adcd..d3ebec01 100644 --- a/feature/history/src/main/kotlin/org/mifospay/feature/specific/transactions/SpecificTransactionsScreen.kt +++ b/feature/history/src/main/kotlin/org/mifospay/feature/specific/transactions/SpecificTransactionsScreen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.specific.transactions import androidx.compose.foundation.clickable @@ -12,7 +21,7 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Info -import androidx.compose.material3.Divider +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface @@ -32,6 +41,7 @@ import com.mifospay.core.model.domain.Transaction import com.mifospay.core.model.domain.TransactionType import com.mifospay.core.model.domain.client.Client import com.mifospay.core.model.entity.accounts.savings.SavingAccount +import org.mifospay.common.Utils.toArrayList import org.mifospay.core.designsystem.component.MfLoadingWheel import org.mifospay.core.designsystem.component.MifosScaffold import org.mifospay.core.designsystem.icon.MifosIcons @@ -43,36 +53,39 @@ import org.mifospay.core.ui.EmptyContentScreen import org.mifospay.core.ui.ErrorScreenContent import org.mifospay.feature.history.R - @Composable -fun SpecificTransactionsScreen( +internal fun SpecificTransactionsScreen( accountNumber: String, - transactions: ArrayList, - viewModel: SpecificTransactionsViewModel = hiltViewModel(), + transactions: List, backPress: () -> Unit, - transactionItemClicked: (String) -> Unit + transactionItemClicked: (String) -> Unit, + modifier: Modifier = Modifier, + viewModel: SpecificTransactionsViewModel = hiltViewModel(), ) { val uiState = viewModel.uiState.collectAsStateWithLifecycle() LaunchedEffect(key1 = Unit) { - viewModel.setArguments(transactions, accountNumber) + viewModel.setArguments(transactions.toArrayList(), accountNumber) viewModel.getSpecificTransactions() } SpecificTransactionsScreen( uiState = uiState.value, backPress = backPress, - transactionItemClicked = transactionItemClicked + transactionItemClicked = transactionItemClicked, + modifier = modifier, ) } @Composable -fun SpecificTransactionsScreen( +private fun SpecificTransactionsScreen( uiState: SpecificTransactionsUiState, backPress: () -> Unit, transactionItemClicked: (String) -> Unit, + modifier: Modifier = Modifier, ) { MifosScaffold( + modifier = modifier, topBarTitle = R.string.feature_history_specific_transactions_history, backPress = backPress, scaffoldContent = { paddingValues -> @@ -89,7 +102,7 @@ fun SpecificTransactionsScreen( SpecificTransactionsUiState.Loading -> { MfLoadingWheel( contentDesc = stringResource(R.string.feature_history_loading), - backgroundColor = MaterialTheme.colorScheme.surface + backgroundColor = MaterialTheme.colorScheme.surface, ) } @@ -100,39 +113,42 @@ fun SpecificTransactionsScreen( title = stringResource(id = R.string.feature_history_error_oops), subTitle = stringResource(id = R.string.feature_history_no_transactions_found), iconTint = MaterialTheme.colorScheme.onSurface, - iconImageVector = Icons.Rounded.Info + iconImageVector = Icons.Rounded.Info, ) } else { SpecificTransactionsContent( transactionList = uiState.transactionsList, - transactionItemClicked = transactionItemClicked + transactionItemClicked = transactionItemClicked, ) } } } } - } + }, ) } @Composable -fun SpecificTransactionsContent( - transactionList: ArrayList, - transactionItemClicked: (String) -> Unit +private fun SpecificTransactionsContent( + transactionList: List, + transactionItemClicked: (String) -> Unit, + modifier: Modifier = Modifier, ) { - LazyColumn { - itemsIndexed(items = transactionList) { index, transaction -> + LazyColumn(modifier = modifier) { + itemsIndexed( + items = transactionList, + ) { index, transaction -> SpecificTransactionItem( transaction = transaction, modifier = Modifier .padding(12.dp) .clickable { transaction.transactionId?.let { transactionItemClicked(it) } - } + }, ) Spacer(modifier = Modifier.height(4.dp)) if (index != transactionList.lastIndex) { - Divider() + HorizontalDivider() } Spacer(modifier = Modifier.height(4.dp)) } @@ -142,43 +158,43 @@ fun SpecificTransactionsContent( @Composable fun SpecificTransactionItem( transaction: Transaction, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { Column(modifier = modifier.padding(horizontal = 12.dp)) { Row(verticalAlignment = Alignment.CenterVertically) { SpecificTransactionAccountInfo( - modifier = Modifier.weight(1f), account = transaction.transferDetail.fromAccount, - client = transaction.transferDetail.fromClient + client = transaction.transferDetail.fromClient, + modifier = Modifier.weight(1f), ) Icon(imageVector = MifosIcons.SendRightTilted, contentDescription = null) SpecificTransactionAccountInfo( - modifier = Modifier.weight(1f), account = transaction.transferDetail.toAccount, - client = transaction.transferDetail.toClient + client = transaction.transferDetail.toClient, + modifier = Modifier.weight(1f), ) } Spacer(modifier = Modifier.height(12.dp)) Row( modifier = Modifier.padding(horizontal = 8.dp), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Column { Text( - text = stringResource(id = R.string.feature_receipt_transaction_id) + transaction.transactionId, + text = stringResource(id = R.string.feature_history_transaction_id) + transaction.transactionId, style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.primary + color = MaterialTheme.colorScheme.primary, ) Text( - text = stringResource(id = R.string.feature_receipt_transaction_date) + transaction.date, + text = stringResource(id = R.string.feature_history_transaction_date) + transaction.date, style = MaterialTheme.typography.bodyLarge, ) Text( text = when (transaction.transactionType) { TransactionType.DEBIT -> stringResource(id = R.string.feature_history_debits) TransactionType.CREDIT -> stringResource(id = R.string.feature_history_credits) - TransactionType.OTHER -> stringResource(id = R.string.feature_receipt_other) + TransactionType.OTHER -> stringResource(id = R.string.feature_history_other) }, style = MaterialTheme.typography.bodyLarge, ) @@ -198,11 +214,11 @@ fun SpecificTransactionItem( } @Composable -fun SpecificTransactionAccountInfo( - modifier: Modifier = Modifier, +internal fun SpecificTransactionAccountInfo( account: SavingAccount, client: Client, - accountClicked: (String) -> Unit = {} + modifier: Modifier = Modifier, + accountClicked: (String) -> Unit = {}, ) { Column( modifier = modifier.clickable { @@ -222,7 +238,7 @@ fun SpecificTransactionAccountInfo( } } -class SpecificTransactionsUiStateProvider : +internal class SpecificTransactionsUiStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( @@ -235,9 +251,9 @@ class SpecificTransactionsUiStateProvider : @Preview(showSystemUi = true) @Composable -fun ShowQrScreenPreview( +private fun ShowQrScreenPreview( @PreviewParameter(SpecificTransactionsUiStateProvider::class) - uiState: SpecificTransactionsUiState + uiState: SpecificTransactionsUiState, ) { MifosTheme { SpecificTransactionsScreen( @@ -250,8 +266,8 @@ fun ShowQrScreenPreview( @Preview @Composable -fun SpecificTransactionItemPreview() { +private fun SpecificTransactionItemPreview() { Surface { SpecificTransactionItem(transaction = Transaction()) } -} \ No newline at end of file +} diff --git a/feature/history/src/main/kotlin/org/mifospay/feature/specific/transactions/SpecificTransactionsViewModel.kt b/feature/history/src/main/kotlin/org/mifospay/feature/specific/transactions/SpecificTransactionsViewModel.kt index 35ef62b5..c1bf93fa 100644 --- a/feature/history/src/main/kotlin/org/mifospay/feature/specific/transactions/SpecificTransactionsViewModel.kt +++ b/feature/history/src/main/kotlin/org/mifospay/feature/specific/transactions/SpecificTransactionsViewModel.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.specific.transactions import androidx.lifecycle.ViewModel @@ -14,7 +23,7 @@ import javax.inject.Inject @HiltViewModel class SpecificTransactionsViewModel @Inject constructor( private val mUseCaseFactory: UseCaseFactory, - private var mTaskLooper: TaskLooper? = null + private var mTaskLooper: TaskLooper? = null, ) : ViewModel() { private val _uiState: MutableStateFlow = @@ -39,16 +48,18 @@ class SpecificTransactionsViewModel @Inject constructor( val transferId = transaction.transferId mTaskLooper?.addTask( useCase = mUseCaseFactory.getUseCase(Constants.FETCH_ACCOUNT_TRANSFER_USECASE) - as UseCase, + as UseCase, values = FetchAccountTransfer.RequestValues(transferId), taskData = TaskLooper.TaskData( - org.mifospay.common.Constants.TRANSFER_DETAILS, i - ) + org.mifospay.common.Constants.TRANSFER_DETAILS, + i, + ), ) } mTaskLooper!!.listen(object : TaskLooper.Listener { override fun onTaskSuccess( - taskData: TaskLooper.TaskData, response: R + taskData: TaskLooper.TaskData, + response: R, ) { when (taskData.taskName) { org.mifospay.common.Constants.TRANSFER_DETAILS -> { @@ -89,4 +100,4 @@ sealed class SpecificTransactionsUiState { data object Loading : SpecificTransactionsUiState() data object Error : SpecificTransactionsUiState() data class Success(val transactionsList: ArrayList) : SpecificTransactionsUiState() -} \ No newline at end of file +} diff --git a/feature/history/src/main/kotlin/org/mifospay/feature/specific/transactions/navigation/SpecificTransactionsNavigation.kt b/feature/history/src/main/kotlin/org/mifospay/feature/specific/transactions/navigation/SpecificTransactionsNavigation.kt index 50918be5..5abdc8e9 100644 --- a/feature/history/src/main/kotlin/org/mifospay/feature/specific/transactions/navigation/SpecificTransactionsNavigation.kt +++ b/feature/history/src/main/kotlin/org/mifospay/feature/specific/transactions/navigation/SpecificTransactionsNavigation.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ @file:Suppress("MaxLineLength") package org.mifospay.feature.specific.transactions.navigation @@ -15,27 +24,32 @@ const val SPECIFIC_TRANSACTIONS_ROUTE = "specific_transactions_route" fun NavGraphBuilder.specificTransactionsScreen( onBackClick: () -> Unit, - onTransactionItemClicked: (String) -> Unit + onTransactionItemClicked: (String) -> Unit, ) { composable( route = "$SPECIFIC_TRANSACTIONS_ROUTE?${Constants.ACCOUNT_NUMBER}={accountNumber}&${Constants.TRANSACTIONS}={transactions}", arguments = listOf( navArgument(Constants.ACCOUNT_NUMBER) { type = NavType.StringType }, - navArgument(Constants.TRANSACTIONS) { type = NavType.StringType } - ) + navArgument(Constants.TRANSACTIONS) { type = NavType.StringType }, + ), ) { backStackEntry -> val accountNumber = backStackEntry.arguments?.getString(Constants.ACCOUNT_NUMBER) ?: "" - val transactions = backStackEntry.arguments?.getParcelableArrayList(Constants.TRANSACTIONS) ?: arrayListOf() + val transactions = + backStackEntry.arguments?.getParcelableArrayList(Constants.TRANSACTIONS) + ?: arrayListOf() SpecificTransactionsScreen( accountNumber = accountNumber, - transactions = transactions, + transactions = transactions.toList(), backPress = onBackClick, - transactionItemClicked = onTransactionItemClicked + transactionItemClicked = onTransactionItemClicked, ) } } -fun NavController.navigateToSpecificTransactions(accountNumber: String, transactions: ArrayList) { +fun NavController.navigateToSpecificTransactions( + accountNumber: String, + transactions: ArrayList, +) { this.navigate("$SPECIFIC_TRANSACTIONS_ROUTE?${Constants.ACCOUNT_NUMBER}=$accountNumber&${Constants.TRANSACTIONS}=$transactions") } diff --git a/feature/history/src/main/kotlin/org/mifospay/feature/transaction/detail/TransactionDetailScreen.kt b/feature/history/src/main/kotlin/org/mifospay/feature/transaction/detail/TransactionDetailScreen.kt index b941c5fc..ffb59961 100644 --- a/feature/history/src/main/kotlin/org/mifospay/feature/transaction/detail/TransactionDetailScreen.kt +++ b/feature/history/src/main/kotlin/org/mifospay/feature/transaction/detail/TransactionDetailScreen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.transaction.detail import androidx.compose.foundation.Image @@ -36,15 +45,14 @@ import org.mifospay.core.ui.ErrorScreenContent import org.mifospay.feature.history.R import org.mifospay.feature.specific.transactions.SpecificTransactionAccountInfo - @Composable -fun TransactionDetailScreen( - viewModel: TransactionDetailViewModel = hiltViewModel(), +internal fun TransactionDetailScreen( transaction: Transaction, viewReceipt: () -> Unit, accountClicked: (String) -> Unit, + modifier: Modifier = Modifier, + viewModel: TransactionDetailViewModel = hiltViewModel(), ) { - val uiState = viewModel.transactionDetailUiState.collectAsStateWithLifecycle() LaunchedEffect(key1 = transaction) { @@ -55,21 +63,23 @@ fun TransactionDetailScreen( uiState = uiState.value, transaction = transaction, viewReceipt = viewReceipt, - accountClicked = accountClicked + accountClicked = accountClicked, + modifier = modifier, ) } @Composable -fun TransactionDetailScreen( +private fun TransactionDetailScreen( uiState: TransactionDetailUiState, transaction: Transaction, viewReceipt: () -> Unit, accountClicked: (String) -> Unit, + modifier: Modifier = Modifier, ) { Box( - modifier = Modifier + modifier = modifier .padding(20.dp) - .height(300.dp) + .height(300.dp), ) { when (uiState) { is TransactionDetailUiState.Error -> { @@ -78,7 +88,7 @@ fun TransactionDetailScreen( is TransactionDetailUiState.Loading -> { MfLoadingWheel( - backgroundColor = Color.Black.copy(alpha = 0.6f) + backgroundColor = Color.Black.copy(alpha = 0.6f), ) } @@ -86,7 +96,7 @@ fun TransactionDetailScreen( TransactionsDetailContent( transaction = transaction, viewReceipt = viewReceipt, - accountClicked = accountClicked + accountClicked = accountClicked, ) } } @@ -94,36 +104,36 @@ fun TransactionDetailScreen( } @Composable -fun TransactionsDetailContent( - modifier: Modifier = Modifier, +private fun TransactionsDetailContent( transaction: Transaction, viewReceipt: () -> Unit, accountClicked: (String) -> Unit, + modifier: Modifier = Modifier, ) { Column( modifier = modifier.padding(12.dp), - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, ) { Row( modifier = Modifier .padding(horizontal = 12.dp) .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Column { Text( - text = stringResource(id = R.string.feature_receipt_transaction_id) + transaction.transactionId, + text = stringResource(id = R.string.feature_history_transaction_id) + transaction.transactionId, style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.primary + color = MaterialTheme.colorScheme.primary, ) Text( - text = stringResource(id = R.string.feature_receipt_transaction_date) + transaction.date, + text = stringResource(id = R.string.feature_history_transaction_date) + transaction.date, style = MaterialTheme.typography.bodyLarge, ) if (transaction.receiptId != null) { Text( - text = stringResource(id = R.string.feature_receipt_pan_id) + transaction.receiptId, + text = stringResource(id = R.string.feature_history_pan_id) + transaction.receiptId, style = MaterialTheme.typography.bodyLarge, ) } @@ -145,31 +155,32 @@ fun TransactionsDetailContent( modifier = Modifier.weight(1f), account = transaction.transferDetail.fromAccount, client = transaction.transferDetail.fromClient, - accountClicked = accountClicked + accountClicked = accountClicked, + ) + Image( + painter = painterResource(id = R.drawable.feature_history_ic_send), + contentDescription = null, ) - Image(painter = painterResource(id = R.drawable.feature_history_ic_send), contentDescription = null) SpecificTransactionAccountInfo( modifier = Modifier.weight(1f), account = transaction.transferDetail.toAccount, client = transaction.transferDetail.toClient, - accountClicked = accountClicked + accountClicked = accountClicked, ) } HorizontalDivider(modifier = Modifier.padding(vertical = 12.dp)) Text( - text = stringResource(id = R.string.feature_receipt_view_Receipt), + text = stringResource(id = R.string.feature_history_view_Receipt), style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.primary, textAlign = TextAlign.Center, - modifier = Modifier.clickable { viewReceipt() } + modifier = Modifier.clickable { viewReceipt() }, ) - } } - class TransactionDetailUiStateProvider : PreviewParameterProvider { override val values: Sequence @@ -182,7 +193,10 @@ class TransactionDetailUiStateProvider : @Preview(showSystemUi = true) @Composable -fun ShowQrScreenPreview(@PreviewParameter(TransactionDetailUiStateProvider::class) uiState: TransactionDetailUiState) { +private fun ShowQrScreenPreview( + @PreviewParameter(TransactionDetailUiStateProvider::class) + uiState: TransactionDetailUiState, +) { MifosTheme { TransactionDetailScreen( uiState = uiState, @@ -191,4 +205,4 @@ fun ShowQrScreenPreview(@PreviewParameter(TransactionDetailUiStateProvider::clas accountClicked = {}, ) } -} \ No newline at end of file +} diff --git a/feature/history/src/main/kotlin/org/mifospay/feature/transaction/detail/TransactionDetailViewModel.kt b/feature/history/src/main/kotlin/org/mifospay/feature/transaction/detail/TransactionDetailViewModel.kt index b5797809..5f33079a 100644 --- a/feature/history/src/main/kotlin/org/mifospay/feature/transaction/detail/TransactionDetailViewModel.kt +++ b/feature/history/src/main/kotlin/org/mifospay/feature/transaction/detail/TransactionDetailViewModel.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.transaction.detail import androidx.lifecycle.ViewModel @@ -12,7 +21,7 @@ import javax.inject.Inject @HiltViewModel class TransactionDetailViewModel @Inject constructor( private val mUseCaseHandler: UseCaseHandler, - private val mFetchAccountTransferUseCase: FetchAccountTransfer + private val mFetchAccountTransferUseCase: FetchAccountTransfer, ) : ViewModel() { private val _transactionDetailUiState: MutableStateFlow = @@ -20,7 +29,8 @@ class TransactionDetailViewModel @Inject constructor( val transactionDetailUiState get() = _transactionDetailUiState fun getTransferDetail(transferId: Long) { - mUseCaseHandler.execute(mFetchAccountTransferUseCase, + mUseCaseHandler.execute( + mFetchAccountTransferUseCase, FetchAccountTransfer.RequestValues(transferId), object : UseCase.UseCaseCallback { override fun onSuccess(response: FetchAccountTransfer.ResponseValue?) { @@ -31,7 +41,7 @@ class TransactionDetailViewModel @Inject constructor( override fun onError(message: String) { _transactionDetailUiState.value = TransactionDetailUiState.Error } - } + }, ) } } @@ -40,4 +50,4 @@ sealed class TransactionDetailUiState { data object Loading : TransactionDetailUiState() data object Error : TransactionDetailUiState() data class Success(val transferDetail: TransferDetail?) : TransactionDetailUiState() -} \ No newline at end of file +} diff --git a/feature/history/src/main/res/drawable/feature_history_ic_send.xml b/feature/history/src/main/res/drawable/feature_history_ic_send.xml index 014a0362..3d56f5f3 100644 --- a/feature/history/src/main/res/drawable/feature_history_ic_send.xml +++ b/feature/history/src/main/res/drawable/feature_history_ic_send.xml @@ -1,3 +1,13 @@ + + diff --git a/feature/history/src/main/res/values/strings.xml b/feature/history/src/main/res/values/strings.xml index 3567f670..871dd2e7 100644 --- a/feature/history/src/main/res/values/strings.xml +++ b/feature/history/src/main/res/values/strings.xml @@ -1,5 +1,19 @@ + + Transaction ID : + Transaction Date : + Other + PAN ID + View Receipt Oops! Your history is empty An unexpected error occurred. Please try again. diff --git a/feature/history/src/test/java/org/mifospay/history/ExampleUnitTest.kt b/feature/history/src/test/java/org/mifospay/history/ExampleUnitTest.kt deleted file mode 100644 index bcbd7d19..00000000 --- a/feature/history/src/test/java/org/mifospay/history/ExampleUnitTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.mifospay.history - -import org.junit.Assert.assertEquals -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/feature/home/build.gradle.kts b/feature/home/build.gradle.kts index 8a6b2856..9e50d5e5 100644 --- a/feature/home/build.gradle.kts +++ b/feature/home/build.gradle.kts @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ plugins { alias(libs.plugins.mifospay.android.feature) alias(libs.plugins.mifospay.android.library.compose) @@ -8,6 +17,5 @@ android { } dependencies { - implementation(projects.feature.history) implementation(projects.core.data) } \ No newline at end of file diff --git a/feature/home/src/androidTest/java/org/mifospay/feature/home/ExampleInstrumentedTest.kt b/feature/home/src/androidTest/java/org/mifospay/feature/home/ExampleInstrumentedTest.kt deleted file mode 100644 index 9db0ef1a..00000000 --- a/feature/home/src/androidTest/java/org/mifospay/feature/home/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.mifospay.feature.home - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("org.mifospay.feature.home.test", appContext.packageName) - } -} \ No newline at end of file diff --git a/feature/home/src/main/AndroidManifest.xml b/feature/home/src/main/AndroidManifest.xml index a5918e68..96138981 100644 --- a/feature/home/src/main/AndroidManifest.xml +++ b/feature/home/src/main/AndroidManifest.xml @@ -1,4 +1,13 @@ + \ No newline at end of file diff --git a/feature/home/src/main/kotlin/org/mifospay/feature/home/HomeScreen.kt b/feature/home/src/main/kotlin/org/mifospay/feature/home/HomeScreen.kt index 577b8661..419fd9f4 100644 --- a/feature/home/src/main/kotlin/org/mifospay/feature/home/HomeScreen.kt +++ b/feature/home/src/main/kotlin/org/mifospay/feature/home/HomeScreen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.home import androidx.compose.foundation.BorderStroke @@ -48,10 +57,11 @@ import org.mifospay.core.ui.ErrorScreenContent import org.mifospay.core.ui.TransactionItemScreen @Composable -fun HomeRoute( - homeViewModel: HomeViewModel = hiltViewModel(), +internal fun HomeRoute( onRequest: (String) -> Unit, - onPay: () -> Unit + onPay: () -> Unit, + modifier: Modifier = Modifier, + homeViewModel: HomeViewModel = hiltViewModel(), ) { val homeUIState by homeViewModel .homeUIState @@ -61,7 +71,7 @@ fun HomeRoute( is HomeUiState.Loading -> { MfLoadingWheel( contentDesc = stringResource(R.string.feature_home_loading), - backgroundColor = MaterialTheme.colorScheme.surface + backgroundColor = MaterialTheme.colorScheme.surface, ) } @@ -73,7 +83,8 @@ fun HomeRoute( onRequest = { onRequest.invoke(successState.vpa ?: "") }, - onPay = onPay + onPay = onPay, + modifier = modifier, ) } @@ -81,23 +92,23 @@ fun HomeRoute( ErrorScreenContent( onClickRetry = { homeViewModel.fetchAccountDetails() - } + }, ) } } } @Composable -fun HomeScreen( +private fun HomeScreen( account: Account?, transactions: List, onRequest: () -> Unit, - onPay: () -> Unit + onPay: () -> Unit, + modifier: Modifier = Modifier, ) { LazyColumn( - modifier = Modifier + modifier = modifier .fillMaxWidth() - .background(color = MaterialTheme.colorScheme.surface) .padding(start = 32.dp, end = 32.dp), ) { @@ -107,7 +118,7 @@ fun HomeScreen( item { PayRequestScreen( onRequest = onRequest, - onPay = onPay + onPay = onPay, ) } if (transactions.isNotEmpty()) { @@ -116,7 +127,7 @@ fun HomeScreen( modifier = Modifier.padding(top = 32.dp), text = "Recent Transactions", style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurface + color = MaterialTheme.colorScheme.onSurface, ) Spacer(modifier = Modifier.height(16.dp)) } @@ -131,19 +142,22 @@ fun HomeScreen( } @Composable -fun MifosWalletCardScreen(account: Account?) { +private fun MifosWalletCardScreen( + modifier: Modifier = Modifier, + account: Account? = null, +) { Card( - modifier = Modifier + modifier = modifier .fillMaxWidth() .height(225.dp) .padding(top = 20.dp, bottom = 32.dp), - colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.onSurface) + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.onSurface), ) { Column( modifier = Modifier .fillMaxHeight() .padding(start = 36.dp), - verticalArrangement = Arrangement.Center + verticalArrangement = Arrangement.Center, ) { val walletBalanceLabel = if (account != null) "(${account.currency.displayLabel})" else "" @@ -152,96 +166,109 @@ fun MifosWalletCardScreen(account: Account?) { style = TextStyle( fontSize = 12.sp, fontWeight = FontWeight.W400, - color = lightGrey - ) + color = lightGrey, + ), ) Spacer(modifier = Modifier.height(10.dp)) val accountBalance = - if (account != null) Utils.getFormattedAccountBalance( - account.balance, account.currency.code - ) else "0" + if (account != null) { + Utils.getFormattedAccountBalance( + account.balance, + account.currency.code, + ) + } else { + "0" + } Text( text = accountBalance, style = TextStyle( fontSize = 42.sp, fontWeight = FontWeight(600), - color = MaterialTheme.colorScheme.surface - ) + color = MaterialTheme.colorScheme.surface, + ), ) Spacer(modifier = Modifier.height(10.dp)) val currencyEqual = if (account != null) { "${account.currency.code}1 ${account.currency.displayLabel}" - } else "" + } else { + "" + } Text( text = currencyEqual, style = TextStyle( fontSize = 12.sp, fontWeight = FontWeight(500), - color = lightGrey - ) + color = lightGrey, + ), ) } } } @Composable -fun PayRequestScreen( +private fun PayRequestScreen( onRequest: () -> Unit, - onPay: () -> Unit + onPay: () -> Unit, + modifier: Modifier = Modifier, ) { Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween + modifier = modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, ) { PayCard( - modifier = Modifier.weight(1f), title = "Request", - icon = R.drawable.core_ui_money_in - ) { - onRequest.invoke() - } + icon = R.drawable.core_ui_money_in, + { + onRequest.invoke() + }, + modifier = Modifier.weight(1f), + ) Spacer(modifier = Modifier.width(16.dp)) PayCard( - modifier = Modifier.weight(1f), title = "Pay", - icon = R.drawable.core_ui_money_out - ) { - onPay.invoke() - } + icon = R.drawable.core_ui_money_out, + { + onPay.invoke() + }, + modifier = Modifier.weight(1f), + ) } } @Composable -fun PayCard( - modifier: Modifier, +private fun PayCard( title: String, icon: Int, - onClickCard: () -> Unit + onClickCard: () -> Unit, + modifier: Modifier = Modifier, ) { Card( modifier = modifier .height(144.dp) .clickable { onClickCard.invoke() }, border = BorderStroke(1.dp, border), - colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface) + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface), ) { Column( modifier = Modifier .fillMaxHeight() .padding(top = 20.dp, bottom = 20.dp, start = 20.dp), - verticalArrangement = Arrangement.SpaceBetween + verticalArrangement = Arrangement.SpaceBetween, ) { Box( modifier = Modifier .size(40.dp) - .background(MaterialTheme.colorScheme.onSurface, shape = RoundedCornerShape(4.dp)), - contentAlignment = Alignment.Center + .background( + MaterialTheme.colorScheme.onSurface, + shape = RoundedCornerShape(4.dp), + ), + contentAlignment = Alignment.Center, ) { Image( modifier = Modifier.size(20.dp), painter = painterResource(id = icon), contentDescription = null, - colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.surface) + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.surface), ) } Text(text = title) @@ -251,7 +278,7 @@ fun PayCard( @Preview(showSystemUi = true, device = "id:pixel_5") @Composable -fun HomeScreenPreview() { +private fun HomeScreenPreview() { HomeScreen( account = Account( image = "", @@ -262,9 +289,9 @@ fun HomeScreenPreview() { currency = Currency( code = "USD", displayLabel = "$", - displaySymbol = "$" + displaySymbol = "$", ), - productId = 1223 + productId = 1223, ), transactions = List(25) { index -> Transaction( @@ -273,24 +300,29 @@ fun HomeScreenPreview() { currency = Currency( code = "USD", displayLabel = "$", - displaySymbol = "$" + displaySymbol = "$", ), - transactionType = TransactionType.CREDIT + transactionType = TransactionType.CREDIT, ) }, onPay = {}, - onRequest = {} + onRequest = {}, ) } @Preview @Composable -fun PayRequestScreenPreview() { +private fun PayRequestScreenPreview() { PayRequestScreen({}, {}) } @Preview @Composable -fun PayCardPreview() { - PayCard(Modifier.width(150.dp), "Request", R.drawable.feature_home_ic_arrow_back_black_24dp) { } +private fun PayCardPreview() { + PayCard( + "Request", + R.drawable.feature_home_ic_arrow_back_black_24dp, + { }, + Modifier.width(150.dp), + ) } diff --git a/feature/home/src/main/kotlin/org/mifospay/feature/home/HomeViewModel.kt b/feature/home/src/main/kotlin/org/mifospay/feature/home/HomeViewModel.kt index bab510c0..ca572713 100644 --- a/feature/home/src/main/kotlin/org/mifospay/feature/home/HomeViewModel.kt +++ b/feature/home/src/main/kotlin/org/mifospay/feature/home/HomeViewModel.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.home import androidx.lifecycle.ViewModel @@ -11,10 +20,10 @@ import kotlinx.coroutines.flow.update import org.mifospay.core.data.base.UseCase.UseCaseCallback import org.mifospay.core.data.base.UseCaseHandler import org.mifospay.core.data.domain.usecase.account.FetchAccount +import org.mifospay.core.data.domain.usecase.history.HistoryContract +import org.mifospay.core.data.domain.usecase.history.TransactionsHistory import org.mifospay.core.data.repository.local.LocalRepository import org.mifospay.core.datastore.PreferencesHelper -import org.mifospay.feature.HistoryContract -import org.mifospay.feature.TransactionsHistory import javax.inject.Inject @HiltViewModel @@ -23,8 +32,8 @@ class HomeViewModel @Inject constructor( private val localRepository: LocalRepository, private val preferencesHelper: PreferencesHelper, private val fetchAccountUseCase: FetchAccount, - private val transactionsHistory: TransactionsHistory -) : ViewModel(), HistoryContract.TransactionsHistoryAsync{ + private val transactionsHistory: TransactionsHistory, +) : ViewModel(), HistoryContract.TransactionsHistoryAsync { // Expose screen UI state private val _homeUIState: MutableStateFlow = MutableStateFlow(HomeUiState.Loading) @@ -36,7 +45,8 @@ class HomeViewModel @Inject constructor( } fun fetchAccountDetails() { - useCaseHandler.execute(fetchAccountUseCase, + useCaseHandler.execute( + fetchAccountUseCase, FetchAccount.RequestValues(localRepository.clientDetails.clientId), object : UseCaseCallback { override fun onSuccess(response: FetchAccount.ResponseValue) { @@ -44,7 +54,7 @@ class HomeViewModel @Inject constructor( _homeUIState.update { HomeUiState.Success( account = response.account, - vpa = localRepository.clientDetails.externalId + vpa = localRepository.clientDetails.externalId, ) } response.account.id.let { @@ -55,7 +65,8 @@ class HomeViewModel @Inject constructor( override fun onError(message: String) { _homeUIState.update { HomeUiState.Error } } - }) + }, + ) } override fun onTransactionsFetchCompleted(transactions: List?) { @@ -71,8 +82,8 @@ sealed interface HomeUiState { data class Success( val account: Account? = null, val transactions: List = emptyList(), - val vpa: String? = null + val vpa: String? = null, ) : HomeUiState data object Error : HomeUiState -} \ No newline at end of file +} diff --git a/feature/home/src/main/kotlin/org/mifospay/feature/home/navigation/HomeNavigation.kt b/feature/home/src/main/kotlin/org/mifospay/feature/home/navigation/HomeNavigation.kt index 20895b90..aa44e66a 100644 --- a/feature/home/src/main/kotlin/org/mifospay/feature/home/navigation/HomeNavigation.kt +++ b/feature/home/src/main/kotlin/org/mifospay/feature/home/navigation/HomeNavigation.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.home.navigation import androidx.navigation.NavController @@ -12,12 +21,12 @@ fun NavController.navigateToHome(navOptions: NavOptions) = navigate(HOME_ROUTE, fun NavGraphBuilder.homeScreen( onRequest: (String) -> Unit, - onPay: () -> Unit + onPay: () -> Unit, ) { composable(route = HOME_ROUTE) { HomeRoute( onRequest = onRequest, - onPay = onPay + onPay = onPay, ) } } diff --git a/feature/home/src/main/res/drawable/feature_home_ic_arrow_back_black_24dp.xml b/feature/home/src/main/res/drawable/feature_home_ic_arrow_back_black_24dp.xml index 58105a0f..a5dc0c32 100644 --- a/feature/home/src/main/res/drawable/feature_home_ic_arrow_back_black_24dp.xml +++ b/feature/home/src/main/res/drawable/feature_home_ic_arrow_back_black_24dp.xml @@ -1,3 +1,13 @@ + + + #DE000000 \ No newline at end of file diff --git a/feature/home/src/main/res/values/strings.xml b/feature/home/src/main/res/values/strings.xml index fbc1a63e..ced67cb0 100644 --- a/feature/home/src/main/res/values/strings.xml +++ b/feature/home/src/main/res/values/strings.xml @@ -1,4 +1,13 @@ + Loading \ No newline at end of file diff --git a/feature/home/src/test/java/org/mifospay/feature/home/ExampleUnitTest.kt b/feature/home/src/test/java/org/mifospay/feature/home/ExampleUnitTest.kt deleted file mode 100644 index b855ccb4..00000000 --- a/feature/home/src/test/java/org/mifospay/feature/home/ExampleUnitTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.mifospay.feature.home - -import org.junit.Assert.assertEquals -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/feature/invoices/build.gradle.kts b/feature/invoices/build.gradle.kts index 3f05205c..93df4818 100644 --- a/feature/invoices/build.gradle.kts +++ b/feature/invoices/build.gradle.kts @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ plugins { alias(libs.plugins.mifospay.android.feature) alias(libs.plugins.mifospay.android.library.compose) @@ -9,5 +18,4 @@ android { dependencies { implementation(projects.core.data) - implementation(projects.feature.receipt) } \ No newline at end of file diff --git a/feature/invoices/src/androidTest/java/org/mifospay/invoices/ExampleInstrumentedTest.kt b/feature/invoices/src/androidTest/java/org/mifospay/invoices/ExampleInstrumentedTest.kt deleted file mode 100644 index c6c25c3a..00000000 --- a/feature/invoices/src/androidTest/java/org/mifospay/invoices/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.mifospay.invoices - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("org.mifospay.invoices.test", appContext.packageName) - } -} \ No newline at end of file diff --git a/feature/invoices/src/main/AndroidManifest.xml b/feature/invoices/src/main/AndroidManifest.xml index a5918e68..96138981 100644 --- a/feature/invoices/src/main/AndroidManifest.xml +++ b/feature/invoices/src/main/AndroidManifest.xml @@ -1,4 +1,13 @@ + \ No newline at end of file diff --git a/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/InvoiceDetailScreen.kt b/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/InvoiceDetailScreen.kt index 1f5f168a..80285cd1 100644 --- a/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/InvoiceDetailScreen.kt +++ b/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/InvoiceDetailScreen.kt @@ -1,6 +1,14 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.invoices -import android.net.Uri import androidx.compose.foundation.background import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement @@ -14,7 +22,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -40,30 +47,31 @@ import org.mifospay.core.ui.ErrorScreenContent import org.mifospay.invoices.R @Composable -fun InvoiceDetailScreen( - viewModel: InvoiceDetailViewModel = hiltViewModel(), - data: Uri?, +internal fun InvoiceDetailScreen( onBackPress: () -> Unit, - navigateToReceiptScreen: (String) -> Unit + navigateToReceiptScreen: (String) -> Unit, + modifier: Modifier = Modifier, + viewModel: InvoiceDetailViewModel = hiltViewModel(), ) { val invoiceDetailUiState by viewModel.invoiceDetailUiState.collectAsStateWithLifecycle() + InvoiceDetailScreen( invoiceDetailUiState = invoiceDetailUiState, onBackPress = onBackPress, - navigateToReceiptScreen = navigateToReceiptScreen + navigateToReceiptScreen = navigateToReceiptScreen, + modifier = modifier, ) - LaunchedEffect(key1 = true) { - viewModel.getInvoiceDetails(data) - } } @Composable -fun InvoiceDetailScreen( +private fun InvoiceDetailScreen( invoiceDetailUiState: InvoiceDetailUiState, onBackPress: () -> Unit, - navigateToReceiptScreen: (String) -> Unit + navigateToReceiptScreen: (String) -> Unit, + modifier: Modifier = Modifier, ) { MifosScaffold( + modifier = modifier, topBarTitle = R.string.feature_invoices_invoice, backPress = { onBackPress.invoke() }, scaffoldContent = { @@ -71,7 +79,7 @@ fun InvoiceDetailScreen( modifier = Modifier .fillMaxSize() .background(MaterialTheme.colorScheme.surface) - .padding(it) + .padding(it), ) { when (invoiceDetailUiState) { is InvoiceDetailUiState.Error -> { @@ -80,7 +88,7 @@ fun InvoiceDetailScreen( InvoiceDetailUiState.Loading -> { MfOverlayLoadingWheel( - contentDesc = stringResource(R.string.feature_invoices_loading) + contentDesc = stringResource(R.string.feature_invoices_loading), ) } @@ -89,90 +97,92 @@ fun InvoiceDetailScreen( invoiceDetailUiState.invoice, invoiceDetailUiState.merchantId, invoiceDetailUiState.paymentLink, - navigateToReceiptScreen = navigateToReceiptScreen + navigateToReceiptScreen = navigateToReceiptScreen, ) } } } - }) + }, + ) } @Composable @Suppress("LongMethod") -fun InvoiceDetailsContent( +private fun InvoiceDetailsContent( invoice: Invoice?, merchantId: String?, paymentLink: String?, - navigateToReceiptScreen: (String) -> Unit + navigateToReceiptScreen: (String) -> Unit, + modifier: Modifier = Modifier, ) { val clipboardManager = LocalClipboardManager.current Column( - modifier = Modifier + modifier = modifier .fillMaxWidth() - .padding(horizontal = 16.dp) + .padding(horizontal = 16.dp), ) { Text( text = stringResource(R.string.feature_invoices_invoice_details), - modifier = Modifier.padding(top = 16.dp) + modifier = Modifier.padding(top = 16.dp), ) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text( text = stringResource(R.string.feature_invoices_merchant_id), - modifier = Modifier.padding(top = 16.dp) + modifier = Modifier.padding(top = 16.dp), ) Text( text = merchantId.toString(), - modifier = Modifier.padding(top = 16.dp) + modifier = Modifier.padding(top = 16.dp), ) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text( text = stringResource(R.string.feature_invoices_consumer_id), - modifier = Modifier.padding(top = 8.dp) + modifier = Modifier.padding(top = 8.dp), ) Text( text = (invoice?.consumerName + " " + invoice?.consumerId), - modifier = Modifier.padding(top = 8.dp) + modifier = Modifier.padding(top = 8.dp), ) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text( text = stringResource(id = R.string.feature_invoices_amount), - modifier = Modifier.padding(top = 8.dp) + modifier = Modifier.padding(top = 8.dp), ) Text( text = Constants.INR + " " + invoice?.amount + "", - modifier = Modifier.padding(top = 8.dp) + modifier = Modifier.padding(top = 8.dp), ) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text( text = stringResource(id = R.string.feature_invoices_items_bought), - modifier = Modifier.padding(top = 8.dp) + modifier = Modifier.padding(top = 8.dp), ) Text( text = invoice?.itemsBought.toString(), - modifier = Modifier.padding(top = 8.dp) + modifier = Modifier.padding(top = 8.dp), ) } @@ -180,42 +190,42 @@ fun InvoiceDetailsContent( Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text( text = stringResource(id = R.string.feature_invoices_status), - modifier = Modifier.padding(top = 8.dp) + modifier = Modifier.padding(top = 8.dp), ) Text( text = Constants.DONE, - modifier = Modifier.padding(top = 8.dp) + modifier = Modifier.padding(top = 8.dp), ) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text( text = stringResource(id = R.string.feature_invoices_transaction_id), color = MaterialTheme.colorScheme.primary, modifier = Modifier - .padding(top = 10.dp) + .padding(top = 10.dp), ) Text( text = invoice.transactionId ?: "", color = MaterialTheme.colorScheme.primary, modifier = Modifier .padding(top = 10.dp) - .then(Modifier.height(0.dp)) + .then(Modifier.height(0.dp)), ) } Divider() Text( text = stringResource(id = R.string.feature_invoices_unique_receipt_link), fontWeight = FontWeight.Normal, - modifier = Modifier.padding(bottom = 10.dp) + modifier = Modifier.padding(bottom = 10.dp), ) Text( text = invoice.transactionId ?: "", @@ -227,33 +237,34 @@ fun InvoiceDetailsContent( onPress = { invoice.transactionId?.let { it1 -> navigateToReceiptScreen.invoke( - it1 + it1, ) } }, onLongPress = { clipboardManager.setText( AnnotatedString( - Constants.RECEIPT_DOMAIN + invoice.transactionId - ) + Constants.RECEIPT_DOMAIN + invoice.transactionId, + ), ) - }) - } + }, + ) + }, ) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text( text = stringResource(id = R.string.feature_invoices_date), - modifier = Modifier.padding(top = 10.dp) + modifier = Modifier.padding(top = 10.dp), ) Text( text = DateHelper.getDateAsString(invoice!!.date), - modifier = Modifier.padding(top = 10.dp) + modifier = Modifier.padding(top = 10.dp), ) } @@ -261,18 +272,18 @@ fun InvoiceDetailsContent( Text( text = stringResource(id = R.string.feature_invoices_payment_options_will_be_fetched_from_upi), fontWeight = FontWeight.Normal, - modifier = Modifier.padding(vertical = 10.dp) + modifier = Modifier.padding(vertical = 10.dp), ) Divider() Text( text = stringResource(id = R.string.feature_invoices_unique_payment_link), fontWeight = FontWeight.Normal, - modifier = Modifier.padding(bottom = 10.dp) + modifier = Modifier.padding(bottom = 10.dp), ) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text( text = paymentLink.toString(), @@ -283,21 +294,21 @@ fun InvoiceDetailsContent( detectTapGestures( onLongPress = { clipboardManager.setText(AnnotatedString(paymentLink.toString())) - }) - } + }, + ) + }, ) } - } } @Composable -fun Divider(modifier: Modifier = Modifier) { +private fun Divider(modifier: Modifier = Modifier) { Box( modifier = modifier .fillMaxWidth() .height(1.dp) - .background(Color(0x44000000)) + .background(Color(0x44000000)), ) } @@ -306,21 +317,21 @@ class InvoiceDetailScreenProvider : PreviewParameterProvider(InvoiceDetailUiState.Loading) val invoiceDetailUiState: StateFlow = _invoiceDetailUiState - fun getInvoiceDetails(data: Uri?) { - mUseCaseHandler.execute(fetchInvoiceUseCase, FetchInvoice.RequestValues(data), + init { + savedStateHandle.get(INVOICE_DATA_ARG)?.let { invoiceData -> + Uri.decode(invoiceData)?.let { + getInvoiceDetails(Uri.parse(it)) + } + } + } + + private fun getInvoiceDetails(data: Uri?) { + mUseCaseHandler.execute( + fetchInvoiceUseCase, + FetchInvoice.RequestValues(data), object : UseCase.UseCaseCallback { override fun onSuccess(response: FetchInvoice.ResponseValue) { _invoiceDetailUiState.value = InvoiceDetailUiState.Success( response.invoices[0], - mPreferencesHelper.fullName + " " - + mPreferencesHelper.clientId, data.toString() + mPreferencesHelper.fullName + " " + + mPreferencesHelper.clientId, + data.toString(), ) } override fun onError(message: String) { _invoiceDetailUiState.value = InvoiceDetailUiState.Error(message) } - }) + }, + ) } } @@ -46,8 +70,8 @@ sealed interface InvoiceDetailUiState { data class Success( val invoice: Invoice?, val merchantId: String?, - val paymentLink: String? + val paymentLink: String?, ) : InvoiceDetailUiState data class Error(val message: String) : InvoiceDetailUiState -} \ No newline at end of file +} diff --git a/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/InvoiceItem.kt b/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/InvoiceItem.kt index 94c2d0b6..de8eba66 100644 --- a/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/InvoiceItem.kt +++ b/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/InvoiceItem.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.invoices import androidx.compose.foundation.background @@ -25,81 +34,84 @@ import org.mifospay.core.designsystem.theme.grey import org.mifospay.invoices.R @Composable -fun InvoiceItem( +internal fun InvoiceItem( invoiceTitle: String, invoiceAmount: String, invoiceStatus: String, invoiceDate: String, invoiceId: String, invoiceStatusIcon: Long, - onClick: (String) -> Unit + onClick: (String) -> Unit, + modifier: Modifier = Modifier, ) { Card( - modifier = Modifier + modifier = modifier .fillMaxWidth() .padding(4.dp) .clickable { onClick(invoiceId) }, elevation = CardDefaults.cardElevation(4.dp), - colors = CardDefaults.cardColors(Color.White) + colors = CardDefaults.cardColors(Color.White), ) { Column { Row( modifier = Modifier .fillMaxWidth() .padding(10.dp), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Icon( painter = painterResource( - id = if (invoiceStatusIcon == 0L) + id = if (invoiceStatusIcon == 0L) { R.drawable.feature_invoices_ic_remove_circle_outline_black_24dp - else R.drawable.feature_invoices_ic_check_round_black_24dp + } else { + R.drawable.feature_invoices_ic_check_round_black_24dp + }, ), contentDescription = "Invoice Status", modifier = Modifier .size(64.dp) .padding(5.dp), - tint = if (invoiceStatusIcon == 0L) Color.Yellow else Color.Blue + tint = if (invoiceStatusIcon == 0L) Color.Yellow else Color.Blue, ) Column( modifier = Modifier .fillMaxWidth() - .padding(end = 10.dp) + .padding(end = 10.dp), ) { Row( modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text( text = invoiceTitle, color = Color.Black, - modifier = Modifier.weight(1f) + modifier = Modifier.weight(1f), ) Text( text = invoiceAmount, color = Color.Black, textAlign = TextAlign.End, - modifier = Modifier.weight(1f) + modifier = Modifier.weight(1f), ) } Text( text = invoiceStatus, color = grey, - modifier = Modifier.padding(top = 1.dp) + modifier = Modifier.padding(top = 1.dp), ) Row( - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) { Text( text = invoiceDate, color = grey, - modifier = Modifier.weight(1f) + modifier = Modifier.weight(1f), ) Text( text = invoiceId, color = grey, textAlign = TextAlign.End, - modifier = Modifier.weight(1f) + modifier = Modifier.weight(1f), ) } Spacer( @@ -107,7 +119,7 @@ fun InvoiceItem( .padding(top = 10.dp) .fillMaxWidth() .height(1.dp) - .background(Color.Gray) + .background(Color.Gray), ) } } @@ -117,6 +129,14 @@ fun InvoiceItem( @Preview(showBackground = true) @Composable -fun PreviewInvoiceItem() { - InvoiceItem("Logo for Richard", "$3000", "Pending", "12/3/4", "Invoice id:12345", 0L) {} +private fun PreviewInvoiceItem() { + InvoiceItem( + invoiceTitle = "Logo for Richard", + invoiceAmount = "$3000", + invoiceStatus = "Pending", + invoiceDate = "12/3/4", + invoiceId = "Invoice id:12345", + invoiceStatusIcon = 0L, + onClick = {}, + ) } diff --git a/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/InvoiceScreen.kt b/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/InvoiceScreen.kt index 42793f63..1d74c589 100644 --- a/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/InvoiceScreen.kt +++ b/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/InvoiceScreen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.invoices import android.net.Uri @@ -25,24 +34,26 @@ import org.mifospay.invoices.R @Composable fun InvoiceScreenRoute( + navigateToInvoiceDetailScreen: (Uri) -> Unit, + modifier: Modifier = Modifier, viewModel: InvoicesViewModel = hiltViewModel(), - navigateToInvoiceDetailScreen: (Uri) -> Unit ) { val invoiceUiState by viewModel.invoiceUiState.collectAsStateWithLifecycle() InvoiceScreen( invoiceUiState = invoiceUiState, getUniqueInvoiceLink = { viewModel.getUniqueInvoiceLink(it) }, - navigateToInvoiceDetailScreen = navigateToInvoiceDetailScreen + navigateToInvoiceDetailScreen = navigateToInvoiceDetailScreen, + modifier = modifier, ) } @Composable -fun InvoiceScreen( +private fun InvoiceScreen( invoiceUiState: InvoicesUiState, getUniqueInvoiceLink: (Long) -> Uri?, - navigateToInvoiceDetailScreen: (Uri) -> Unit + navigateToInvoiceDetailScreen: (Uri) -> Unit, + modifier: Modifier = Modifier, ) { - when (invoiceUiState) { is InvoicesUiState.Error -> { EmptyContentScreen( @@ -50,14 +61,16 @@ fun InvoiceScreen( title = stringResource(id = R.string.feature_invoices_error_oops), subTitle = stringResource(id = R.string.feature_invoices_unexpected_error_subtitle), iconTint = Color.Black, - iconImageVector = Info + iconImageVector = Info, ) } is InvoicesUiState.InvoiceList -> { - Column(modifier = Modifier.fillMaxSize()) { + Column(modifier = modifier.fillMaxSize()) { LazyColumn(modifier = Modifier.fillMaxSize()) { - items(invoiceUiState.list) { + items( + items = invoiceUiState.list, + ) { InvoiceItem( invoiceTitle = it?.title.toString(), invoiceAmount = it?.amount.toString(), @@ -70,7 +83,7 @@ fun InvoiceScreen( invoiceUri?.let { uri -> navigateToInvoiceDetailScreen.invoke(uri) } - } + }, ) } } @@ -89,7 +102,7 @@ fun InvoiceScreen( InvoicesUiState.Loading -> { MifosLoadingWheel( modifier = Modifier.fillMaxWidth(), - contentDesc = stringResource(R.string.feature_invoices_loading) + contentDesc = stringResource(R.string.feature_invoices_loading), ) } } @@ -101,14 +114,14 @@ class InvoicesUiStateProvider : PreviewParameterProvider { InvoicesUiState.Loading, InvoicesUiState.Empty, InvoicesUiState.InvoiceList(sampleInvoiceList), - InvoicesUiState.Error("Some Error Occurred") + InvoicesUiState.Error("Some Error Occurred"), ) } @Preview(showBackground = true, showSystemUi = true) @Composable private fun InvoiceScreenPreview( - @PreviewParameter(InvoicesUiStateProvider::class) invoiceUiState: InvoicesUiState + @PreviewParameter(InvoicesUiStateProvider::class) invoiceUiState: InvoicesUiState, ) { MifosTheme { InvoiceScreen(invoiceUiState = invoiceUiState, getUniqueInvoiceLink = { Uri.EMPTY }, {}) @@ -125,6 +138,6 @@ val sampleInvoiceList = List(10) { index -> transactionId = "txn_78910", id = index.toLong(), title = "Stationery Purchase", - date = mutableListOf(2024, 3, 23) + date = mutableListOf(2024, 3, 23), ) } diff --git a/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/InvoicesViewModel.kt b/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/InvoicesViewModel.kt index 3eab7f7d..6457b089 100644 --- a/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/InvoicesViewModel.kt +++ b/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/InvoicesViewModel.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.invoices import android.net.Uri @@ -6,10 +15,10 @@ import com.mifospay.core.model.entity.Invoice import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import org.mifospay.common.Constants.INVOICE_DOMAIN import org.mifospay.core.data.base.UseCase import org.mifospay.core.data.base.UseCaseHandler import org.mifospay.core.data.domain.usecase.invoice.FetchInvoices -import org.mifospay.common.Constants.INVOICE_DOMAIN import org.mifospay.core.datastore.PreferencesHelper import javax.inject.Inject @@ -17,33 +26,36 @@ import javax.inject.Inject class InvoicesViewModel @Inject constructor( private val mUseCaseHandler: UseCaseHandler, private val mPreferencesHelper: PreferencesHelper, - private val fetchInvoicesUseCase: FetchInvoices + private val fetchInvoicesUseCase: FetchInvoices, ) : ViewModel() { - private val _invoiceUiState = MutableStateFlow(InvoicesUiState.Loading) val invoiceUiState: StateFlow = _invoiceUiState private fun fetchInvoices() { _invoiceUiState.value = InvoicesUiState.Loading - mUseCaseHandler.execute(fetchInvoicesUseCase, + mUseCaseHandler.execute( + fetchInvoicesUseCase, FetchInvoices.RequestValues(mPreferencesHelper.clientId.toString() + ""), object : UseCase.UseCaseCallback { override fun onSuccess(response: FetchInvoices.ResponseValue) { - if (response.invoiceList.isNotEmpty()) + if (response.invoiceList.isNotEmpty()) { _invoiceUiState.value = InvoicesUiState.InvoiceList(response.invoiceList) - else _invoiceUiState.value = InvoicesUiState.Empty + } else { + _invoiceUiState.value = InvoicesUiState.Empty + } } override fun onError(message: String) { _invoiceUiState.value = InvoicesUiState.Error(message) } - }) + }, + ) } fun getUniqueInvoiceLink(id: Long): Uri? { return Uri.parse( - INVOICE_DOMAIN + mPreferencesHelper.clientId + "/" + id + INVOICE_DOMAIN + mPreferencesHelper.clientId + "/" + id, ) } @@ -57,4 +69,4 @@ sealed class InvoicesUiState { data object Empty : InvoicesUiState() data class Error(val message: String) : InvoicesUiState() data class InvoiceList(val list: List) : InvoicesUiState() -} \ No newline at end of file +} diff --git a/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/navigation/InvoiceNavigation.kt b/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/navigation/InvoiceNavigation.kt index b5a3e614..bb5afe0f 100644 --- a/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/navigation/InvoiceNavigation.kt +++ b/feature/invoices/src/main/kotlin/org/mifospay/feature/invoices/navigation/InvoiceNavigation.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.invoices.navigation import android.net.Uri @@ -17,19 +26,17 @@ fun NavController.navigateToInvoiceDetail(invoiceData: String) { fun NavGraphBuilder.invoiceDetailScreen( navigateToReceiptScreen: (String) -> Unit, - onBackPress: () -> Unit + onBackPress: () -> Unit, ) { composable( route = "$INVOICE_ROUTE/{$INVOICE_DATA_ARG}", arguments = listOf( - navArgument(INVOICE_DATA_ARG) { type = NavType.StringType } - ) - ) { backStackEntry -> - val invoiceData = Uri.decode(backStackEntry.arguments?.getString(INVOICE_DATA_ARG)) + navArgument(INVOICE_DATA_ARG) { type = NavType.StringType }, + ), + ) { InvoiceDetailScreen( - data = Uri.parse(invoiceData), onBackPress = onBackPress, - navigateToReceiptScreen = navigateToReceiptScreen + navigateToReceiptScreen = navigateToReceiptScreen, ) } -} \ No newline at end of file +} diff --git a/feature/invoices/src/main/res/drawable/feature_invoices_ic_check_round_black_24dp.xml b/feature/invoices/src/main/res/drawable/feature_invoices_ic_check_round_black_24dp.xml index 7d196dc5..b3553706 100644 --- a/feature/invoices/src/main/res/drawable/feature_invoices_ic_check_round_black_24dp.xml +++ b/feature/invoices/src/main/res/drawable/feature_invoices_ic_check_round_black_24dp.xml @@ -1,3 +1,13 @@ + + + + #303F9F \ No newline at end of file diff --git a/feature/invoices/src/main/res/values/strings.xml b/feature/invoices/src/main/res/values/strings.xml index 67101d8e..bd850719 100644 --- a/feature/invoices/src/main/res/values/strings.xml +++ b/feature/invoices/src/main/res/values/strings.xml @@ -1,4 +1,13 @@ + Invoice Loading diff --git a/feature/invoices/src/test/java/org/mifospay/invoices/ExampleUnitTest.kt b/feature/invoices/src/test/java/org/mifospay/invoices/ExampleUnitTest.kt deleted file mode 100644 index 9eb7e5d2..00000000 --- a/feature/invoices/src/test/java/org/mifospay/invoices/ExampleUnitTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.mifospay.invoices - -import org.junit.Assert.assertEquals -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/feature/kyc/build.gradle.kts b/feature/kyc/build.gradle.kts index ce0d7e47..ef4ccfcc 100644 --- a/feature/kyc/build.gradle.kts +++ b/feature/kyc/build.gradle.kts @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ plugins { alias(libs.plugins.mifospay.android.feature) alias(libs.plugins.mifospay.android.library.compose) diff --git a/feature/kyc/src/androidTest/java/org/mifospay/kyc/ExampleInstrumentedTest.kt b/feature/kyc/src/androidTest/java/org/mifospay/kyc/ExampleInstrumentedTest.kt deleted file mode 100644 index b8a04ed5..00000000 --- a/feature/kyc/src/androidTest/java/org/mifospay/kyc/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.mifospay.kyc - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("org.mifospay.kyc.test", appContext.packageName) - } -} \ No newline at end of file diff --git a/feature/kyc/src/main/AndroidManifest.xml b/feature/kyc/src/main/AndroidManifest.xml index a5918e68..96138981 100644 --- a/feature/kyc/src/main/AndroidManifest.xml +++ b/feature/kyc/src/main/AndroidManifest.xml @@ -1,4 +1,13 @@ + \ No newline at end of file diff --git a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCDescriptionScreen.kt b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCDescriptionScreen.kt index f7dbf7a8..dcf6c179 100644 --- a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCDescriptionScreen.kt +++ b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCDescriptionScreen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.kyc import androidx.compose.foundation.layout.Arrangement @@ -49,15 +58,17 @@ import org.mifospay.kyc.R @Composable fun KYCScreen( - viewModel: KYCDescriptionViewModel = hiltViewModel(), onLevel1Clicked: () -> Unit, onLevel2Clicked: () -> Unit, - onLevel3Clicked: () -> Unit + onLevel3Clicked: () -> Unit, + modifier: Modifier = Modifier, + viewModel: KYCDescriptionViewModel = hiltViewModel(), ) { - val kUiState by viewModel.kycdescriptionState.collectAsState() + val kUiState by viewModel.kycDescriptionState.collectAsState() val isRefreshing by viewModel.isRefreshing.collectAsStateWithLifecycle() KYCDescriptionScreen( + modifier = modifier, kUiState = kUiState, onLevel1Clicked = { // Todo : Implement onLevel1Clicked flow @@ -72,22 +83,26 @@ fun KYCScreen( onLevel3Clicked.invoke() }, isRefreshing = isRefreshing, - onRefresh = { viewModel.refresh() } + onRefresh = viewModel::refresh, ) } @OptIn(ExperimentalMaterialApi::class) @Composable -fun KYCDescriptionScreen( +private fun KYCDescriptionScreen( kUiState: KYCDescriptionUiState, + isRefreshing: Boolean, + onRefresh: () -> Unit, onLevel1Clicked: () -> Unit, onLevel2Clicked: () -> Unit, onLevel3Clicked: () -> Unit, - isRefreshing: Boolean, - onRefresh: () -> Unit, + modifier: Modifier = Modifier, ) { val pullRefreshState = rememberPullRefreshState(isRefreshing, onRefresh) - Box(Modifier.pullRefresh(pullRefreshState)) { + Box( + modifier = modifier + .pullRefresh(pullRefreshState), + ) { when (kUiState) { KYCDescriptionUiState.Loading -> { MifosOverlayLoadingWheel(contentDesc = stringResource(R.string.feature_kyc_loading)) @@ -99,7 +114,7 @@ fun KYCDescriptionScreen( title = stringResource(id = R.string.feature_kyc_error_oops), subTitle = stringResource(id = R.string.feature_kyc_unexpected_error_subtitle), iconTint = MaterialTheme.colorScheme.primary, - iconImageVector = Icons.Rounded.Info + iconImageVector = Icons.Rounded.Info, ) } @@ -110,40 +125,42 @@ fun KYCDescriptionScreen( kyc, onLevel1Clicked, onLevel2Clicked, - onLevel3Clicked + onLevel3Clicked, ) } } } + PullRefreshIndicator( refreshing = isRefreshing, state = pullRefreshState, - modifier = Modifier.align(Alignment.TopCenter) + modifier = Modifier.align(Alignment.TopCenter), ) } } @Composable -fun KYCDescriptionScreen( +private fun KYCDescriptionScreen( kyc: KYCLevel1Details, onLevel1Clicked: () -> Unit, onLevel2Clicked: () -> Unit, - onLevel3Clicked: () -> Unit + onLevel3Clicked: () -> Unit, + modifier: Modifier = Modifier, ) { val currentLevel = kyc.currentLevel Column( - modifier = Modifier + modifier = modifier .fillMaxSize() .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Top + verticalArrangement = Arrangement.Top, ) { Text( text = stringResource(R.string.feature_kyc_complete_kyc), modifier = Modifier.padding(top = 40.dp), fontSize = 19.sp, - textAlign = TextAlign.Center + textAlign = TextAlign.Center, ) KYCLevelButton( @@ -151,7 +168,7 @@ fun KYCDescriptionScreen( enabled = currentLevel >= 0.toString(), completed = currentLevel >= 1.toString(), modifier = Modifier.padding(top = 90.dp), - onLevel1Clicked + onLevel1Clicked, ) KYCLevelButton( @@ -159,7 +176,7 @@ fun KYCDescriptionScreen( enabled = currentLevel >= 1.toString(), completed = currentLevel >= 2.toString(), modifier = Modifier.padding(top = 80.dp), - onLevel2Clicked + onLevel2Clicked, ) KYCLevelButton( @@ -167,47 +184,48 @@ fun KYCDescriptionScreen( enabled = currentLevel >= 2.toString(), completed = currentLevel >= 3.toString(), modifier = Modifier.padding(top = 80.dp), - onLevel3Clicked + onLevel3Clicked, ) } } @Composable -fun KYCLevelButton( +private fun KYCLevelButton( level: Int, enabled: Boolean, completed: Boolean, modifier: Modifier = Modifier, - onClick: () -> Unit = {} + onClick: () -> Unit = {}, ) { Row( modifier = modifier, verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, ) { ButtonComponent( stringResource(R.string.feature_kyc_level) + "$level", enabled, completed, - onClick + onClick, ) Spacer(modifier = Modifier.weight(0.1f)) IconComponent( completed, - modifier = Modifier.weight(0.9f) + modifier = Modifier.weight(0.9f), ) } } @Composable -fun ButtonComponent( +private fun ButtonComponent( value: String, enabled: Boolean, completed: Boolean, - onButtonClicked: () -> Unit + onButtonClicked: () -> Unit, + modifier: Modifier = Modifier, ) { Button( - modifier = Modifier + modifier = modifier .width(130.dp) .heightIn(38.dp) .shadow(43.dp), @@ -227,35 +245,38 @@ fun ButtonComponent( completed -> MaterialTheme.colorScheme.primary enabled -> MaterialTheme.colorScheme.onPrimary else -> Color.Gray - } + }, ), shape = RoundedCornerShape(10.dp), - enabled = enabled + enabled = enabled, ) { Text( text = value, fontSize = 18.sp, - fontWeight = FontWeight.Normal + fontWeight = FontWeight.Normal, ) } } @Composable -fun IconComponent(completed: Boolean, modifier: Modifier = Modifier) { +private fun IconComponent( + completed: Boolean, + modifier: Modifier = Modifier, +) { if (completed) { Row( modifier = modifier - .height(23.dp) + .height(23.dp), ) { Icon( Icons.Filled.Check, contentDescription = stringResource(R.string.feature_kyc_check), modifier = Modifier - .size(20.dp) + .size(20.dp), ) Spacer(modifier = Modifier.width(26.dp)) Text( - text = stringResource(R.string.feature_kyc_completion) + text = stringResource(R.string.feature_kyc_completion), ) } } @@ -263,7 +284,7 @@ fun IconComponent(completed: Boolean, modifier: Modifier = Modifier) { @Preview(showBackground = true) @Composable -fun KYCDescriptionPreview() { +private fun KYCDescriptionPreview() { val onLevel1Clicked: () -> Unit = { } val onLevel2Clicked: () -> Unit = { } val onLevel3Clicked: () -> Unit = { } @@ -271,41 +292,42 @@ fun KYCDescriptionPreview() { kyc = KYCLevel1Details(), onLevel1Clicked, onLevel2Clicked, - onLevel3Clicked + onLevel3Clicked, ) } -class KYCDescriptionUiStatePreviewProvider : PreviewParameterProvider { +internal class KYCDescriptionUiStatePreviewProvider : + PreviewParameterProvider { override val values = sequenceOf( KYCDescriptionUiState.Loading, KYCDescriptionUiState.Error, KYCDescriptionUiState.KYCDescription( KYCLevel1Details().apply { currentLevel = "0" - } + }, ), KYCDescriptionUiState.KYCDescription( KYCLevel1Details().apply { currentLevel = "1" - } + }, ), KYCDescriptionUiState.KYCDescription( KYCLevel1Details().apply { currentLevel = "2" - } + }, ), KYCDescriptionUiState.KYCDescription( KYCLevel1Details().apply { currentLevel = "3" - } - ) + }, + ), ) } @Preview(showBackground = true) @Composable -fun KYCDescriptionScreenPreview( - @PreviewParameter(KYCDescriptionUiStatePreviewProvider::class) uiState: KYCDescriptionUiState +private fun KYCDescriptionScreenPreview( + @PreviewParameter(KYCDescriptionUiStatePreviewProvider::class) uiState: KYCDescriptionUiState, ) { KYCDescriptionScreen( kUiState = uiState, @@ -313,25 +335,25 @@ fun KYCDescriptionScreenPreview( onLevel2Clicked = {}, onLevel3Clicked = {}, isRefreshing = false, - onRefresh = {} + onRefresh = {}, ) } @Preview(showBackground = true) @Composable -fun ButtonComponentPreview() { +private fun ButtonComponentPreview() { Column { - ButtonComponent(value = "Level 1", enabled = true, completed = false) {} - ButtonComponent(value = "Level 2", enabled = true, completed = true) {} - ButtonComponent(value = "Level 3", enabled = false, completed = false) {} + ButtonComponent(value = "Level 1", enabled = true, completed = false, onButtonClicked = {}) + ButtonComponent(value = "Level 2", enabled = true, completed = true, {}) + ButtonComponent(value = "Level 3", enabled = false, completed = false, {}) } } @Preview(showBackground = true) @Composable -fun IconComponentPreview() { +private fun IconComponentPreview() { Column { IconComponent(completed = true) IconComponent(completed = false) } -} \ No newline at end of file +} diff --git a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCDescriptionViewModel.kt b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCDescriptionViewModel.kt index 5f37fd08..c20a7776 100644 --- a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCDescriptionViewModel.kt +++ b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCDescriptionViewModel.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.kyc import androidx.lifecycle.ViewModel @@ -13,18 +22,17 @@ import org.mifospay.core.data.base.UseCase import org.mifospay.core.data.base.UseCaseHandler import org.mifospay.core.data.domain.usecase.kyc.FetchKYCLevel1Details import org.mifospay.core.data.repository.local.LocalRepository +import org.mifospay.feature.kyc.KYCDescriptionUiState.Loading import javax.inject.Inject @HiltViewModel class KYCDescriptionViewModel @Inject constructor( private val mUseCaseHandler: UseCaseHandler, private val mLocalRepository: LocalRepository, - private val fetchKYCLevel1DetailsUseCase: FetchKYCLevel1Details -) : ViewModel(){ - private val _kycdescriptionState = - MutableStateFlow(KYCDescriptionUiState.Loading) - - val kycdescriptionState: StateFlow = _kycdescriptionState + private val fetchKYCLevel1DetailsUseCase: FetchKYCLevel1Details, +) : ViewModel() { + private val descriptionState = MutableStateFlow(Loading) + val kycDescriptionState: StateFlow = descriptionState init { fetchCurrentLevel() @@ -46,31 +54,30 @@ class KYCDescriptionViewModel @Inject constructor( fetchKYCLevel1DetailsUseCase.walletRequestValues = FetchKYCLevel1Details.RequestValues(mLocalRepository.clientDetails.clientId.toInt()) val requestValues = fetchKYCLevel1DetailsUseCase.walletRequestValues - mUseCaseHandler.execute(fetchKYCLevel1DetailsUseCase, requestValues, + mUseCaseHandler.execute( + fetchKYCLevel1DetailsUseCase, + requestValues, object : UseCase.UseCaseCallback { override fun onSuccess(response: FetchKYCLevel1Details.ResponseValue) { if (response.kycLevel1DetailsList.size == 1) { - _kycdescriptionState.value = KYCDescriptionUiState.KYCDescription( - response.kycLevel1DetailsList.first()!! - ) + descriptionState.value = KYCDescriptionUiState.KYCDescription( + response.kycLevel1DetailsList.first()!!, + ) } else { - _kycdescriptionState.value = KYCDescriptionUiState.Error + descriptionState.value = KYCDescriptionUiState.Error } } override fun onError(message: String) { - _kycdescriptionState.value = KYCDescriptionUiState.Error + descriptionState.value = KYCDescriptionUiState.Error } - }) + }, + ) } - } sealed interface KYCDescriptionUiState { - data class KYCDescription( - val kycLevel1Details: KYCLevel1Details? - ) : KYCDescriptionUiState - + data class KYCDescription(val kycLevel1Details: KYCLevel1Details?) : KYCDescriptionUiState data object Error : KYCDescriptionUiState data object Loading : KYCDescriptionUiState -} \ No newline at end of file +} diff --git a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel1Screen.kt b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel1Screen.kt index 97d6efd3..a41203e9 100644 --- a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel1Screen.kt +++ b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel1Screen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.kyc import android.widget.Toast @@ -45,51 +54,33 @@ import org.mifospay.kyc.R import java.time.format.DateTimeFormatter @Composable -fun KYCLevel1Screen( +internal fun KYCLevel1Screen( + navigateToKycLevel2: () -> Unit, + modifier: Modifier = Modifier, viewModel: KYCLevel1ViewModel = hiltViewModel(), - navigateToKycLevel2: () -> Unit ) { val kyc1uiState by viewModel.kyc1uiState.collectAsStateWithLifecycle() KYCLevel1Screen( uiState = kyc1uiState, - submitData = { fname, - lname, - address1, - address2, - phoneno, - dob -> - viewModel.submitData( - fname.trim { it <= ' ' }, - lname.trim { it <= ' ' }, - address1.trim { it <= ' ' }, - address2.trim { it <= ' ' }, - phoneno.trim { it <= ' ' }, - dob.trim { it <= ' ' } - ) - }, - navigateToKycLevel2 = navigateToKycLevel2 + submitData = viewModel::submitData, + navigateToKycLevel2 = navigateToKycLevel2, + modifier = modifier, ) } @Composable -fun KYCLevel1Screen( +private fun KYCLevel1Screen( uiState: KYCLevel1UiState, - submitData: ( - String, - String, - String, - String, - String, - String - ) -> Unit, - navigateToKycLevel2: () -> Unit + submitData: (KYCLevel1DetailsState) -> Unit, + navigateToKycLevel2: () -> Unit, + modifier: Modifier = Modifier, ) { val context = LocalContext.current Kyc1Form( - modifier = Modifier, - submitData = submitData + submitData = submitData, + modifier = modifier, ) when (uiState) { @@ -101,7 +92,7 @@ fun KYCLevel1Screen( Toast.makeText( context, stringResource(R.string.feature_kyc_error_adding_KYC_Level_1_details), - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() navigateToKycLevel2.invoke() } @@ -110,7 +101,7 @@ fun KYCLevel1Screen( Toast.makeText( context, stringResource(R.string.feature_kyc_successkyc1), - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() navigateToKycLevel2.invoke() } @@ -119,18 +110,10 @@ fun KYCLevel1Screen( @OptIn(ExperimentalMaterial3Api::class) @Composable -fun Kyc1Form( - modifier: Modifier, - submitData: ( - String, - String, - String, - String, - String, - String - ) -> Unit +private fun Kyc1Form( + submitData: (KYCLevel1DetailsState) -> Unit, + modifier: Modifier = Modifier, ) { - var firstName by rememberSaveable { mutableStateOf("") } var lastName by rememberSaveable { mutableStateOf("") } var address1 by rememberSaveable { mutableStateOf("") } @@ -138,16 +121,26 @@ fun Kyc1Form( var mobileNumber by rememberSaveable { mutableStateOf("") } var dateOfBirth by rememberSaveable { mutableStateOf("") } val dateState = rememberUseCaseState() - val dateFormatter = DateTimeFormatter.ofPattern(stringResource(R.string.feature_kyc_date_format)) + val dateFormatter = + DateTimeFormatter.ofPattern(stringResource(R.string.feature_kyc_date_format)) + + val kycDetails = KYCLevel1DetailsState( + firstName = firstName, + lastName = lastName, + addressLine1 = address1, + addressLine2 = address2, + mobileNo = mobileNumber, + dob = dateOfBirth, + ) Column( - modifier = Modifier + modifier = modifier .fillMaxWidth() - .padding(16.dp) + .padding(16.dp), ) { Spacer(modifier = Modifier.height(20.dp)) MifosOutlinedTextField( - modifier = modifier + modifier = Modifier .fillMaxWidth() .padding(vertical = 8.dp), value = firstName, @@ -157,7 +150,7 @@ fun Kyc1Form( label = R.string.feature_kyc_first_name, ) MifosOutlinedTextField( - modifier = modifier + modifier = Modifier .fillMaxWidth() .padding(vertical = 8.dp), value = lastName, @@ -167,7 +160,7 @@ fun Kyc1Form( label = R.string.feature_kyc_last_name, ) MifosOutlinedTextField( - modifier = modifier + modifier = Modifier .fillMaxWidth() .padding(vertical = 8.dp), value = address1, @@ -177,7 +170,7 @@ fun Kyc1Form( label = R.string.feature_kyc_address_line_1, ) MifosOutlinedTextField( - modifier = modifier + modifier = Modifier .fillMaxWidth() .padding(vertical = 8.dp), value = address2, @@ -189,14 +182,14 @@ fun Kyc1Form( Box( modifier = Modifier - .padding(vertical = 7.dp) + .padding(vertical = 7.dp), ) { val keyboardController = LocalSoftwareKeyboardController.current TogiCountryCodePicker( modifier = Modifier, shape = RoundedCornerShape(3.dp), colors = TextFieldDefaults.outlinedTextFieldColors( - focusedBorderColor = MaterialTheme.colorScheme.primary + focusedBorderColor = MaterialTheme.colorScheme.primary, ), onValueChange = { (code, phone), isValid -> if (isValid) { @@ -206,7 +199,7 @@ fun Kyc1Form( label = { Text(stringResource(id = R.string.feature_kyc_phone_number)) }, - keyboardActions = KeyboardActions { keyboardController?.hide() } + keyboardActions = KeyboardActions { keyboardController?.hide() }, ) } @@ -215,11 +208,11 @@ fun Kyc1Form( config = CalendarConfig( monthSelection = true, yearSelection = true, - style = CalendarStyle.MONTH + style = CalendarStyle.MONTH, ), selection = CalendarSelection.Date { date -> dateOfBirth = dateFormatter.format(date) - } + }, ) Box( @@ -230,26 +223,20 @@ fun Kyc1Form( .clickable { dateState.show() } .border( width = 1.dp, - color = Color.Black + color = Color.Black, ) .padding(12.dp) - .clip(shape = RoundedCornerShape(8.dp)) + .clip(shape = RoundedCornerShape(8.dp)), ) { Text( text = dateOfBirth.ifEmpty { stringResource(R.string.feature_kyc_select_dob) }, - style = MaterialTheme.typography.bodyLarge + style = MaterialTheme.typography.bodyLarge, ) } + Button( onClick = { - submitData( - firstName, - lastName, - address1, - address2, - mobileNumber, - dateOfBirth - ) + submitData(kycDetails) }, modifier = Modifier .align(Alignment.CenterHorizontally) @@ -262,36 +249,36 @@ fun Kyc1Form( @Preview(showBackground = true) @Composable -fun Kyc1FormPreview() { +private fun Kyc1FormPreview() { MifosTheme { KYCLevel1Screen( uiState = KYCLevel1UiState.Loading, - submitData = { _, _, _, _, _, _ -> }, - navigateToKycLevel2 = {} + submitData = { _ -> }, + navigateToKycLevel2 = {}, ) } } @Preview(showBackground = true) @Composable -fun Kyc1PreviewWithError() { +private fun Kyc1PreviewWithError() { MifosTheme { KYCLevel1Screen( uiState = KYCLevel1UiState.Error, - submitData = { _, _, _, _, _, _ -> }, - navigateToKycLevel2 = {} + submitData = { _ -> }, + navigateToKycLevel2 = {}, ) } } @Preview(showBackground = true) @Composable -fun Kyc1FormPreviewWithSuccess() { +private fun Kyc1FormPreviewWithSuccess() { MifosTheme { KYCLevel1Screen( uiState = KYCLevel1UiState.Success, - submitData = { _, _, _, _, _, _ -> }, - navigateToKycLevel2 = {} + submitData = { _ -> }, + navigateToKycLevel2 = {}, ) } -} \ No newline at end of file +} diff --git a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel1ViewModel.kt b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel1ViewModel.kt index a61a624c..1733a968 100644 --- a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel1ViewModel.kt +++ b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel1ViewModel.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.kyc import androidx.lifecycle.ViewModel @@ -9,53 +18,37 @@ import org.mifospay.core.data.base.UseCase import org.mifospay.core.data.base.UseCaseHandler import org.mifospay.core.data.domain.usecase.kyc.UploadKYCLevel1Details import org.mifospay.core.data.repository.local.LocalRepository +import org.mifospay.feature.kyc.KYCLevel1UiState.Loading import javax.inject.Inject @HiltViewModel class KYCLevel1ViewModel @Inject constructor( private val mUseCaseHandler: UseCaseHandler, private val mLocalRepository: LocalRepository, - private val uploadKYCLevel1DetailsUseCase: UploadKYCLevel1Details + private val uploadKYCLevel1DetailsUseCase: UploadKYCLevel1Details, ) : ViewModel() { - private val _kyc1uiState = - MutableStateFlow(KYCLevel1UiState.Loading) - val kyc1uiState: StateFlow = _kyc1uiState - - fun submitData( - fname: String, - lname: String, - address1: String, - address2: String, - phoneno: String, - dob: String - ) { - val kycLevel1Details = - KYCLevel1Details( - fname, - lname, - address1, - address2, - phoneno, - dob, - "1" - ) + private val kycUiState = MutableStateFlow(Loading) + val kyc1uiState: StateFlow = kycUiState + fun submitData(kycLevel1Details: KYCLevel1DetailsState) { uploadKYCLevel1DetailsUseCase.walletRequestValues = UploadKYCLevel1Details.RequestValues( mLocalRepository.clientDetails.clientId.toInt(), - kycLevel1Details + kycLevel1Details.toModel(), ) val requestValues = uploadKYCLevel1DetailsUseCase.walletRequestValues - mUseCaseHandler.execute(uploadKYCLevel1DetailsUseCase, requestValues, + mUseCaseHandler.execute( + uploadKYCLevel1DetailsUseCase, + requestValues, object : UseCase.UseCaseCallback { override fun onSuccess(response: UploadKYCLevel1Details.ResponseValue) { - _kyc1uiState.value = KYCLevel1UiState.Success + kycUiState.value = KYCLevel1UiState.Success } override fun onError(message: String) { - _kyc1uiState.value = KYCLevel1UiState.Error + kycUiState.value = KYCLevel1UiState.Error } - } + }, ) } } @@ -64,4 +57,32 @@ sealed interface KYCLevel1UiState { data object Loading : KYCLevel1UiState data object Success : KYCLevel1UiState data object Error : KYCLevel1UiState -} \ No newline at end of file +} + +data class KYCLevel1DetailsState( + val firstName: String, + + val lastName: String, + + val addressLine1: String, + + val addressLine2: String, + + val mobileNo: String, + + val dob: String, + + val currentLevel: String = "1", +) + +internal fun KYCLevel1DetailsState.toModel(): KYCLevel1Details { + return KYCLevel1Details( + firstName = firstName.trim(), + lastName = lastName.trim(), + addressLine1 = addressLine1.trim(), + addressLine2 = addressLine2.trim(), + mobileNo = mobileNo.trim(), + dob = dob.trim(), + currentLevel = currentLevel, + ) +} diff --git a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel2Screen.kt b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel2Screen.kt index 5426a66c..ce10cb81 100644 --- a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel2Screen.kt +++ b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel2Screen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.kyc import android.Manifest @@ -10,15 +19,13 @@ import android.provider.Settings import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold @@ -55,41 +62,34 @@ import org.mifospay.core.designsystem.component.MifosOutlinedTextField import org.mifospay.core.designsystem.theme.MifosTheme import org.mifospay.kyc.R - @Composable -fun KYCLevel2Screen( +internal fun KYCLevel2Screen( + onSuccessKyc2: () -> Unit, + modifier: Modifier = Modifier, viewModel: KYCLevel2ViewModel = hiltViewModel(), - onSuccessKyc2: () -> Unit ) { val kyc2uiState by viewModel.kyc2uiState.collectAsStateWithLifecycle() KYCLevel2Screen( uiState = kyc2uiState, - uploadData = { idType, - uri -> - viewModel.uploadKYCDocs( - idType, - uri - ) - }, - onSuccessKyc2 = onSuccessKyc2 + uploadData = viewModel::uploadKYCDocs, + onSuccessKyc2 = onSuccessKyc2, + modifier = modifier, ) } @Composable -fun KYCLevel2Screen( +private fun KYCLevel2Screen( uiState: KYCLevel2UiState, - uploadData: ( - String, - Uri, - ) -> Unit, - onSuccessKyc2: () -> Unit + uploadData: (String, Uri) -> Unit, + onSuccessKyc2: () -> Unit, + modifier: Modifier = Modifier, ) { val context = LocalContext.current Kyc2Form( - modifier = Modifier, - uploadData = uploadData + modifier = modifier, + uploadData = uploadData, ) when (uiState) { @@ -101,7 +101,7 @@ fun KYCLevel2Screen( Toast.makeText( context, stringResource(R.string.feature_kyc_error_adding_KYC_Level_2_details), - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() } @@ -109,22 +109,18 @@ fun KYCLevel2Screen( Toast.makeText( context, stringResource(R.string.feature_kyc_successkyc2), - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() onSuccessKyc2.invoke() } } - } @Suppress("LongMethod", "CyclomaticComplexMethod") @Composable -fun Kyc2Form( - modifier: Modifier, - uploadData: ( - String, - Uri, - ) -> Unit +private fun Kyc2Form( + uploadData: (String, Uri) -> Unit, + modifier: Modifier = Modifier, ) { var idType by rememberSaveable { mutableStateOf("") } val context = LocalContext.current @@ -141,14 +137,14 @@ fun Kyc2Form( mutableStateOf( if (SDK_INT >= 33) { ContextCompat.checkSelfPermission(context, Manifest.permission.READ_MEDIA_IMAGES) == - PackageManager.PERMISSION_GRANTED + PackageManager.PERMISSION_GRANTED } else { ContextCompat.checkSelfPermission( context, - Manifest.permission.READ_EXTERNAL_STORAGE + Manifest.permission.READ_EXTERNAL_STORAGE, ) == - PackageManager.PERMISSION_GRANTED - } + PackageManager.PERMISSION_GRANTED + }, ) } @@ -156,12 +152,12 @@ fun Kyc2Form( if (SDK_INT >= 33) { shouldShowRequestPermissionRationale( context as Activity, - Manifest.permission.READ_MEDIA_IMAGES + Manifest.permission.READ_MEDIA_IMAGES, ) } else { shouldShowRequestPermissionRationale( context as Activity, - Manifest.permission.READ_EXTERNAL_STORAGE + Manifest.permission.READ_EXTERNAL_STORAGE, ) } @@ -170,18 +166,22 @@ fun Kyc2Form( } val decideCurrentPermissionStatus: (Boolean, Boolean) -> String = - { storagePermissionGranted, shouldShowPermissionRationale -> - if (storagePermissionGranted) "Granted" - else if (shouldShowPermissionRationale) "Rejected" - else "Denied" + { granted, permissionRationale -> + if (granted) { + "Granted" + } else if (permissionRationale) { + "Rejected" + } else { + "Denied" + } } var currentPermissionStatus by remember { mutableStateOf( decideCurrentPermissionStatus( storagePermissionGranted, - shouldShowPermissionRationale - ) + shouldShowPermissionRationale, + ), ) } @@ -201,12 +201,12 @@ fun Kyc2Form( if (SDK_INT >= 33) { shouldShowRequestPermissionRationale( context, - Manifest.permission.READ_MEDIA_IMAGES + Manifest.permission.READ_MEDIA_IMAGES, ) } else { shouldShowRequestPermissionRationale( context, - Manifest.permission.READ_EXTERNAL_STORAGE + Manifest.permission.READ_EXTERNAL_STORAGE, ) } } @@ -214,55 +214,58 @@ fun Kyc2Form( !shouldShowPermissionRationale && !storagePermissionGranted currentPermissionStatus = decideCurrentPermissionStatus( storagePermissionGranted, - shouldShowPermissionRationale + shouldShowPermissionRationale, ) - }) + }, + ) - - DisposableEffect(key1 = lifecycleOwner, effect = { - val observer = LifecycleEventObserver { _, event -> - if (event == Lifecycle.Event.ON_START && - !storagePermissionGranted && - !shouldShowPermissionRationale - ) { - storagePermissionLauncher.launch(permission) + DisposableEffect( + key1 = lifecycleOwner, + effect = { + val observer = LifecycleEventObserver { _, event -> + if (event == Lifecycle.Event.ON_START && + !storagePermissionGranted && + !shouldShowPermissionRationale + ) { + storagePermissionLauncher.launch(permission) + } } - } - lifecycleOwner.lifecycle.addObserver(observer) - onDispose { - lifecycleOwner.lifecycle.removeObserver(observer) - } - }) + lifecycleOwner.lifecycle.addObserver(observer) + onDispose { + lifecycleOwner.lifecycle.removeObserver(observer) + } + }, + ) Scaffold( + modifier = modifier, snackbarHost = { SnackbarHost(hostState = snackBarHostState) - } + }, ) { contentPadding -> Box( modifier = Modifier .padding(contentPadding) .fillMaxSize(), - contentAlignment = Alignment.TopCenter + contentAlignment = Alignment.TopCenter, ) { Column( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 20.dp) + .padding(horizontal = 20.dp), + verticalArrangement = Arrangement.spacedBy(20.dp), ) { MifosOutlinedTextField( - modifier = modifier + modifier = Modifier .fillMaxWidth() .padding(vertical = 8.dp), value = idType, onValueChange = { idType = it }, - label = R.string.feature_kyc_id_type + label = R.string.feature_kyc_id_type, ) - Spacer(modifier = Modifier.width(20.dp)) - Row { Button( onClick = { @@ -272,10 +275,10 @@ fun Kyc2Form( Toast.makeText( context, R.string.feature_kyc_approve_permission, - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() } - } + }, ) { Text(text = stringResource(id = R.string.feature_kyc_browse)) } @@ -284,20 +287,18 @@ fun Kyc2Form( Text( text = stringResource(id = R.string.feature_kyc_file_name) + fileName, style = MaterialTheme.typography.bodyLarge, - modifier = Modifier.padding(horizontal = 2.dp) + modifier = Modifier.padding(horizontal = 2.dp), ) } } - Spacer(modifier = Modifier.height(20.dp)) - Button( modifier = Modifier.align(Alignment.CenterHorizontally), onClick = { result?.let { uri -> uploadData(idType, uri) } - } + }, ) { Text(text = stringResource(id = R.string.feature_kyc_submit)) } @@ -311,7 +312,7 @@ fun Kyc2Form( message = R.string.feature_kyc_approve_permission.toString(), actionLabel = R.string.feature_kyc_approve.toString(), duration = SnackbarDuration.Indefinite, - withDismissAction = true + withDismissAction = true, ) when (userAction) { SnackbarResult.ActionPerformed -> { @@ -330,7 +331,7 @@ fun Kyc2Form( if (shouldDirectUserToApplicationSettings) { Intent( Settings.ACTION_APPLICATION_DETAILS_SETTINGS, - Uri.fromParts("package", context.packageName, null) + Uri.fromParts("package", context.packageName, null), ).also { context.startActivity(it) } @@ -340,47 +341,47 @@ fun Kyc2Form( @Preview(showBackground = true) @Composable -fun EmptyKyc2FormPreview() { +private fun EmptyKyc2FormPreview() { MifosTheme { Kyc2Form( modifier = Modifier, - uploadData = { _, _ -> } + uploadData = { _, _ -> }, ) } } @Preview(showBackground = true) @Composable -fun Kyc2FormPreviewWithLoading() { +private fun Kyc2FormPreviewWithLoading() { MifosTheme { KYCLevel2Screen( uiState = KYCLevel2UiState.Loading, uploadData = { _, _ -> }, - onSuccessKyc2 = {} + onSuccessKyc2 = {}, ) } } @Preview(showBackground = true) @Composable -fun Kyc2FormPreviewWithError() { +private fun Kyc2FormPreviewWithError() { MifosTheme { KYCLevel2Screen( uiState = KYCLevel2UiState.Error, uploadData = { _, _ -> }, - onSuccessKyc2 = {} + onSuccessKyc2 = {}, ) } } @Preview(showBackground = true) @Composable -fun Kyc2FormPreviewWithSuccess() { +private fun Kyc2FormPreviewWithSuccess() { MifosTheme { KYCLevel2Screen( uiState = KYCLevel2UiState.Success, uploadData = { _, _ -> }, - onSuccessKyc2 = {} + onSuccessKyc2 = {}, ) } -} \ No newline at end of file +} diff --git a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel2ViewModel.kt b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel2ViewModel.kt index 994e1dce..04fba616 100644 --- a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel2ViewModel.kt +++ b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel2ViewModel.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.kyc import android.net.Uri @@ -8,11 +17,12 @@ import kotlinx.coroutines.flow.StateFlow import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody import okhttp3.RequestBody.Companion.asRequestBody +import org.mifospay.common.Constants import org.mifospay.core.data.base.UseCase import org.mifospay.core.data.base.UseCaseHandler import org.mifospay.core.data.domain.usecase.kyc.UploadKYCDocs -import org.mifospay.common.Constants import org.mifospay.core.datastore.PreferencesHelper +import org.mifospay.feature.kyc.KYCLevel2UiState.Loading import java.io.File import javax.inject.Inject @@ -20,12 +30,11 @@ import javax.inject.Inject class KYCLevel2ViewModel @Inject constructor( private val mUseCaseHandler: UseCaseHandler, private val preferencesHelper: PreferencesHelper, - private val uploadKYCDocsUseCase: UploadKYCDocs + private val uploadKYCDocsUseCase: UploadKYCDocs, ) : ViewModel() { - private val _kyc2uiState = - MutableStateFlow(KYCLevel2UiState.Loading) - val kyc2uiState: StateFlow = _kyc2uiState + private val kycUiState = MutableStateFlow(Loading) + val kyc2uiState: StateFlow = kycUiState fun uploadKYCDocs(identityType: String, result: Uri) { val file = result.path?.let { File(it) } @@ -34,21 +43,24 @@ class KYCLevel2ViewModel @Inject constructor( UploadKYCDocs.RequestValues( org.mifospay.core.data.util.Constants.ENTITY_TYPE_CLIENTS, preferencesHelper.clientId, file.name, it, - getRequestFileBody(file) + getRequestFileBody(file), ) } } val requestValues = uploadKYCDocsUseCase.walletRequestValues - mUseCaseHandler.execute(uploadKYCDocsUseCase, requestValues, + mUseCaseHandler.execute( + uploadKYCDocsUseCase, + requestValues, object : UseCase.UseCaseCallback { override fun onSuccess(response: UploadKYCDocs.ResponseValue) { - _kyc2uiState.value = KYCLevel2UiState.Success + kycUiState.value = KYCLevel2UiState.Success } override fun onError(message: String) { - _kyc2uiState.value = KYCLevel2UiState.Error + kycUiState.value = KYCLevel2UiState.Error } - }) + }, + ) } private fun getRequestFileBody(file: File): MultipartBody.Part { @@ -64,4 +76,4 @@ sealed interface KYCLevel2UiState { data object Loading : KYCLevel2UiState data object Success : KYCLevel2UiState data object Error : KYCLevel2UiState -} \ No newline at end of file +} diff --git a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel3Screen.kt b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel3Screen.kt index cf381a2a..f8e52354 100644 --- a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel3Screen.kt +++ b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel3Screen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.kyc import androidx.compose.foundation.layout.Arrangement @@ -25,24 +34,24 @@ import org.mifospay.core.designsystem.theme.MifosTheme import org.mifospay.kyc.R @Composable -fun KYCLevel3Screen( - viewModel: KYCLevel3ViewModel = hiltViewModel() -){ +internal fun KYCLevel3Screen( + modifier: Modifier = Modifier, + viewModel: KYCLevel3ViewModel = hiltViewModel(), +) { val kyc3uiState by viewModel.kyc3uiState.collectAsStateWithLifecycle() KYCLevel3Screen( - uiState = kyc3uiState + uiState = kyc3uiState, + modifier = modifier, ) } @Composable -fun KYCLevel3Screen( - uiState: KYCLevel3UiState -){ - - Kyc3Form( - modifier = Modifier - ) +private fun KYCLevel3Screen( + uiState: KYCLevel3UiState, + modifier: Modifier = Modifier, +) { + Kyc3Form(modifier = modifier) when (uiState) { KYCLevel3UiState.Loading -> { @@ -50,30 +59,29 @@ fun KYCLevel3Screen( } KYCLevel3UiState.Error -> { - //Todo : Implement Error state + // Todo : Implement Error state } KYCLevel3UiState.Success -> { - //Todo : Implement Success state + // Todo : Implement Success state } } } @Composable -fun Kyc3Form( - modifier: Modifier +private fun Kyc3Form( + modifier: Modifier = Modifier, ) { var panIdValue by rememberSaveable { mutableStateOf("") } Column( - modifier = Modifier + modifier = modifier .fillMaxSize() .padding(20.dp), - verticalArrangement = Arrangement.spacedBy(10.dp) + verticalArrangement = Arrangement.spacedBy(10.dp), ) { - MifosOutlinedTextField( - modifier = modifier + modifier = Modifier .fillMaxWidth() .padding(vertical = 8.dp), value = panIdValue, @@ -87,7 +95,7 @@ fun Kyc3Form( onClick = {}, modifier = Modifier .align(Alignment.CenterHorizontally) - .padding(16.dp) + .padding(16.dp), ) { Text(stringResource(R.string.feature_kyc_submit)) } @@ -96,8 +104,8 @@ fun Kyc3Form( @Preview(showBackground = true) @Composable -fun KYCLevel3ScreenPreview() { +private fun KYCLevel3ScreenPreview() { MifosTheme { Kyc3Form(modifier = Modifier) } -} \ No newline at end of file +} diff --git a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel3ViewModel.kt b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel3ViewModel.kt index 0d9df984..73ed5e5a 100644 --- a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel3ViewModel.kt +++ b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/KYCLevel3ViewModel.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.kyc import androidx.lifecycle.ViewModel @@ -6,24 +15,23 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import org.mifospay.core.data.base.UseCaseHandler import org.mifospay.core.data.repository.local.LocalRepository +import org.mifospay.feature.kyc.KYCLevel3UiState.Loading import javax.inject.Inject @HiltViewModel @Suppress("UnusedPrivateProperty") class KYCLevel3ViewModel @Inject constructor( private val mUseCaseHandler: UseCaseHandler, - private val mLocalRepository: LocalRepository + private val mLocalRepository: LocalRepository, ) : ViewModel() { - private val _kyc3uiState = - MutableStateFlow(KYCLevel3UiState.Loading) - val kyc3uiState: StateFlow = _kyc3uiState - - //Todo: Implement KYCLevel3ViewModel flow + private val kycUiState = MutableStateFlow(Loading) + val kyc3uiState: StateFlow = kycUiState + // Todo: Implement KYCLevel3ViewModel flow } sealed interface KYCLevel3UiState { data object Loading : KYCLevel3UiState data object Success : KYCLevel3UiState data object Error : KYCLevel3UiState -} \ No newline at end of file +} diff --git a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/navigation/KYCLevel1Navigation.kt b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/navigation/KYCLevel1Navigation.kt index c83183af..bfce6e3f 100644 --- a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/navigation/KYCLevel1Navigation.kt +++ b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/navigation/KYCLevel1Navigation.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.kyc.navigation import androidx.navigation.NavController @@ -12,11 +21,11 @@ fun NavController.navigateToKYCLevel1() { } fun NavGraphBuilder.kycLevel1Screen( - navigateToKycLevel2: () -> Unit + navigateToKycLevel2: () -> Unit, ) { composable(route = KYC_LEVEL_1_ROUTE) { KYCLevel1Screen( - navigateToKycLevel2 = navigateToKycLevel2 + navigateToKycLevel2 = navigateToKycLevel2, ) } -} \ No newline at end of file +} diff --git a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/navigation/KYCLevel2Navigation.kt b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/navigation/KYCLevel2Navigation.kt index 5e720622..3f5c6da2 100644 --- a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/navigation/KYCLevel2Navigation.kt +++ b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/navigation/KYCLevel2Navigation.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.kyc.navigation import androidx.navigation.NavController @@ -12,11 +21,11 @@ fun NavController.navigateToKYCLevel2() { } fun NavGraphBuilder.kycLevel2Screen( - onSuccessKyc2: () -> Unit + onSuccessKyc2: () -> Unit, ) { composable(route = KYC_LEVEL_2_ROUTE) { KYCLevel2Screen( - onSuccessKyc2 = onSuccessKyc2 + onSuccessKyc2 = onSuccessKyc2, ) } -} \ No newline at end of file +} diff --git a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/navigation/KYCLevel3Navigation.kt b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/navigation/KYCLevel3Navigation.kt index 8a127fba..c29db1d4 100644 --- a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/navigation/KYCLevel3Navigation.kt +++ b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/navigation/KYCLevel3Navigation.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.kyc.navigation import androidx.navigation.NavController @@ -15,4 +24,4 @@ fun NavGraphBuilder.kycLevel3Screen() { composable(route = KYC_LEVEL_3_ROUTE) { KYCLevel3Screen() } -} \ No newline at end of file +} diff --git a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/navigation/KYCNavigation.kt b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/navigation/KYCNavigation.kt index c2ac49cc..dbf59ad6 100644 --- a/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/navigation/KYCNavigation.kt +++ b/feature/kyc/src/main/kotlin/org/mifospay/feature/kyc/navigation/KYCNavigation.kt @@ -1,6 +1,14 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.kyc.navigation - import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable @@ -15,13 +23,13 @@ fun NavController.navigateToKYC() { fun NavGraphBuilder.kycScreen( onLevel1Clicked: () -> Unit, onLevel2Clicked: () -> Unit, - onLevel3Clicked: () -> Unit + onLevel3Clicked: () -> Unit, ) { composable(route = KYC_ROUTE) { KYCScreen( onLevel1Clicked = onLevel1Clicked, onLevel2Clicked = onLevel2Clicked, - onLevel3Clicked = onLevel3Clicked + onLevel3Clicked = onLevel3Clicked, ) } -} \ No newline at end of file +} diff --git a/feature/kyc/src/main/res/drawable/feature_kyc_ic_error_state.xml b/feature/kyc/src/main/res/drawable/feature_kyc_ic_error_state.xml index 7c7eea97..bedfa5a2 100644 --- a/feature/kyc/src/main/res/drawable/feature_kyc_ic_error_state.xml +++ b/feature/kyc/src/main/res/drawable/feature_kyc_ic_error_state.xml @@ -1,3 +1,13 @@ + + + + diff --git a/feature/kyc/src/main/res/values/colors.xml b/feature/kyc/src/main/res/values/colors.xml index a71307d8..7f718a9d 100644 --- a/feature/kyc/src/main/res/values/colors.xml +++ b/feature/kyc/src/main/res/values/colors.xml @@ -1,4 +1,13 @@ + #000000 #8A000000 diff --git a/feature/kyc/src/main/res/values/dimens.xml b/feature/kyc/src/main/res/values/dimens.xml index b5065a6c..108b4b61 100644 --- a/feature/kyc/src/main/res/values/dimens.xml +++ b/feature/kyc/src/main/res/values/dimens.xml @@ -1,4 +1,13 @@ + 40dp 15sp diff --git a/feature/kyc/src/main/res/values/strings.xml b/feature/kyc/src/main/res/values/strings.xml index 9dd4a0bc..b92a9afb 100644 --- a/feature/kyc/src/main/res/values/strings.xml +++ b/feature/kyc/src/main/res/values/strings.xml @@ -1,4 +1,13 @@ + Complete the KYC process Level 1 diff --git a/feature/kyc/src/test/java/org/mifospay/kyc/ExampleUnitTest.kt b/feature/kyc/src/test/java/org/mifospay/kyc/ExampleUnitTest.kt deleted file mode 100644 index f117e1df..00000000 --- a/feature/kyc/src/test/java/org/mifospay/kyc/ExampleUnitTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.mifospay.kyc - -import org.junit.Assert.assertEquals -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/feature/make-transfer/build.gradle.kts b/feature/make-transfer/build.gradle.kts index 74ea21ed..4d74d592 100644 --- a/feature/make-transfer/build.gradle.kts +++ b/feature/make-transfer/build.gradle.kts @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ plugins { alias(libs.plugins.mifospay.android.feature) alias(libs.plugins.mifospay.android.library.compose) diff --git a/feature/make-transfer/src/main/AndroidManifest.xml b/feature/make-transfer/src/main/AndroidManifest.xml index 8072ee00..ae124d8f 100644 --- a/feature/make-transfer/src/main/AndroidManifest.xml +++ b/feature/make-transfer/src/main/AndroidManifest.xml @@ -1,2 +1,11 @@ + diff --git a/feature/make-transfer/src/main/kotlin/org/mifospay/feature/make/transfer/MakeTransferScreen.kt b/feature/make-transfer/src/main/kotlin/org/mifospay/feature/make/transfer/MakeTransferScreen.kt index 535856bd..fba0b31a 100644 --- a/feature/make-transfer/src/main/kotlin/org/mifospay/feature/make/transfer/MakeTransferScreen.kt +++ b/feature/make-transfer/src/main/kotlin/org/mifospay/feature/make/transfer/MakeTransferScreen.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.make.transfer import android.widget.Toast @@ -16,13 +25,11 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet -import androidx.compose.material3.SheetState import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -37,71 +44,62 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import kotlinx.coroutines.launch import org.mifospay.common.Constants import org.mifospay.core.designsystem.component.MifosLoadingWheel @Composable -fun MakeTransferScreenRoute( - viewModel: MakeTransferViewModel = hiltViewModel(), - onDismiss: () -> Unit +internal fun MakeTransferScreenRoute( + onDismiss: () -> Unit, + modifier: Modifier = Modifier, + viewModel: MakeTransferViewModel = hiltViewModel(), ) { - val fetchPayeeClient by viewModel.fetchPayeeClient.collectAsStateWithLifecycle() + // TODO: commented out because not using it + // val fetchPayeeClient by viewModel.fetchPayeeClient.collectAsStateWithLifecycle() val makeTransferState by viewModel.makeTransferState.collectAsStateWithLifecycle() val showTransactionStatus by viewModel.showTransactionStatus.collectAsStateWithLifecycle() MakeTransferScreen( - uiState = makeTransferState, + state = makeTransferState, showTransactionStatus = showTransactionStatus, - makeTransfer = { toClientId, transferAmount -> - viewModel.makeTransfer( - toClientId, - transferAmount - ) - }, - onDismiss = onDismiss + makeTransfer = viewModel::makeTransfer, + onDismiss = onDismiss, + modifier = modifier, ) } @Composable -fun MakeTransferScreen( - uiState: MakeTransferState, +private fun MakeTransferScreen( + state: MakeTransferState, showTransactionStatus: ShowTransactionStatus, makeTransfer: (Long, Double) -> Unit, - onDismiss: () -> Unit + onDismiss: () -> Unit, + modifier: Modifier = Modifier, ) { val context = LocalContext.current - when (uiState) { + when (state) { MakeTransferState.Loading -> { MifosLoadingWheel( modifier = Modifier.fillMaxWidth(), - contentDesc = stringResource(R.string.feature_make_transfer_loading) + contentDesc = stringResource(R.string.feature_make_transfer_loading), ) } is MakeTransferState.Error -> { - val message = uiState.message + val message = state.message Toast.makeText(context, message, Toast.LENGTH_SHORT).show() } is MakeTransferState.Success -> { - - val showBottomSheet = uiState.showBottomSheet - val toClientId = uiState.toClientId - val resultName = uiState.resultName - val externalId = uiState.externalId - val transferAmount = uiState.transferAmount - val showTransactionStatus = showTransactionStatus - MakeTransferBottomSheetContent( - showBottomSheet, - toClientId, - resultName, - externalId, - transferAmount, - showTransactionStatus, + showBottomSheet = state.showBottomSheet, + toClientId = state.toClientId, + resultName = state.resultName, + externalId = state.externalId, + transferAmount = state.transferAmount, + showTransactionStatus = showTransactionStatus, makeTransfer = makeTransfer, - onDismiss = onDismiss + onDismiss = onDismiss, + modifier = modifier, ) } } @@ -109,7 +107,7 @@ fun MakeTransferScreen( @OptIn(ExperimentalMaterial3Api::class) @Composable -fun MakeTransferBottomSheetContent( +private fun MakeTransferBottomSheetContent( showBottomSheet: Boolean, toClientId: Long, resultName: String, @@ -117,89 +115,87 @@ fun MakeTransferBottomSheetContent( transferAmount: Double, showTransactionStatus: ShowTransactionStatus, makeTransfer: (Long, Double) -> Unit, - onDismiss: () -> Unit + onDismiss: () -> Unit, + modifier: Modifier = Modifier, ) { val sheetState = rememberModalBottomSheetState() - var showBottomSheet by rememberSaveable { - mutableStateOf(showBottomSheet) - } + var visibleBottomSheet by rememberSaveable { mutableStateOf(showBottomSheet) } - if (showBottomSheet) { + if (visibleBottomSheet) { ModalBottomSheet( + modifier = modifier, sheetState = sheetState, onDismissRequest = { - showBottomSheet = false + visibleBottomSheet = false onDismiss.invoke() }, dragHandle = { BottomSheetDefaults.DragHandle() }, ) { - if (showTransactionStatus.showErrorStatus || showTransactionStatus.showSuccessStatus) { + if (showTransactionStatus.showErrorStatus || + showTransactionStatus.showSuccessStatus + ) { TransactionStatusContent(showTransactionStatus) } else { MakeTransferContent( - toClientId, - resultName, - externalId, - transferAmount, + toClientId = toClientId, + resultName = resultName, + externalId = externalId, + transferAmount = transferAmount, makeTransfer = makeTransfer, - showBottomSheet = showBottomSheet, - sheetState + onCloseBottomSheet = { + visibleBottomSheet = false + onDismiss.invoke() + }, ) } } } } -@OptIn(ExperimentalMaterial3Api::class) @Composable -fun MakeTransferContent( +private fun MakeTransferContent( toClientId: Long, resultName: String, externalId: String, transferAmount: Double, makeTransfer: (Long, Double) -> Unit, - showBottomSheet: Boolean, - sheetState: SheetState? + onCloseBottomSheet: () -> Unit, + modifier: Modifier = Modifier, ) { - val scope = rememberCoroutineScope() - var showBottomSheet by rememberSaveable { - mutableStateOf(showBottomSheet) - } - Column( - modifier = Modifier + modifier = modifier .padding(20.dp) - .fillMaxWidth() + .fillMaxWidth(), ) { Text( text = stringResource(id = R.string.feature_make_transfer_send_money), style = TextStyle( fontSize = 24.sp, fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.onSurface + color = MaterialTheme.colorScheme.onSurface, ), modifier = Modifier .padding(vertical = 10.dp) - .align(Alignment.CenterHorizontally) + .align(Alignment.CenterHorizontally), ) Box( modifier = Modifier .padding(vertical = 10.dp) .fillMaxWidth() - .height(180.dp) + .height(180.dp), ) { Column( modifier = Modifier - .padding(20.dp) + .padding(20.dp), ) { Text( text = stringResource(id = R.string.feature_make_transfer_sending_to) + Constants.COLON, style = TextStyle( MaterialTheme.colorScheme.onSurface, - MaterialTheme.typography.bodyMedium.fontSize - ) + MaterialTheme.typography.bodyMedium.fontSize, + ), ) Spacer(modifier = Modifier.height(5.dp)) @@ -208,8 +204,8 @@ fun MakeTransferContent( text = resultName, style = TextStyle( MaterialTheme.colorScheme.onSurface, - MaterialTheme.typography.bodyLarge.fontSize - ) + MaterialTheme.typography.bodyLarge.fontSize, + ), ) Spacer(modifier = Modifier.height(5.dp)) @@ -218,8 +214,8 @@ fun MakeTransferContent( text = externalId, style = TextStyle( MaterialTheme.colorScheme.onSurface, - MaterialTheme.typography.bodyLarge.fontSize - ) + MaterialTheme.typography.bodyLarge.fontSize, + ), ) Spacer(modifier = Modifier.height(20.dp)) @@ -228,8 +224,8 @@ fun MakeTransferContent( text = stringResource(id = R.string.feature_make_transfer_amount) + Constants.COLON, style = TextStyle( MaterialTheme.colorScheme.onSurface, - MaterialTheme.typography.bodyMedium.fontSize - ) + MaterialTheme.typography.bodyMedium.fontSize, + ), ) Spacer(modifier = Modifier.height(5.dp)) @@ -238,8 +234,8 @@ fun MakeTransferContent( text = transferAmount.toString(), style = TextStyle( MaterialTheme.colorScheme.onSurface, - MaterialTheme.typography.bodyLarge.fontSize - ) + MaterialTheme.typography.bodyLarge.fontSize, + ), ) } } @@ -248,23 +244,13 @@ fun MakeTransferContent( modifier = Modifier .padding(top = 20.dp) .fillMaxWidth(), - horizontalArrangement = Arrangement.End + horizontalArrangement = Arrangement.End, ) { Button( - onClick = { - scope.launch { - sheetState?.hide() - }.invokeOnCompletion { - if (sheetState != null) { - if (!sheetState.isVisible) { - showBottomSheet = false - } - } - } - }, + onClick = onCloseBottomSheet, modifier = Modifier .width(100.dp) - .height(50.dp) + .height(50.dp), ) { Text(text = "Cancel") } @@ -275,12 +261,12 @@ fun MakeTransferContent( onClick = { makeTransfer( toClientId, - transferAmount + transferAmount, ) }, modifier = Modifier .width(100.dp) - .height(50.dp) + .height(50.dp), ) { Text(text = "Confirm") } @@ -288,13 +274,12 @@ fun MakeTransferContent( } } - @Composable -fun TransactionStatusContent(showTransactionStatus: ShowTransactionStatus) { +private fun TransactionStatusContent(showTransactionStatus: ShowTransactionStatus) { Column( modifier = Modifier .padding(20.dp) - .fillMaxWidth() + .fillMaxWidth(), ) { Text( text = if (showTransactionStatus.showSuccessStatus) { @@ -305,18 +290,18 @@ fun TransactionStatusContent(showTransactionStatus: ShowTransactionStatus) { style = TextStyle( fontSize = 24.sp, fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.onSurface + color = MaterialTheme.colorScheme.onSurface, ), modifier = Modifier .padding(vertical = 10.dp) - .align(Alignment.CenterHorizontally) + .align(Alignment.CenterHorizontally), ) Box( modifier = Modifier .padding(vertical = 10.dp) .fillMaxWidth() - .height(180.dp) + .height(180.dp), ) { Icon( if (showTransactionStatus.showSuccessStatus) { @@ -330,32 +315,31 @@ fun TransactionStatusContent(showTransactionStatus: ShowTransactionStatus) { stringResource(id = R.string.feature_make_transfer_transaction_unable_to_process) }, modifier = Modifier - .align(Alignment.Center) + .align(Alignment.Center), ) } - } } @Preview(showBackground = true) @Composable -fun PreviewWithMakeTransferContentLoading() { +private fun PreviewWithMakeTransferContentLoading() { MakeTransferScreen( - uiState = MakeTransferState.Loading, + state = MakeTransferState.Loading, showTransactionStatus = ShowTransactionStatus( showSuccessStatus = false, - showErrorStatus = false + showErrorStatus = false, ), makeTransfer = { _, _ -> }, - onDismiss = { } + onDismiss = { }, ) } @Preview(showBackground = true) @Composable -fun PreviewWithMakeTransferContentSuccess() { +private fun PreviewWithMakeTransferContentSuccess() { MakeTransferScreen( - uiState = MakeTransferState.Success( + state = MakeTransferState.Success( toClientId = 1234, resultName = "John Doe", externalId = "example@example.com", @@ -364,31 +348,29 @@ fun PreviewWithMakeTransferContentSuccess() { ), showTransactionStatus = ShowTransactionStatus( showSuccessStatus = false, - showErrorStatus = false + showErrorStatus = false, ), makeTransfer = { _, _ -> }, - onDismiss = { } + onDismiss = { }, ) } -@OptIn(ExperimentalMaterial3Api::class) @Preview(showBackground = true) @Composable -fun PreviewMakeTransferContent() { +private fun PreviewMakeTransferContent() { MakeTransferContent( toClientId = 1234, resultName = "John Doe", externalId = "example@example.com", transferAmount = 100.0, makeTransfer = { _, _ -> }, - sheetState = null, - showBottomSheet = true + onCloseBottomSheet = {}, ) } @Preview(showBackground = true) @Composable -fun PreviewMakeTransferBottomSheetContent() { +private fun PreviewMakeTransferBottomSheetContent() { MakeTransferBottomSheetContent( showBottomSheet = true, toClientId = 1234, @@ -397,24 +379,23 @@ fun PreviewMakeTransferBottomSheetContent() { transferAmount = 100.0, showTransactionStatus = ShowTransactionStatus( showSuccessStatus = false, - showErrorStatus = false + showErrorStatus = false, ), makeTransfer = { _, _ -> }, - onDismiss = { } + onDismiss = { }, ) } @Preview(showBackground = true) @Composable -fun PreviewWithMakeTransferContentError() { +private fun PreviewWithMakeTransferContentError() { MakeTransferScreen( - uiState = MakeTransferState.Error("An error occurred"), + state = MakeTransferState.Error("An error occurred"), showTransactionStatus = ShowTransactionStatus( showSuccessStatus = false, - showErrorStatus = false + showErrorStatus = false, ), makeTransfer = { _, _ -> }, - onDismiss = { } + onDismiss = { }, ) } - diff --git a/feature/make-transfer/src/main/kotlin/org/mifospay/feature/make/transfer/MakeTransferViewModel.kt b/feature/make-transfer/src/main/kotlin/org/mifospay/feature/make/transfer/MakeTransferViewModel.kt index 39f95d6f..a4bbda33 100644 --- a/feature/make-transfer/src/main/kotlin/org/mifospay/feature/make/transfer/MakeTransferViewModel.kt +++ b/feature/make-transfer/src/main/kotlin/org/mifospay/feature/make/transfer/MakeTransferViewModel.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.make.transfer import androidx.lifecycle.SavedStateHandle @@ -26,11 +35,13 @@ class MakeTransferViewModel @Inject constructor( private val useCaseHandler: UseCaseHandler, private val searchClientUseCase: SearchClient, private val transferFundsUseCase: TransferFunds, - private val localRepository: LocalRepository + private val localRepository: LocalRepository, ) : ViewModel() { - private val payeeExternalId: StateFlow = savedStateHandle.getStateFlow(PAYEE_EXTERNAL_ID_ARG, "") - private val transferAmount: StateFlow = savedStateHandle.getStateFlow(TRANSFER_AMOUNT_ARG, null) + private val payeeExternalId: StateFlow = + savedStateHandle.getStateFlow(PAYEE_EXTERNAL_ID_ARG, "") + private val transferAmount: StateFlow = + savedStateHandle.getStateFlow(TRANSFER_AMOUNT_ARG, null) private val _makeTransferState = MutableStateFlow(MakeTransferState.Loading) val makeTransferState: StateFlow = _makeTransferState.asStateFlow() @@ -38,24 +49,25 @@ class MakeTransferViewModel @Inject constructor( private val _showTransactionStatus = MutableStateFlow( ShowTransactionStatus( showSuccessStatus = false, - showErrorStatus = false - ) + showErrorStatus = false, + ), ) val showTransactionStatus: StateFlow = _showTransactionStatus.asStateFlow() - // Fetch Payee client details val fetchPayeeClient = combine(payeeExternalId, transferAmount, ::Pair) - .map { - it.takeIf { it.first.isNotEmpty() }?.let { - fetchClient(it.first, it.second?.toDouble() ?: 0.0) + .map { stringPair -> + stringPair.takeIf { it.first.isNotEmpty() }?.let { + fetchClient(it.first, it.second?.toDouble() ?: 0.0) + } } - } - .stateIn(scope = viewModelScope, started = SharingStarted.Eagerly, initialValue = null) + .stateIn(scope = viewModelScope, started = SharingStarted.Eagerly, initialValue = null) private fun fetchClient(externalId: String, transferAmount: Double) { - useCaseHandler.execute(searchClientUseCase, SearchClient.RequestValues(externalId), + useCaseHandler.execute( + searchClientUseCase, + SearchClient.RequestValues(externalId), object : UseCase.UseCaseCallback { override fun onSuccess(response: SearchClient.ResponseValue) { val searchResult = response.results[0] @@ -65,7 +77,7 @@ class MakeTransferViewModel @Inject constructor( searchResult.resultName, externalId, transferAmount, - true + true, ) } } @@ -73,35 +85,37 @@ class MakeTransferViewModel @Inject constructor( override fun onError(message: String) { _makeTransferState.value = MakeTransferState.Error(message) } - }) + }, + ) } fun makeTransfer(toClientId: Long, amount: Double) { val fromClientId = localRepository.clientDetails.clientId - useCaseHandler.execute(transferFundsUseCase, + useCaseHandler.execute( + transferFundsUseCase, TransferFunds.RequestValues(fromClientId, toClientId, amount), object : UseCase.UseCaseCallback { override fun onSuccess(response: TransferFunds.ResponseValue) { - _showTransactionStatus.value = ShowTransactionStatus( showSuccessStatus = true, - showErrorStatus = false + showErrorStatus = false, ) } override fun onError(message: String) { _showTransactionStatus.value = ShowTransactionStatus( showSuccessStatus = false, - showErrorStatus = true + showErrorStatus = true, ) } - }) + }, + ) } } data class ShowTransactionStatus( val showSuccessStatus: Boolean, - val showErrorStatus: Boolean + val showErrorStatus: Boolean, ) sealed interface MakeTransferState { @@ -115,4 +129,4 @@ sealed interface MakeTransferState { ) : MakeTransferState data class Error(val message: String) : MakeTransferState -} \ No newline at end of file +} diff --git a/feature/make-transfer/src/main/kotlin/org/mifospay/feature/make/transfer/navigation/MakeTransferNavigation.kt b/feature/make-transfer/src/main/kotlin/org/mifospay/feature/make/transfer/navigation/MakeTransferNavigation.kt index 52094b45..5e8f0be4 100644 --- a/feature/make-transfer/src/main/kotlin/org/mifospay/feature/make/transfer/navigation/MakeTransferNavigation.kt +++ b/feature/make-transfer/src/main/kotlin/org/mifospay/feature/make/transfer/navigation/MakeTransferNavigation.kt @@ -1,3 +1,12 @@ +/* + * Copyright 2024 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-wallet/blob/master/LICENSE.md + */ package org.mifospay.feature.make.transfer.navigation import androidx.compose.material.navigation.bottomSheet @@ -18,7 +27,7 @@ const val MAKE_TRANSFER_ROUTE = MAKE_TRANSFER_ROUTE_BASE + fun NavController.navigateToMakeTransferScreen( externalId: String? = null, transferAmount: String? = null, - navOptions: NavOptions? = null + navOptions: NavOptions? = null, ) { val route = MAKE_TRANSFER_ROUTE_BASE + if (transferAmount != null) { "?${PAYEE_EXTERNAL_ID_ARG}=$externalId" + @@ -31,7 +40,7 @@ fun NavController.navigateToMakeTransferScreen( } fun NavGraphBuilder.makeTransferScreen( - onDismiss: () -> Unit + onDismiss: () -> Unit, ) { bottomSheet( route = MAKE_TRANSFER_ROUTE, @@ -45,11 +54,11 @@ fun NavGraphBuilder.makeTransferScreen( type = NavType.StringType defaultValue = null nullable = true - } - ) + }, + ), ) { MakeTransferScreenRoute( - onDismiss = onDismiss + onDismiss = onDismiss, ) } } diff --git a/feature/make-transfer/src/main/res/drawable/feature_make_transfer_ic_close.xml b/feature/make-transfer/src/main/res/drawable/feature_make_transfer_ic_close.xml index d6ff41a3..5780f029 100644 --- a/feature/make-transfer/src/main/res/drawable/feature_make_transfer_ic_close.xml +++ b/feature/make-transfer/src/main/res/drawable/feature_make_transfer_ic_close.xml @@ -1,3 +1,13 @@ + + + + + diff --git a/feature/make-transfer/src/main/res/values/colors.xml b/feature/make-transfer/src/main/res/values/colors.xml index 38f0d007..94c23678 100644 --- a/feature/make-transfer/src/main/res/values/colors.xml +++ b/feature/make-transfer/src/main/res/values/colors.xml @@ -1,4 +1,13 @@ + #303F9F #DE000000 diff --git a/feature/make-transfer/src/main/res/values/strings.xml b/feature/make-transfer/src/main/res/values/strings.xml index 0a9915d5..b5080ec9 100644 --- a/feature/make-transfer/src/main/res/values/strings.xml +++ b/feature/make-transfer/src/main/res/values/strings.xml @@ -1,3 +1,13 @@ + + Loading Send money diff --git a/feature/make-transfer/src/test/java/org/mifospay/feature/make/transfer/ExampleUnitTest.kt b/feature/make-transfer/src/test/java/org/mifospay/feature/make/transfer/ExampleUnitTest.kt deleted file mode 100644 index 9961f6fc..00000000 --- a/feature/make-transfer/src/test/java/org/mifospay/feature/make/transfer/ExampleUnitTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.mifospay.feature.make.transfer - -import org.junit.Assert.assertEquals -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/feature/merchants/src/main/kotlin/org/mifospay/feature/merchants/MerchantTransferViewModel.kt b/feature/merchants/src/main/kotlin/org/mifospay/feature/merchants/MerchantTransferViewModel.kt index acbe41dd..d3a44d05 100644 --- a/feature/merchants/src/main/kotlin/org/mifospay/feature/merchants/MerchantTransferViewModel.kt +++ b/feature/merchants/src/main/kotlin/org/mifospay/feature/merchants/MerchantTransferViewModel.kt @@ -18,8 +18,8 @@ import org.mifospay.core.data.domain.usecase.account.FetchAccountTransfer import org.mifospay.core.data.repository.local.LocalRepository import org.mifospay.core.data.util.Constants.FETCH_ACCOUNT_TRANSFER_USECASE import org.mifospay.core.datastore.PreferencesHelper -import org.mifospay.feature.HistoryContract -import org.mifospay.feature.TransactionsHistory +import org.mifospay.core.data.domain.usecase.history.HistoryContract +import org.mifospay.core.data.domain.usecase.history.TransactionsHistory import javax.inject.Inject @HiltViewModel diff --git a/feature/payments/src/main/kotlin/org/mifospay/feature/payments/PaymentsScreen.kt b/feature/payments/src/main/kotlin/org/mifospay/feature/payments/PaymentsScreen.kt index 97a315c7..c631cc77 100644 --- a/feature/payments/src/main/kotlin/org/mifospay/feature/payments/PaymentsScreen.kt +++ b/feature/payments/src/main/kotlin/org/mifospay/feature/payments/PaymentsScreen.kt @@ -26,7 +26,7 @@ fun PaymentsRoute( viewReceipt: (String) -> Unit, onAccountClicked: (String, ArrayList) -> Unit, proceedWithMakeTransferFlow: (String, String) -> Unit, - navigateToInvoiceDetailScreen:(Uri) -> Unit + navigateToInvoiceDetailScreen: (Uri) -> Unit, ) { val vpa by viewModel.vpa.collectAsStateWithLifecycle() PaymentScreenContent( @@ -36,7 +36,7 @@ fun PaymentsRoute( onAccountClicked = onAccountClicked, viewReceipt = viewReceipt, proceedWithMakeTransferFlow = proceedWithMakeTransferFlow, - navigateToInvoiceDetailScreen = navigateToInvoiceDetailScreen + navigateToInvoiceDetailScreen = navigateToInvoiceDetailScreen, ) } @@ -48,17 +48,14 @@ fun PaymentScreenContent( viewReceipt: (String) -> Unit, onAccountClicked: (String, ArrayList) -> Unit, proceedWithMakeTransferFlow: (String, String) -> Unit, - navigateToInvoiceDetailScreen:(Uri) -> Unit + navigateToInvoiceDetailScreen: (Uri) -> Unit, ) { - - val pagerState = rememberPagerState(initialPage = 0) - val tabContents = listOf( TabContent(PaymentsScreenContents.SEND.name) { SendScreenRoute( onBackClick = {}, showToolBar = false, - proceedWithMakeTransferFlow = proceedWithMakeTransferFlow + proceedWithMakeTransferFlow = proceedWithMakeTransferFlow, ) }, TabContent(PaymentsScreenContents.REQUEST.name) { @@ -67,7 +64,7 @@ fun PaymentScreenContent( TabContent(PaymentsScreenContents.HISTORY.name) { HistoryScreen( accountClicked = onAccountClicked, - viewReceipt = viewReceipt + viewReceipt = viewReceipt, ) }, TabContent(PaymentsScreenContents.SI.name) { @@ -75,13 +72,18 @@ fun PaymentScreenContent( }, TabContent(PaymentsScreenContents.INVOICES.name) { InvoiceScreenRoute( - navigateToInvoiceDetailScreen = navigateToInvoiceDetailScreen + navigateToInvoiceDetailScreen = navigateToInvoiceDetailScreen, ) - } + }, ) + val pagerState = rememberPagerState(initialPage = 0) + Column(modifier = Modifier.fillMaxSize()) { - MifosScrollableTabRow(tabContents = tabContents, pagerState = pagerState) + MifosScrollableTabRow( + tabContents = tabContents, + pagerState = pagerState, + ) } } diff --git a/feature/profile/src/main/kotlin/org/mifospay/feature/profile/edit/EditProfileScreen.kt b/feature/profile/src/main/kotlin/org/mifospay/feature/profile/edit/EditProfileScreen.kt index 096db8cd..bc76b503 100644 --- a/feature/profile/src/main/kotlin/org/mifospay/feature/profile/edit/EditProfileScreen.kt +++ b/feature/profile/src/main/kotlin/org/mifospay/feature/profile/edit/EditProfileScreen.kt @@ -69,14 +69,14 @@ import java.util.Locale fun EditProfileScreenRoute( viewModel: EditProfileViewModel = hiltViewModel(), onBackClick: () -> Unit, - getUri:(context: Context, file: File) -> Uri + getUri: (context: Context, file: File) -> Uri, ) { val editProfileUiState by viewModel.editProfileUiState.collectAsStateWithLifecycle() val updateSuccess by viewModel.updateSuccess.collectAsStateWithLifecycle() val context = LocalContext.current val file = createImageFile(context) - val uri = getUri(context,file) + val uri = getUri(context, file) LaunchedEffect(key1 = true) { viewModel.fetchProfileDetails() @@ -92,7 +92,7 @@ fun EditProfileScreenRoute( viewModel.updateMobile(mobile) }, updateSuccess = updateSuccess, - uri + uri, ) } @@ -103,12 +103,12 @@ fun EditProfileScreen( updateEmail: (String) -> Unit, updateMobile: (String) -> Unit, updateSuccess: Boolean, - uri: Uri? + uri: Uri?, ) { var showDiscardChangesDialog by rememberSaveable { mutableStateOf(false) } Box( modifier = Modifier - .fillMaxSize() + .fillMaxSize(), ) { MifosScaffold( topBarTitle = R.string.feature_profile_edit_profile, @@ -118,7 +118,7 @@ fun EditProfileScreen( EditProfileUiState.Loading -> { MfLoadingWheel( contentDesc = stringResource(R.string.feature_profile_loading), - backgroundColor = MaterialTheme.colorScheme.surface + backgroundColor = MaterialTheme.colorScheme.surface, ) } @@ -138,11 +138,12 @@ fun EditProfileScreen( updateMobile = updateMobile, contentPadding = it, onBackClick = onBackClick, - updateSuccess = updateSuccess + updateSuccess = updateSuccess, ) } } - }) + }, + ) MifosDialogBox( showDialogState = showDiscardChangesDialog, @@ -153,7 +154,7 @@ fun EditProfileScreen( showDiscardChangesDialog = false onBackClick.invoke() }, - dismissButtonText = R.string.feature_profile_dismiss_text + dismissButtonText = R.string.feature_profile_dismiss_text, ) } } @@ -169,7 +170,7 @@ fun EditProfileScreenContent( updateEmail: (String) -> Unit, updateMobile: (String) -> Unit, updateSuccess: Boolean, - onBackClick: () -> Unit + onBackClick: () -> Unit, ) { var username by rememberSaveable { mutableStateOf(initialUsername) } var mobile by rememberSaveable { mutableStateOf(initialMobile) } @@ -182,13 +183,13 @@ fun EditProfileScreenContent( PermissionBox( requiredPermissions = if (Build.VERSION.SDK_INT >= 33) { listOf( - Manifest.permission.CAMERA + Manifest.permission.CAMERA, ) } else { listOf( Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE + Manifest.permission.WRITE_EXTERNAL_STORAGE, ) }, title = R.string.feature_profile_permission_required, @@ -202,7 +203,7 @@ fun EditProfileScreenContent( } val galleryLauncher = rememberLauncherForActivityResult( - contract = ActivityResultContracts.GetContent() + contract = ActivityResultContracts.GetContent(), ) { uri: Uri? -> uri?.let { imageUri = uri @@ -213,45 +214,49 @@ fun EditProfileScreenContent( MifosBottomSheet( content = { EditProfileBottomSheetContent( - { - cameraLauncher.launch(uri) + onClickProfilePicture = { + if (uri != null) { + cameraLauncher.launch(uri) + } showBottomSheet = false }, - { + onChangeProfilePicture = { galleryLauncher.launch("image/*") showBottomSheet = false }, - { + onRemoveProfilePicture = { imageUri = null showBottomSheet = false - }) + }, + ) }, - onDismiss = { showBottomSheet = false } + onDismiss = { showBottomSheet = false }, ) } - } + }, ) Box( modifier = Modifier .padding(contentPadding) - .fillMaxSize() + .fillMaxSize(), ) { Column( modifier = Modifier .fillMaxSize() .background(color = MaterialTheme.colorScheme.surface) - .verticalScroll(rememberScrollState()) + .verticalScroll(rememberScrollState()), ) { EditProfileScreenImage( imageUri = imageUri, - onCameraIconClick = { showBottomSheet = true }) + onCameraIconClick = { showBottomSheet = true }, + ) MfOutlinedTextField( modifier = Modifier .fillMaxWidth() .padding(start = 16.dp, end = 16.dp), value = username, label = stringResource(id = R.string.feature_profile_username), - onValueChange = { username = it } + onValueChange = { username = it }, ) MfOutlinedTextField( modifier = Modifier @@ -259,7 +264,7 @@ fun EditProfileScreenContent( .padding(start = 16.dp, end = 16.dp), value = email, label = stringResource(id = R.string.feature_profile_email), - onValueChange = { email = it } + onValueChange = { email = it }, ) MfOutlinedTextField( modifier = Modifier @@ -267,12 +272,12 @@ fun EditProfileScreenContent( .padding(start = 16.dp, end = 16.dp), value = vpa, label = stringResource(id = R.string.feature_profile_vpa), - onValueChange = { vpa = it } + onValueChange = { vpa = it }, ) Box( modifier = Modifier .fillMaxWidth() - .padding(start = 16.dp, end = 16.dp) + .padding(start = 16.dp, end = 16.dp), ) { val keyboardController = LocalSoftwareKeyboardController.current if (LocalInspectionMode.current) { @@ -292,7 +297,7 @@ fun EditProfileScreenContent( } }, label = { Text(stringResource(id = R.string.feature_profile_phone_number)) }, - keyboardActions = KeyboardActions { keyboardController?.hide() } + keyboardActions = KeyboardActions { keyboardController?.hide() }, ) } } @@ -309,11 +314,15 @@ fun EditProfileScreenContent( // same behaviour as onBackPress, hence reused the callback onBackClick.invoke() } else { - Toast.makeText(context, R.string.feature_profile_failed_to_save_changes, Toast.LENGTH_SHORT) + Toast.makeText( + context, + R.string.feature_profile_failed_to_save_changes, + Toast.LENGTH_SHORT, + ) .show() } }, - buttonText = R.string.feature_profile_save + buttonText = R.string.feature_profile_save, ) } } @@ -327,12 +336,12 @@ private fun isDataSaveNecessary(input: String, initialInput: String): Boolean { fun EditProfileBottomSheetContent( onClickProfilePicture: () -> Unit, onChangeProfilePicture: () -> Unit, - onRemoveProfilePicture: () -> Unit + onRemoveProfilePicture: () -> Unit, ) { Column( modifier = Modifier .background(MaterialTheme.colorScheme.surface) - .padding(top = 8.dp, bottom = 12.dp) + .padding(top = 8.dp, bottom = 12.dp), ) { Row( modifier = Modifier @@ -340,12 +349,12 @@ fun EditProfileBottomSheetContent( .padding(12.dp) .clickable { onClickProfilePicture.invoke() }, horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Icon(imageVector = Camera, contentDescription = null) Text( text = stringResource(id = R.string.feature_profile_click_profile_picture), - style = historyItemTextStyle + style = historyItemTextStyle, ) } Row( @@ -354,12 +363,12 @@ fun EditProfileBottomSheetContent( .padding(12.dp) .clickable { onChangeProfilePicture.invoke() }, horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Icon(imageVector = PhotoLibrary, contentDescription = null) Text( text = stringResource(id = R.string.feature_profile_change_profile_picture), - style = historyItemTextStyle + style = historyItemTextStyle, ) } Row( @@ -368,12 +377,12 @@ fun EditProfileBottomSheetContent( .padding(12.dp) .clickable { onRemoveProfilePicture.invoke() }, horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Icon(imageVector = Delete, contentDescription = null) Text( text = stringResource(id = R.string.feature_profile_remove_profile_picture), - style = historyItemTextStyle + style = historyItemTextStyle, ) } } @@ -388,11 +397,11 @@ fun EditProfileSaveButton(onClick: () -> Unit, buttonText: Int) { .fillMaxWidth() .padding(16.dp), shape = RoundedCornerShape(10.dp), - contentPadding = PaddingValues(12.dp) + contentPadding = PaddingValues(12.dp), ) { Text( text = stringResource(id = buttonText), - style = styleMedium16sp.copy(MaterialTheme.colorScheme.onPrimary) + style = styleMedium16sp.copy(MaterialTheme.colorScheme.onPrimary), ) } } @@ -403,7 +412,7 @@ fun createImageFile(context: Context): File { return File.createTempFile( "JPEG_${timeStamp}_", ".jpg", - storageDir + storageDir, ) } @@ -417,8 +426,8 @@ class EditProfilePreviewProvider : PreviewParameterProvider username = "John", email = "john@mifos.org", vpa = "vpa", - mobile = "+1 55557772901" - ) + mobile = "+1 55557772901", + ), ) } @@ -426,7 +435,7 @@ class EditProfilePreviewProvider : PreviewParameterProvider @Preview(showBackground = true, showSystemUi = true) @Composable private fun EditProfileScreenPreview( - @PreviewParameter(EditProfilePreviewProvider::class) editProfileUiState: EditProfileUiState + @PreviewParameter(EditProfilePreviewProvider::class) editProfileUiState: EditProfileUiState, ) { MifosTheme { EditProfileScreen( @@ -435,7 +444,7 @@ private fun EditProfileScreenPreview( updateEmail = {}, updateMobile = {}, updateSuccess = false, - uri = null + uri = null, ) } } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 73c6adef..0a9b3852 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,37 +1,39 @@ [versions] accompanistPagerVersion = "0.34.0" androidPdfViewerVersion = "2.8.2" -appcompatVersion = "1.6.1" -androidGradlePlugin = "8.4.0" +appcompatVersion = "1.7.0" +androidGradlePlugin = "8.5.1" checkstyleVersion = "10.16.0" compileSdk = "34" constraintlayoutVersion = "2.1.4" swiperefreshLayoutVersion = "1.1.0" -coreKtxVersion = "1.12.0" +coreKtxVersion = "1.13.1" credentialsVersion = "1.2.2" googleidVersion = "1.1.1" materialNavigationVersion = "1.7.0-alpha04" minSdk = "24" playServicesAuthVersion = "21.2.0" targetSdk = "34" -androidTools = "31.4.0" -androidxActivity = "1.8.2" -androidxComposeBom = "2024.02.02" -androidxComposeCompiler = "1.5.10" +androidTools = "31.5.1" +androidxActivity = "1.9.1" +androidxComposeBom = "2024.06.00" +androidxComposeCompiler = "1.5.15" +androidxComposeUi = "1.6.8" +androidxComposeRuntime = "1.6.8" androidxCore = "1.10.1" androidxHilt = "1.2.0" -androidxLifecycle = "2.7.0" -hilt = "2.51" +androidxLifecycle = "2.8.4" +hilt = "2.51.1" junit = "4.13.2" -kotlin = "1.9.22" -ksp = "1.9.22-1.0.18" -firebaseCrashlyticsPlugin = "2.9.9" -gmsPlugin = "4.4.1" +kotlin = "1.9.25" +ksp = "1.9.25-1.0.20" +firebaseCrashlyticsPlugin = "3.0.2" +gmsPlugin = "4.4.2" butterknifePlugin = "10.2.3" secrets = "2.0.1" -lifecycleVersion = "2.7.0" +lifecycleVersion = "2.8.4" lifecycleExtensionsVersion = "2.2.0" -activityVersion = "1.8.2" +activityVersion = "1.9.1" fragmentVersion = "1.6.2" retrofitVersion = "2.9.0" okHttp3Version = "4.12.0" @@ -44,22 +46,22 @@ truth = "1.4.2" dependencyGuard = "0.4.3" room = "2.6.1" roborazzi = "1.7.0" -googleOss = "17.0.1" +googleOss = "17.1.0" googleOssPlugin = "0.10.6" -firebaseBom = "32.7.4" +firebaseBom = "33.1.2" androidDesugarJdkLibs = "2.0.4" -androidx-test-ext-junit = "1.1.5" -espresso-core = "3.5.1" +androidx-test-ext-junit = "1.2.1" +espresso-core = "3.6.1" material = "1.11.0" kotlinxDatetime = "0.5.0" kotlinxSerializationJson = "1.6.3" retrofitKotlinxSerializationJson = "1.0.0" -kotlinxCoroutines = "1.8.0" +kotlinxCoroutines = "1.8.1" compose_country_code_picker = "2.2.8" sheets_compose_dialogs_core = "1.3.0" protobuf = "3.25.2" protobufPlugin = "0.9.4" -androidxDataStore = "1.0.0" +androidxDataStore = "1.1.1" detekt = "1.23.5" twitter-detekt-compose = "0.0.26" androidxComposeMaterial3Adaptive = "1.0.0-alpha08" @@ -79,6 +81,7 @@ zxingAndroidEmbeddedVersion = "4.2.0" zxingVersion = "1.9.13" cameraViewVersion = "1.3.4" cameraLifecycleVersion = "1.3.4" +spotlessVersion = "6.23.3" [libraries] accompanist-pager = { module = "com.google.accompanist:accompanist-pager", version.ref = "accompanistPagerVersion" } @@ -86,7 +89,7 @@ android-pdf-viewer = { module = "com.github.barteksc:android-pdf-viewer", versio androidx-activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityVersion" } androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompatVersion" } androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayoutVersion" } -androidx-swiperefresh-layout = {module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref="swiperefreshLayoutVersion"} +androidx-swiperefresh-layout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshLayoutVersion" } androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtxVersion" } androidx-credentials = { module = "androidx.credentials:credentials", version.ref = "credentialsVersion" } androidx-credentials-play-services-auth = { module = "androidx.credentials:credentials-play-services-auth", version.ref = "credentialsVersion" } @@ -100,23 +103,23 @@ androidx-compose-material3-adaptive-layout = { group = "androidx.compose.materia androidx-compose-material3-adaptive-navigation = { group = "androidx.compose.material3.adaptive", name = "adaptive-navigation", version.ref = "androidxComposeMaterial3Adaptive" } androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" } androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout" } -androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3"} +androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" } compose-material = { group = "androidx.compose.material", name = "material", version.ref = "composeMaterial" } androidx-compose-material-iconsExtended = { group = "androidx.compose.material", name = "material-icons-extended" } androidx-compose-material3-windowSizeClass = { group = "androidx.compose.material3", name = "material3-window-size-class" } -androidx-compose-ui = { group = "androidx.compose.ui", name = "ui"} -androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test-junit4"} -androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest"} -androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling"} -androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime" } +androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref="androidxComposeUi" } +androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test-junit4" } +androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } +androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } +androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime", version.ref="androidxComposeRuntime" } androidx-material-navigation = { module = "androidx.compose.material:material-navigation", version.ref = "materialNavigationVersion" } checkstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "checkstyleVersion" } googleid = { module = "com.google.android.libraries.identity.googleid:googleid", version.ref = "googleidVersion" } hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } hilt-android-testing = { group = "com.google.dagger", name = "hilt-android-testing", version.ref = "hilt" } hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } -androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview"} -androidx-compose-ui-util = { group = "androidx.compose.ui", name = "ui-util"} +androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } +androidx-compose-ui-util = { group = "androidx.compose.ui", name = "ui-util", version.ref="androidxComposeUi" } androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "androidxHilt" } androidx-lifecycle-runtimeCompose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidxLifecycle" } androidx-lifecycle-viewModelCompose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" } @@ -134,7 +137,7 @@ squareup-logging-interceptor = { module = "com.squareup.okhttp3:logging-intercep reactivex-rxjava-android = { module = "io.reactivex:rxandroid", version.ref = "rxandroidVersion" } reactivex-rxjava = { module = "io.reactivex:rxjava", version.ref = "rxjavaVersion" } truth = { group = "com.google.truth", name = "truth", version.ref = "truth" } -junit = { module = "junit:junit", version.ref = "junitVersion"} +junit = { module = "junit:junit", version.ref = "junitVersion" } google-oss-licenses = { group = "com.google.android.gms", name = "play-services-oss-licenses", version.ref = "googleOss" } google-oss-licenses-plugin = { group = "com.google.android.gms", name = "oss-licenses-plugin", version.ref = "googleOssPlugin" } jetbrains-kotlin-jdk7 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlin" } @@ -155,8 +158,8 @@ androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } compose-country-code-picker = { group = "com.github.jump-sdk", name = "jetpack_compose_country_code_picker_emoji", version.ref = "compose_country_code_picker" } -sheets-compose-dialogs-core = { group = "com.maxkeppeler.sheets-compose-dialogs",name = "core",version.ref = "sheets_compose_dialogs_core"} -sheets-compose-dialogs-calender = { group = "com.maxkeppeler.sheets-compose-dialogs",name = "calendar",version.ref = "sheets_compose_dialogs_core"} +sheets-compose-dialogs-core = { group = "com.maxkeppeler.sheets-compose-dialogs", name = "core", version.ref = "sheets_compose_dialogs_core" } +sheets-compose-dialogs-calender = { group = "com.maxkeppeler.sheets-compose-dialogs", name = "calendar", version.ref = "sheets_compose_dialogs_core" } protobuf-kotlin-lite = { group = "com.google.protobuf", name = "protobuf-kotlin-lite", version.ref = "protobuf" } protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" } androidx-dataStore-core = { group = "androidx.datastore", name = "datastore", version.ref = "androidxDataStore" } @@ -175,9 +178,9 @@ google-play-services-code-scanner = { group = "com.google.android.gms", name = " mifosPasscode = { group = "com.mifos.mobile", name = "mifos-passcode", version.ref = "mifosPasscode" } #Detekt -detekt-gradlePlugin = { group = "io.gitlab.arturbosch.detekt", name="detekt-gradle-plugin", version.ref="detekt"} -detekt-formatting = { group = "io.gitlab.arturbosch.detekt", name="detekt-formatting", version.ref="detekt"} -twitter-detekt-compose = { group="com.twitter.compose.rules", name="detekt", version.ref="twitter-detekt-compose" } +detekt-gradlePlugin = { group = "io.gitlab.arturbosch.detekt", name = "detekt-gradle-plugin", version.ref = "detekt" } +detekt-formatting = { group = "io.gitlab.arturbosch.detekt", name = "detekt-formatting", version.ref = "detekt" } +twitter-detekt-compose = { group = "com.twitter.compose.rules", name = "detekt", version.ref = "twitter-detekt-compose" } # Dependencies of the included build-logic android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } @@ -191,6 +194,8 @@ zxing = { module = "me.dm7.barcodescanner:zxing", version.ref = "zxingVersion" } zxing-android-embedded = { module = "com.journeyapps:zxing-android-embedded", version.ref = "zxingAndroidEmbeddedVersion" } androidx-camera-view = { group = "androidx.camera", name = "camera-view", version.ref = "cameraViewVersion" } androidx-camera-lifecycle = { group = "androidx.camera", name = "camera-lifecycle", version.ref = "cameraLifecycleVersion" } +spotless-gradle = { group = "com.diffplug.spotless", name = "spotless-plugin-gradle", version.ref = "spotlessVersion" } + [plugins] android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } @@ -208,12 +213,13 @@ kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" } -ksp = { id = "com.google.devtools.ksp", version.ref = "ksp"} +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } room = { id = "androidx.room", version.ref = "room" } protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" } -detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt"} -detekt-compiler = {id = "io.github.detekt.gradle.compiler-plugin", version.ref="detekt"} +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } +detekt-compiler = { id = "io.github.detekt.gradle.compiler-plugin", version.ref = "detekt" } module-graph = { id = "com.jraska.module.graph.assertion", version.ref = "moduleGraph" } +spotless = { id = "com.diffplug.spotless", version.ref = "spotlessVersion" } # Plugins defined by this project mifospay-android-application = { id = "mifospay.android.application", version = "unspecified" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a78a50f1..c73d0c43 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ #Fri Mar 11 18:59:41 IST 2022 distributionBase=GRADLE_USER_HOME # When updating gradle version don't forget to update in $Dockerfile -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/mifospay/dependencies/prodReleaseRuntimeClasspath.txt b/mifospay/dependencies/prodReleaseRuntimeClasspath.txt index 96507c85..61800b5d 100644 --- a/mifospay/dependencies/prodReleaseRuntimeClasspath.txt +++ b/mifospay/dependencies/prodReleaseRuntimeClasspath.txt @@ -1,11 +1,11 @@ -androidx.activity:activity-compose:1.8.2 -androidx.activity:activity-ktx:1.8.2 -androidx.activity:activity:1.8.2 +androidx.activity:activity-compose:1.9.1 +androidx.activity:activity-ktx:1.9.1 +androidx.activity:activity:1.9.1 androidx.annotation:annotation-experimental:1.4.0 -androidx.annotation:annotation-jvm:1.7.1 -androidx.annotation:annotation:1.7.1 -androidx.appcompat:appcompat-resources:1.6.1 -androidx.appcompat:appcompat:1.6.1 +androidx.annotation:annotation-jvm:1.8.0 +androidx.annotation:annotation:1.8.0 +androidx.appcompat:appcompat-resources:1.7.0 +androidx.appcompat:appcompat:1.7.0 androidx.arch.core:core-common:2.2.0 androidx.arch.core:core-runtime:2.2.0 androidx.asynclayoutinflater:asynclayoutinflater:1.0.0 @@ -67,28 +67,34 @@ androidx.compose.ui:ui-unit:1.7.0-alpha04 androidx.compose.ui:ui-util-android:1.7.0-alpha04 androidx.compose.ui:ui-util:1.7.0-alpha04 androidx.compose.ui:ui:1.7.0-alpha04 -androidx.compose:compose-bom:2024.02.02 +androidx.compose:compose-bom:2024.06.00 androidx.concurrent:concurrent-futures:1.1.0 androidx.constraintlayout:constraintlayout-core:1.0.4 androidx.constraintlayout:constraintlayout:2.1.4 androidx.coordinatorlayout:coordinatorlayout:1.1.0 -androidx.core:core-ktx:1.12.0 +androidx.core:core-ktx:1.13.1 androidx.core:core-splashscreen:1.0.1 -androidx.core:core:1.12.0 +androidx.core:core:1.13.1 androidx.credentials:credentials-play-services-auth:1.3.0-beta01 androidx.credentials:credentials:1.3.0-beta01 androidx.cursoradapter:cursoradapter:1.0.0 androidx.customview:customview-poolingcontainer:1.0.0 androidx.customview:customview:1.1.0 -androidx.databinding:databinding-adapters:8.4.0 -androidx.databinding:databinding-common:8.4.0 -androidx.databinding:databinding-ktx:8.4.0 -androidx.databinding:databinding-runtime:8.4.0 -androidx.databinding:viewbinding:8.4.0 -androidx.datastore:datastore-core:1.0.0 -androidx.datastore:datastore-preferences-core:1.0.0 -androidx.datastore:datastore-preferences:1.0.0 -androidx.datastore:datastore:1.0.0 +androidx.databinding:databinding-adapters:8.5.1 +androidx.databinding:databinding-common:8.5.1 +androidx.databinding:databinding-ktx:8.5.1 +androidx.databinding:databinding-runtime:8.5.1 +androidx.databinding:viewbinding:8.5.1 +androidx.datastore:datastore-android:1.1.1 +androidx.datastore:datastore-core-android:1.1.1 +androidx.datastore:datastore-core-okio-jvm:1.1.1 +androidx.datastore:datastore-core-okio:1.1.1 +androidx.datastore:datastore-core:1.1.1 +androidx.datastore:datastore-preferences-android:1.1.1 +androidx.datastore:datastore-preferences-core-jvm:1.1.1 +androidx.datastore:datastore-preferences-core:1.1.1 +androidx.datastore:datastore-preferences:1.1.1 +androidx.datastore:datastore:1.1.1 androidx.documentfile:documentfile:1.0.0 androidx.drawerlayout:drawerlayout:1.1.1 androidx.dynamicanimation:dynamicanimation:1.0.0 @@ -104,21 +110,27 @@ androidx.interpolator:interpolator:1.0.0 androidx.legacy:legacy-support-core-ui:1.0.0 androidx.legacy:legacy-support-core-utils:1.0.0 androidx.legacy:legacy-support-v4:1.0.0 -androidx.lifecycle:lifecycle-common-java8:2.7.0 -androidx.lifecycle:lifecycle-common:2.7.0 +androidx.lifecycle:lifecycle-common-java8:2.8.4 +androidx.lifecycle:lifecycle-common-jvm:2.8.4 +androidx.lifecycle:lifecycle-common:2.8.4 androidx.lifecycle:lifecycle-extensions:2.2.0 -androidx.lifecycle:lifecycle-livedata-core-ktx:2.7.0 -androidx.lifecycle:lifecycle-livedata-core:2.7.0 -androidx.lifecycle:lifecycle-livedata:2.7.0 -androidx.lifecycle:lifecycle-process:2.7.0 -androidx.lifecycle:lifecycle-runtime-compose:2.7.0 -androidx.lifecycle:lifecycle-runtime-ktx:2.7.0 -androidx.lifecycle:lifecycle-runtime:2.7.0 -androidx.lifecycle:lifecycle-service:2.7.0 -androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0 -androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0 -androidx.lifecycle:lifecycle-viewmodel-savedstate:2.7.0 -androidx.lifecycle:lifecycle-viewmodel:2.7.0 +androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4 +androidx.lifecycle:lifecycle-livedata-core:2.8.4 +androidx.lifecycle:lifecycle-livedata:2.8.4 +androidx.lifecycle:lifecycle-process:2.8.4 +androidx.lifecycle:lifecycle-runtime-android:2.8.4 +androidx.lifecycle:lifecycle-runtime-compose-android:2.8.4 +androidx.lifecycle:lifecycle-runtime-compose:2.8.4 +androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.4 +androidx.lifecycle:lifecycle-runtime-ktx:2.8.4 +androidx.lifecycle:lifecycle-runtime:2.8.4 +androidx.lifecycle:lifecycle-service:2.8.4 +androidx.lifecycle:lifecycle-viewmodel-android:2.8.4 +androidx.lifecycle:lifecycle-viewmodel-compose-android:2.8.4 +androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 +androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4 +androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4 +androidx.lifecycle:lifecycle-viewmodel:2.8.4 androidx.loader:loader:1.1.0 androidx.localbroadcastmanager:localbroadcastmanager:1.0.0 androidx.media:media:1.0.0 @@ -155,62 +167,62 @@ androidx.window:window:1.3.0-alpha02 com.github.jump-sdk:jetpack_compose_country_code_picker_emoji:2.2.8 com.google.accompanist:accompanist-drawablepainter:0.32.0 com.google.accompanist:accompanist-pager:0.34.0 -com.google.android.datatransport:transport-api:3.1.0 -com.google.android.datatransport:transport-backend-cct:3.1.9 -com.google.android.datatransport:transport-runtime:3.1.9 +com.google.android.datatransport:transport-api:3.2.0 +com.google.android.datatransport:transport-backend-cct:3.3.0 +com.google.android.datatransport:transport-runtime:3.3.0 com.google.android.gms:play-services-ads-identifier:18.0.0 com.google.android.gms:play-services-auth-api-phone:18.0.2 com.google.android.gms:play-services-auth-base:18.0.10 com.google.android.gms:play-services-auth:21.2.0 com.google.android.gms:play-services-base:18.3.0 -com.google.android.gms:play-services-basement:18.3.0 -com.google.android.gms:play-services-cloud-messaging:17.1.0 +com.google.android.gms:play-services-basement:18.4.0 +com.google.android.gms:play-services-cloud-messaging:17.2.0 com.google.android.gms:play-services-code-scanner:16.1.0 com.google.android.gms:play-services-fido:21.0.0 -com.google.android.gms:play-services-measurement-api:21.5.1 -com.google.android.gms:play-services-measurement-base:21.5.1 -com.google.android.gms:play-services-measurement-impl:21.5.1 -com.google.android.gms:play-services-measurement-sdk-api:21.5.1 -com.google.android.gms:play-services-measurement-sdk:21.5.1 -com.google.android.gms:play-services-measurement:21.5.1 +com.google.android.gms:play-services-measurement-api:22.0.2 +com.google.android.gms:play-services-measurement-base:22.0.2 +com.google.android.gms:play-services-measurement-impl:22.0.2 +com.google.android.gms:play-services-measurement-sdk-api:22.0.2 +com.google.android.gms:play-services-measurement-sdk:22.0.2 +com.google.android.gms:play-services-measurement:22.0.2 com.google.android.gms:play-services-stats:17.0.2 -com.google.android.gms:play-services-tasks:18.1.0 +com.google.android.gms:play-services-tasks:18.2.0 com.google.android.libraries.identity.googleid:googleid:1.1.1 com.google.android.material:material:1.11.0 com.google.android.odml:image:1.0.0-beta1 com.google.auto.value:auto-value-annotations:1.6.3 com.google.code.findbugs:jsr305:3.0.2 com.google.code.gson:gson:2.8.5 -com.google.dagger:dagger-lint-aar:2.51 -com.google.dagger:dagger:2.51 -com.google.dagger:hilt-android:2.51 -com.google.dagger:hilt-core:2.51 -com.google.errorprone:error_prone_annotations:2.15.0 +com.google.dagger:dagger-lint-aar:2.51.1 +com.google.dagger:dagger:2.51.1 +com.google.dagger:hilt-android:2.51.1 +com.google.dagger:hilt-core:2.51.1 +com.google.errorprone:error_prone_annotations:2.26.0 com.google.firebase:firebase-abt:21.1.1 -com.google.firebase:firebase-analytics-ktx:21.5.1 -com.google.firebase:firebase-analytics:21.5.1 +com.google.firebase:firebase-analytics-ktx:22.0.2 +com.google.firebase:firebase-analytics:22.0.2 com.google.firebase:firebase-annotations:16.2.0 -com.google.firebase:firebase-bom:32.7.4 -com.google.firebase:firebase-common-ktx:20.4.2 -com.google.firebase:firebase-common:20.4.2 -com.google.firebase:firebase-components:17.1.5 +com.google.firebase:firebase-bom:33.1.2 +com.google.firebase:firebase-common-ktx:21.0.0 +com.google.firebase:firebase-common:21.0.0 +com.google.firebase:firebase-components:18.0.0 com.google.firebase:firebase-config-interop:16.0.1 -com.google.firebase:firebase-config:21.6.3 -com.google.firebase:firebase-crashlytics-ktx:18.6.2 -com.google.firebase:firebase-crashlytics:18.6.2 -com.google.firebase:firebase-datatransport:18.1.8 +com.google.firebase:firebase-config:22.0.0 +com.google.firebase:firebase-crashlytics-ktx:19.0.3 +com.google.firebase:firebase-crashlytics:19.0.3 +com.google.firebase:firebase-datatransport:19.0.0 com.google.firebase:firebase-encoders-json:18.0.1 com.google.firebase:firebase-encoders-proto:16.0.0 com.google.firebase:firebase-encoders:17.0.0 com.google.firebase:firebase-iid-interop:17.1.0 -com.google.firebase:firebase-installations-interop:17.1.1 -com.google.firebase:firebase-installations:17.2.0 -com.google.firebase:firebase-measurement-connector:19.0.0 -com.google.firebase:firebase-messaging-ktx:23.4.1 -com.google.firebase:firebase-messaging:23.4.1 -com.google.firebase:firebase-perf-ktx:20.5.2 -com.google.firebase:firebase-perf:20.5.2 -com.google.firebase:firebase-sessions:1.2.2 +com.google.firebase:firebase-installations-interop:17.2.0 +com.google.firebase:firebase-installations:18.0.0 +com.google.firebase:firebase-measurement-connector:20.0.1 +com.google.firebase:firebase-messaging-ktx:24.0.0 +com.google.firebase:firebase-messaging:24.0.0 +com.google.firebase:firebase-perf-ktx:21.0.1 +com.google.firebase:firebase-perf:21.0.1 +com.google.firebase:firebase-sessions:2.0.3 com.google.firebase:protolite-well-known-types:18.0.0 com.google.guava:failureaccess:1.0.1 com.google.guava:guava:31.1-android @@ -245,21 +257,21 @@ javax.inject:javax.inject:1 me.dm7.barcodescanner:core:1.9.13 me.dm7.barcodescanner:zxing:1.9.13 org.checkerframework:checker-qual:3.12.0 -org.jetbrains.kotlin:kotlin-android-extensions-runtime:1.9.22 +org.jetbrains.kotlin:kotlin-android-extensions-runtime:1.9.25 org.jetbrains.kotlin:kotlin-bom:1.8.22 -org.jetbrains.kotlin:kotlin-parcelize-runtime:1.9.22 -org.jetbrains.kotlin:kotlin-stdlib-common:1.9.22 -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.22 +org.jetbrains.kotlin:kotlin-parcelize-runtime:1.9.25 +org.jetbrains.kotlin:kotlin-stdlib-common:1.9.25 +org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.25 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.20 -org.jetbrains.kotlin:kotlin-stdlib:1.9.22 +org.jetbrains.kotlin:kotlin-stdlib:1.9.25 org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.3.7 org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 -org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0 -org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.0 -org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0 -org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0 -org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.8.0 -org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.8.0 +org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1 +org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.1 +org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.1 +org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1 +org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.8.1 +org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.8.1 org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.5.0 org.jetbrains.kotlinx:kotlinx-datetime:0.5.0 org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.3 diff --git a/mifospay/src/main/java/org/mifospay/navigation/MifosNavHost.kt b/mifospay/src/main/java/org/mifospay/navigation/MifosNavHost.kt index e5202ab1..423f00c4 100644 --- a/mifospay/src/main/java/org/mifospay/navigation/MifosNavHost.kt +++ b/mifospay/src/main/java/org/mifospay/navigation/MifosNavHost.kt @@ -12,11 +12,13 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import com.mifos.mobile.passcode.utils.PassCodeConstants import org.mifospay.common.Constants +import org.mifospay.core.ui.utility.TabContent import org.mifospay.feature.auth.navigation.loginScreen import org.mifospay.feature.auth.navigation.mobileVerificationScreen import org.mifospay.feature.auth.navigation.navigateToMobileVerification import org.mifospay.feature.auth.navigation.navigateToSignup import org.mifospay.feature.auth.navigation.signupScreen +import org.mifospay.feature.bank.accounts.AccountsScreen import org.mifospay.feature.bank.accounts.navigation.bankAccountDetailScreen import org.mifospay.feature.bank.accounts.navigation.linkBankAccountScreen import org.mifospay.feature.bank.accounts.navigation.navigateToBankAccountDetail @@ -24,11 +26,13 @@ import org.mifospay.feature.bank.accounts.navigation.navigateToLinkBankAccount import org.mifospay.feature.editpassword.navigation.editPasswordScreen import org.mifospay.feature.editpassword.navigation.navigateToEditPassword import org.mifospay.feature.faq.navigation.faqScreen +import org.mifospay.feature.finance.FinanceScreenContents import org.mifospay.feature.finance.navigation.financeScreen import org.mifospay.feature.home.navigation.HOME_ROUTE import org.mifospay.feature.home.navigation.homeScreen import org.mifospay.feature.invoices.navigation.invoiceDetailScreen import org.mifospay.feature.invoices.navigation.navigateToInvoiceDetail +import org.mifospay.feature.kyc.KYCScreen import org.mifospay.feature.kyc.navigation.kycLevel1Screen import org.mifospay.feature.kyc.navigation.kycLevel2Screen import org.mifospay.feature.kyc.navigation.kycLevel3Screen @@ -39,6 +43,7 @@ import org.mifospay.feature.kyc.navigation.navigateToKYCLevel3 import org.mifospay.feature.make.transfer.navigation.makeTransferScreen import org.mifospay.feature.make.transfer.navigation.navigateToMakeTransferScreen import org.mifospay.feature.merchants.navigation.merchantTransferScreen +import org.mifospay.feature.merchants.ui.MerchantScreen import org.mifospay.feature.passcode.PassCodeActivity import org.mifospay.feature.payments.paymentsScreen import org.mifospay.feature.profile.navigation.editProfileScreen @@ -49,8 +54,8 @@ import org.mifospay.feature.receipt.navigation.navigateToReceipt import org.mifospay.feature.receipt.navigation.receiptScreen import org.mifospay.feature.request.money.navigation.navigateToShowQrScreen import org.mifospay.feature.request.money.navigation.showQrScreen +import org.mifospay.feature.savedcards.CardsScreen import org.mifospay.feature.savedcards.navigation.addCardScreen -import org.mifospay.feature.savedcards.navigation.navigateToAddCard import org.mifospay.feature.send.money.navigation.navigateToSendMoneyScreen import org.mifospay.feature.send.money.navigation.sendMoneyScreen import org.mifospay.feature.settings.navigation.navigateToSettings @@ -79,8 +84,29 @@ fun MifosNavHost( modifier: Modifier = Modifier, startDestination: String = HOME_ROUTE, ) { - val context = LocalContext.current + val tabContents = listOf( + TabContent(FinanceScreenContents.ACCOUNTS.name) { + AccountsScreen( + navigateToBankAccountDetailScreen = navController::navigateToBankAccountDetail, + navigateToLinkBankAccountScreen = navController::navigateToLinkBankAccount, + ) + }, + TabContent(FinanceScreenContents.CARDS.name) { + CardsScreen(onEditCard = {}) + }, + TabContent(FinanceScreenContents.MERCHANTS.name) { + MerchantScreen() + }, + TabContent(FinanceScreenContents.KYC.name) { + KYCScreen( + onLevel1Clicked = navController::navigateToKYCLevel1, + onLevel2Clicked = navController::navigateToKYCLevel2, + onLevel3Clicked = navController::navigateToKYCLevel3, + ) + }, + ) + NavHost( navController = navController, startDestination = startDestination, @@ -88,7 +114,7 @@ fun MifosNavHost( ) { homeScreen( onRequest = { vpa -> navController.navigateToShowQrScreen(vpa) }, - onPay = navController::navigateToSendMoneyScreen + onPay = navController::navigateToSendMoneyScreen, ) paymentsScreen( showQr = { vpa -> navController.navigateToShowQrScreen(vpa) }, @@ -102,54 +128,45 @@ fun MifosNavHost( }, navigateToInvoiceDetailScreen = { uri -> navController.navigateToInvoiceDetail(uri.toString()) - } + }, ) financeScreen( - onAddBtn = { navController.navigateToAddCard() }, - onLevel1Clicked = { navController.navigateToKYCLevel1() }, - onLevel2Clicked = { navController.navigateToKYCLevel2() }, - onLevel3Clicked = { navController.navigateToKYCLevel3() }, - navigateToBankAccountDetailScreen = { bankAccountDetails, index -> - navController.navigateToBankAccountDetail(bankAccountDetails, index) - }, - navigateToLinkBankAccountScreen = { - navController.navigateToLinkBankAccount() - } + tabContents = tabContents, ) addCardScreen( onDismiss = navController::popBackStack, onAddCard = { // Handle adding the card navController.popBackStack() - } + }, ) profileScreen( onEditProfile = { navController.navigateToEditProfile() }, - onSettings = { navController.navigateToSettings() } + onSettings = { navController.navigateToSettings() }, ) sendMoneyScreen( onBackClick = navController::popBackStack, proceedWithMakeTransferFlow = { externalId, transferAmount -> navController.navigateToMakeTransferScreen(externalId, transferAmount) - } + }, ) makeTransferScreen( - onDismiss = navController::popBackStack + onDismiss = navController::popBackStack, ) showQrScreen( - onBackClick = navController::popBackStack + onBackClick = navController::popBackStack, ) merchantTransferScreen( proceedWithMakeTransferFlow = { externalId, transferAmount -> navController.navigateToMakeTransferScreen(externalId, transferAmount) }, - onBackPressed = navController::popBackStack + onBackPressed = navController::popBackStack, ) settingsScreen( onBackPress = navController::popBackStack, navigateToEditPasswordScreen = { navController.navigateToEditPassword() - } + }, ) kycScreen( @@ -164,19 +181,19 @@ fun MifosNavHost( onLevel3Clicked = { // Navigate to Level 3 screen navController.navigate("kyc_level_3") - } + }, ) kycLevel1Screen( navigateToKycLevel2 = { // Navigate to KYC Level 2 screen // For now, we'll just pop back to the previous screen navController.popBackStack() - } + }, ) kycLevel2Screen( onSuccessKyc2 = { navController.popBackStack() - } + }, ) kycLevel3Screen() newSiScreen(onBackClick = navController::popBackStack) @@ -189,23 +206,23 @@ fun MifosNavHost( ) faqScreen( - navigateBack = { navController.popBackStack() } + navigateBack = { navController.popBackStack() }, ) readQrScreen( - onBackClick = navController::popBackStack + onBackClick = navController::popBackStack, ) specificTransactionsScreen( onBackClick = navController::popBackStack, onTransactionItemClicked = { transactionId -> navController.navigateToReceipt(Uri.parse(Constants.RECEIPT_DOMAIN + transactionId)) - } + }, ) invoiceDetailScreen( onBackPress = { navController.popBackStack() }, navigateToReceiptScreen = { uri -> navController.navigateToReceipt(Uri.parse(Constants.RECEIPT_DOMAIN + uri)) - } + }, ) receiptScreen( onShowSnackbar = { message, action -> @@ -215,7 +232,7 @@ fun MifosNavHost( openPassCodeActivity = { uri -> context.openPassCodeActivity(uri) }, - onBackClick = navController::popBackStack + onBackClick = navController::popBackStack, ) setupUpiPinScreen() bankAccountDetailScreen( @@ -231,14 +248,14 @@ fun MifosNavHost( onBackClick = { bankAccountDetails, index -> navController.previousBackStackEntry?.savedStateHandle?.set( Constants.UPDATED_BANK_ACCOUNT, - bankAccountDetails + bankAccountDetails, ) navController.previousBackStackEntry?.savedStateHandle?.set(Constants.INDEX, index) navController.popBackStack() - } + }, ) linkBankAccountScreen( - onBackClick = { navController.popBackStack() } + onBackClick = { navController.popBackStack() }, ) editPasswordScreen( onBackPress = { @@ -246,7 +263,7 @@ fun MifosNavHost( }, onCancelChanges = { navController.popBackStack() - } + }, ) signupScreen( onLoginSuccess = { @@ -268,9 +285,9 @@ fun MifosNavHost( email = extraData[Constants.GOOGLE_EMAIL] as? String ?: "", firstName = extraData[Constants.GOOGLE_GIVEN_NAME] as? String ?: "", lastName = extraData[Constants.GOOGLE_FAMILY_NAME] as? String ?: "", - businessName = extraData[Constants.GOOGLE_DISPLAY_NAME] as? String ?: "" + businessName = extraData[Constants.GOOGLE_DISPLAY_NAME] as? String ?: "", ) - } + }, ) loginScreen( @@ -283,19 +300,20 @@ fun MifosNavHost( googleDisplayName = googleDisplayName, googleEmail = googleEmail, googleFamilyName = googleFamilyName, - googleGivenName = googleGivenName + googleGivenName = googleGivenName, ) - } + }, ) } } + fun Context.openPassCodeActivity(deepLinkURI: Uri) { PassCodeActivity.startPassCodeActivity( context = this, bundle = bundleOf( Pair("uri", deepLinkURI.toString()), - Pair(PassCodeConstants.PASSCODE_INITIAL_LOGIN, true) + Pair(PassCodeConstants.PASSCODE_INITIAL_LOGIN, true), ), ) } @@ -303,7 +321,7 @@ fun Context.openPassCodeActivity(deepLinkURI: Uri) { fun getUri(context: Context, file: File): Uri { val uri = FileProvider.getUriForFile( Objects.requireNonNull(context), - org.mifospay.BuildConfig.APPLICATION_ID + ".provider", file + org.mifospay.BuildConfig.APPLICATION_ID + ".provider", file, ) return uri } \ No newline at end of file diff --git a/spotless/copyright.kt b/spotless/copyright.kt new file mode 100644 index 00000000..d3ad4866 --- /dev/null +++ b/spotless/copyright.kt @@ -0,0 +1,9 @@ +/* + * Copyright $YEAR 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-wallet/blob/master/LICENSE.md + */ \ No newline at end of file diff --git a/spotless/copyright.kts b/spotless/copyright.kts new file mode 100644 index 00000000..d3ad4866 --- /dev/null +++ b/spotless/copyright.kts @@ -0,0 +1,9 @@ +/* + * Copyright $YEAR 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-wallet/blob/master/LICENSE.md + */ \ No newline at end of file diff --git a/spotless/copyright.xml b/spotless/copyright.xml new file mode 100644 index 00000000..4e21a6ae --- /dev/null +++ b/spotless/copyright.xml @@ -0,0 +1,10 @@ + + \ No newline at end of file