mirror of
https://github.com/openMF/mobile-wallet.git
synced 2026-02-06 11:56:48 +00:00
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
This commit is contained in:
parent
6f87da5869
commit
e45c9b6f59
7
.editorconfig
Normal file
7
.editorconfig
Normal file
@ -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
|
||||||
29
.github/workflows/master_dev_ci.yml
vendored
29
.github/workflows/master_dev_ci.yml
vendored
@ -52,20 +52,21 @@ jobs:
|
|||||||
name: mobile-wallet
|
name: mobile-wallet
|
||||||
path: mifospay/build/outputs/apk/debug/
|
path: mifospay/build/outputs/apk/debug/
|
||||||
|
|
||||||
lintCheck:
|
# Turing off detekt check until migration finished
|
||||||
name: Static Analysis
|
# lintCheck:
|
||||||
runs-on: ubuntu-latest
|
# name: Static Analysis
|
||||||
steps:
|
# runs-on: ubuntu-latest
|
||||||
- uses: actions/checkout@v2
|
# steps:
|
||||||
|
# - uses: actions/checkout@v2
|
||||||
# Setup JDK 17
|
#
|
||||||
- name: Setup JDK 17
|
# # Setup JDK 17
|
||||||
uses: actions/setup-java@v1
|
# - name: Setup JDK 17
|
||||||
with:
|
# uses: actions/setup-java@v1
|
||||||
java-version: 17
|
# with:
|
||||||
|
# java-version: 17
|
||||||
- name: Detekt For All Modules
|
#
|
||||||
run: ./gradlew detekt
|
# - name: Detekt For All Modules
|
||||||
|
# run: ./gradlew detekt
|
||||||
|
|
||||||
pmd:
|
pmd:
|
||||||
name: PMD
|
name: PMD
|
||||||
|
|||||||
@ -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.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
buildscript {
|
buildscript {
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath(libs.google.oss.licenses.plugin) {
|
classpath(libs.google.oss.licenses.plugin) {
|
||||||
exclude(group = "com.google.protobuf")
|
exclude(group = "com.google.protobuf")
|
||||||
}
|
}
|
||||||
|
classpath(libs.spotless.gradle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,11 +27,14 @@ plugins {
|
|||||||
alias(libs.plugins.kotlin.android) apply false
|
alias(libs.plugins.kotlin.android) apply false
|
||||||
alias(libs.plugins.detekt)
|
alias(libs.plugins.detekt)
|
||||||
alias(libs.plugins.detekt.compiler)
|
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 detektFormatting = libs.detekt.formatting
|
||||||
val twitterComposeRules = libs.twitter.detekt.compose
|
val twitterComposeRules = libs.twitter.detekt.compose
|
||||||
|
val ktlintVersion = "1.0.1"
|
||||||
|
|
||||||
val reportMerge by tasks.registering(io.gitlab.arturbosch.detekt.report.ReportMergeTask::class) {
|
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"
|
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 {
|
subprojects {
|
||||||
apply {
|
apply {
|
||||||
plugin("io.gitlab.arturbosch.detekt")
|
plugin("io.gitlab.arturbosch.detekt")
|
||||||
}
|
plugin("com.diffplug.spotless")
|
||||||
|
|
||||||
detekt {
|
|
||||||
config.from(rootProject.files("config/detekt/detekt.yml"))
|
|
||||||
reports.xml.required.set(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
|
tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
|
||||||
|
config.from(rootProject.files("config/detekt/detekt.yml"))
|
||||||
|
reports.xml.required.set(true)
|
||||||
finalizedBy(reportMerge)
|
finalizedBy(reportMerge)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extensions.configure<com.diffplug.gradle.spotless.SpotlessExtension> {
|
||||||
|
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 (<!--) or the xml declaration (<?xml)
|
||||||
|
licenseHeaderFile(rootProject.file("spotless/copyright.xml"), "(<[^!?])")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
reportMerge {
|
reportMerge {
|
||||||
input.from(tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().map {
|
input.from(tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().map {
|
||||||
it.htmlReportFile }
|
it.htmlReportFile }
|
||||||
|
|||||||
@ -805,7 +805,7 @@ style:
|
|||||||
NestedClassesVisibility:
|
NestedClassesVisibility:
|
||||||
active: true
|
active: true
|
||||||
NewLineAtEndOfFile:
|
NewLineAtEndOfFile:
|
||||||
active: false # Turning off for current implementation
|
active: true
|
||||||
NoTabs:
|
NoTabs:
|
||||||
active: false
|
active: false
|
||||||
NullableBooleanCheck:
|
NullableBooleanCheck:
|
||||||
@ -936,4 +936,50 @@ style:
|
|||||||
WildcardImport:
|
WildcardImport:
|
||||||
active: true
|
active: true
|
||||||
excludeImports:
|
excludeImports:
|
||||||
- "java.util.*"
|
- "java.util.*"
|
||||||
|
|
||||||
|
TwitterCompose:
|
||||||
|
CompositionLocalAllowlist:
|
||||||
|
active: true
|
||||||
|
# You can optionally define a list of CompositionLocals that are allowed here
|
||||||
|
# allowedCompositionLocals: LocalSomething,LocalSomethingElse
|
||||||
|
CompositionLocalNaming:
|
||||||
|
active: true
|
||||||
|
ContentEmitterReturningValues:
|
||||||
|
active: true
|
||||||
|
# You can optionally add your own composables here
|
||||||
|
# contentEmitters: MyComposable,MyOtherComposable
|
||||||
|
ModifierComposable:
|
||||||
|
active: true
|
||||||
|
ModifierMissing:
|
||||||
|
active: true
|
||||||
|
ModifierReused:
|
||||||
|
active: true
|
||||||
|
ModifierWithoutDefault:
|
||||||
|
active: true
|
||||||
|
MultipleEmitters:
|
||||||
|
active: true
|
||||||
|
# You can optionally add your own composables here
|
||||||
|
# contentEmitters: MyComposable,MyOtherComposable
|
||||||
|
MutableParams:
|
||||||
|
active: true
|
||||||
|
ComposableNaming:
|
||||||
|
active: true
|
||||||
|
# You can optionally disable the checks in this rule for regex matches against the composable name (e.g. molecule presenters)
|
||||||
|
# allowedComposableFunctionNames: .*Presenter,.*MoleculePresenter
|
||||||
|
ComposableParamOrder:
|
||||||
|
active: true
|
||||||
|
PreviewNaming:
|
||||||
|
active: true
|
||||||
|
PreviewPublic:
|
||||||
|
active: true
|
||||||
|
# You can optionally disable that only previews with @PreviewParameter are flagged
|
||||||
|
# previewPublicOnlyIfParams: false
|
||||||
|
RememberMissing:
|
||||||
|
active: true
|
||||||
|
UnstableCollections:
|
||||||
|
active: false
|
||||||
|
ViewModelForwarding:
|
||||||
|
active: true
|
||||||
|
ViewModelInjection:
|
||||||
|
active: true
|
||||||
@ -5,7 +5,7 @@ import android.content.Context
|
|||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import java.text.NumberFormat
|
import java.text.NumberFormat
|
||||||
import java.util.*
|
import java.util.Currency
|
||||||
|
|
||||||
object Utils {
|
object Utils {
|
||||||
|
|
||||||
@ -31,4 +31,10 @@ object Utils {
|
|||||||
return accountBalanceFormatter.format(balance)
|
return accountBalanceFormatter.format(balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T> List<T>.toArrayList(): ArrayList<T> {
|
||||||
|
val array: ArrayList<T> = ArrayList()
|
||||||
|
for (index in this) array.add(index)
|
||||||
|
return array
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* 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.core.data.domain.usecase.history
|
||||||
|
|
||||||
|
import com.mifospay.core.model.domain.Transaction
|
||||||
|
|
||||||
|
interface HistoryContract {
|
||||||
|
interface TransactionsHistoryAsync {
|
||||||
|
fun onTransactionsFetchCompleted(transactions: List<Transaction>?)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,19 +1,25 @@
|
|||||||
package org.mifospay.feature
|
/*
|
||||||
|
* 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.core.data.domain.usecase.history
|
||||||
|
|
||||||
import com.mifospay.core.model.domain.Transaction
|
import com.mifospay.core.model.domain.Transaction
|
||||||
import org.mifospay.core.data.base.TaskLooper
|
import org.mifospay.core.data.base.TaskLooper
|
||||||
import org.mifospay.core.data.base.UseCase.UseCaseCallback
|
import org.mifospay.core.data.base.UseCase.UseCaseCallback
|
||||||
import org.mifospay.core.data.base.UseCaseFactory
|
import org.mifospay.core.data.base.UseCaseFactory
|
||||||
import org.mifospay.core.data.base.UseCaseHandler
|
import org.mifospay.core.data.base.UseCaseHandler
|
||||||
import org.mifospay.core.data.domain.usecase.account.FetchAccount
|
|
||||||
import org.mifospay.core.data.domain.usecase.account.FetchAccountTransactions
|
import org.mifospay.core.data.domain.usecase.account.FetchAccountTransactions
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@Suppress("UnusedPrivateProperty")
|
|
||||||
class TransactionsHistory @Inject constructor(
|
class TransactionsHistory @Inject constructor(
|
||||||
private val mUsecaseHandler: UseCaseHandler,
|
private val mUseCaseHandler: UseCaseHandler,
|
||||||
private val fetchAccountTransactionsUseCase: FetchAccountTransactions,
|
private val fetchAccountTransactionsUseCase: FetchAccountTransactions,
|
||||||
private val mFetchAccountUseCase: FetchAccount
|
|
||||||
) {
|
) {
|
||||||
var delegate: HistoryContract.TransactionsHistoryAsync? = null
|
var delegate: HistoryContract.TransactionsHistoryAsync? = null
|
||||||
|
|
||||||
@ -31,7 +37,8 @@ class TransactionsHistory @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun fetchTransactionsHistory(accountId: Long) {
|
fun fetchTransactionsHistory(accountId: Long) {
|
||||||
mUsecaseHandler.execute(fetchAccountTransactionsUseCase,
|
mUseCaseHandler.execute(
|
||||||
|
fetchAccountTransactionsUseCase,
|
||||||
FetchAccountTransactions.RequestValues(accountId),
|
FetchAccountTransactions.RequestValues(accountId),
|
||||||
object : UseCaseCallback<FetchAccountTransactions.ResponseValue?> {
|
object : UseCaseCallback<FetchAccountTransactions.ResponseValue?> {
|
||||||
override fun onSuccess(response: FetchAccountTransactions.ResponseValue?) {
|
override fun onSuccess(response: FetchAccountTransactions.ResponseValue?) {
|
||||||
@ -42,6 +49,7 @@ class TransactionsHistory @Inject constructor(
|
|||||||
override fun onError(message: String) {
|
override fun onError(message: String) {
|
||||||
transactions = null
|
transactions = null
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -31,7 +31,6 @@ class FetchInvoice @Inject constructor(private val mFineractRepository: Fineract
|
|||||||
val clientId = params?.get(0) // "clientId"
|
val clientId = params?.get(0) // "clientId"
|
||||||
val invoiceId = params?.get(1) // "invoiceId"
|
val invoiceId = params?.get(1) // "invoiceId"
|
||||||
if (clientId != null && invoiceId != null) {
|
if (clientId != null && invoiceId != null) {
|
||||||
|
|
||||||
mFineractRepository.fetchInvoice(clientId, invoiceId)
|
mFineractRepository.fetchInvoice(clientId, invoiceId)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package org.mifospay.core.data.domain.usecase.invoice
|
package org.mifospay.core.data.domain.usecase.invoice
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import org.mifospay.core.data.base.UseCase
|
import org.mifospay.core.data.base.UseCase
|
||||||
import com.mifospay.core.model.entity.Invoice
|
import com.mifospay.core.model.entity.Invoice
|
||||||
import org.mifospay.core.data.fineract.repository.FineractRepository
|
import org.mifospay.core.data.fineract.repository.FineractRepository
|
||||||
@ -26,6 +27,7 @@ class FetchInvoices @Inject constructor(private val mFineractRepository: Finerac
|
|||||||
.subscribe(object : Subscriber<List<Invoice>>() {
|
.subscribe(object : Subscriber<List<Invoice>>() {
|
||||||
override fun onCompleted() {}
|
override fun onCompleted() {}
|
||||||
override fun onError(e: Throwable) {
|
override fun onError(e: Throwable) {
|
||||||
|
Log.e("Invoices", e.message.toString())
|
||||||
useCaseCallback.onError(e.toString())
|
useCaseCallback.onError(e.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,16 +5,18 @@ import androidx.compose.foundation.layout.RowScope
|
|||||||
import androidx.compose.material3.FloatingActionButton
|
import androidx.compose.material3.FloatingActionButton
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MifosScaffold(
|
fun MifosScaffold(
|
||||||
topBarTitle: Int? = null,
|
|
||||||
backPress: () -> Unit,
|
backPress: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
topBarTitle: Int? = null,
|
||||||
floatingActionButtonContent: FloatingActionButtonContent? = null,
|
floatingActionButtonContent: FloatingActionButtonContent? = null,
|
||||||
snackbarHost: @Composable () -> Unit = {},
|
snackbarHost: @Composable () -> Unit = {},
|
||||||
scaffoldContent: @Composable (PaddingValues) -> Unit,
|
scaffoldContent: @Composable (PaddingValues) -> Unit,
|
||||||
actions: @Composable RowScope.() -> Unit = {}
|
actions: @Composable RowScope.() -> Unit = {},
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
@ -22,7 +24,7 @@ fun MifosScaffold(
|
|||||||
MifosTopBar(
|
MifosTopBar(
|
||||||
topBarTitle = topBarTitle,
|
topBarTitle = topBarTitle,
|
||||||
backPress = backPress,
|
backPress = backPress,
|
||||||
actions = actions
|
actions = actions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -31,17 +33,18 @@ fun MifosScaffold(
|
|||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
onClick = content.onClick,
|
onClick = content.onClick,
|
||||||
contentColor = content.contentColor,
|
contentColor = content.contentColor,
|
||||||
content = content.content
|
content = content.content,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
snackbarHost = snackbarHost,
|
snackbarHost = snackbarHost,
|
||||||
content = scaffoldContent,
|
content = scaffoldContent,
|
||||||
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class FloatingActionButtonContent(
|
data class FloatingActionButtonContent(
|
||||||
val onClick: (() -> Unit),
|
val onClick: (() -> Unit),
|
||||||
val contentColor: Color,
|
val contentColor: Color,
|
||||||
val content: (@Composable () -> Unit)
|
val content: (@Composable () -> Unit),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -16,11 +16,8 @@ dependencies {
|
|||||||
api(projects.core.designsystem)
|
api(projects.core.designsystem)
|
||||||
api(projects.core.model)
|
api(projects.core.model)
|
||||||
api(projects.core.common)
|
api(projects.core.common)
|
||||||
|
|
||||||
api(libs.androidx.metrics)
|
api(libs.androidx.metrics)
|
||||||
api(projects.core.analytics)
|
api(projects.core.analytics)
|
||||||
api(projects.core.designsystem)
|
|
||||||
api(projects.core.model)
|
|
||||||
|
|
||||||
implementation(libs.accompanist.pager)
|
implementation(libs.accompanist.pager)
|
||||||
implementation(libs.androidx.browser)
|
implementation(libs.androidx.browser)
|
||||||
|
|||||||
@ -16,11 +16,6 @@ import com.google.accompanist.pager.PagerState
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.mifospay.core.ui.utility.TabContent
|
import org.mifospay.core.ui.utility.TabContent
|
||||||
|
|
||||||
/**
|
|
||||||
* @author pratyush
|
|
||||||
* @since 23/3/24
|
|
||||||
*/
|
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MifosScrollableTabRow(
|
fun MifosScrollableTabRow(
|
||||||
@ -35,11 +30,10 @@ fun MifosScrollableTabRow(
|
|||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
ScrollableTabRow(
|
ScrollableTabRow(
|
||||||
|
modifier = modifier,
|
||||||
containerColor = containerColor,
|
containerColor = containerColor,
|
||||||
selectedTabIndex = pagerState.currentPage,
|
selectedTabIndex = pagerState.currentPage,
|
||||||
edgePadding = edgePadding,
|
edgePadding = edgePadding,
|
||||||
indicator = {},
|
|
||||||
divider = {},
|
|
||||||
) {
|
) {
|
||||||
tabContents.forEachIndexed { index, currentTab ->
|
tabContents.forEachIndexed { index, currentTab ->
|
||||||
Tab(
|
Tab(
|
||||||
@ -59,9 +53,8 @@ fun MifosScrollableTabRow(
|
|||||||
|
|
||||||
HorizontalPager(
|
HorizontalPager(
|
||||||
count = tabContents.size,
|
count = tabContents.size,
|
||||||
state = pagerState,
|
state = pagerState
|
||||||
modifier = modifier
|
) {
|
||||||
) { page ->
|
tabContents[it].content.invoke()
|
||||||
tabContents.getOrNull(page)?.content?.invoke() ?: Text("Page $page")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
plugins {
|
||||||
alias(libs.plugins.mifospay.android.feature)
|
alias(libs.plugins.mifospay.android.feature)
|
||||||
alias(libs.plugins.mifospay.android.library.compose)
|
alias(libs.plugins.mifospay.android.library.compose)
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
package org.mifospay.feature.bank.accounts
|
|
||||||
|
|
||||||
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.bank.accounts.test", appContext.packageName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@ -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
|
package org.mifospay.feature.bank.accounts
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
@ -55,33 +64,54 @@ class AccountViewModel @Inject constructor() : ViewModel() {
|
|||||||
private fun fetchSampleLinkedAccounts(): List<BankAccountDetails> {
|
private fun fetchSampleLinkedAccounts(): List<BankAccountDetails> {
|
||||||
return listOf(
|
return listOf(
|
||||||
BankAccountDetails(
|
BankAccountDetails(
|
||||||
"SBI", "Ankur Sharma", "New Delhi",
|
"SBI",
|
||||||
mRandom.nextInt().toString() + " ", "Savings"
|
"Ankur Sharma",
|
||||||
|
"New Delhi",
|
||||||
|
mRandom.nextInt().toString() + " ",
|
||||||
|
"Savings",
|
||||||
),
|
),
|
||||||
BankAccountDetails(
|
BankAccountDetails(
|
||||||
"HDFC", "Mandeep Singh", "Uttar Pradesh",
|
"HDFC",
|
||||||
mRandom.nextInt().toString() + " ", "Savings"
|
"Mandeep Singh",
|
||||||
|
"Uttar Pradesh",
|
||||||
|
mRandom.nextInt().toString() + " ",
|
||||||
|
"Savings",
|
||||||
),
|
),
|
||||||
BankAccountDetails(
|
BankAccountDetails(
|
||||||
"ANDHRA", "Rakesh anna", "Telegana",
|
"ANDHRA",
|
||||||
mRandom.nextInt().toString() + " ", "Savings"
|
"Rakesh anna",
|
||||||
|
"Telegana",
|
||||||
|
mRandom.nextInt().toString() + " ",
|
||||||
|
"Savings",
|
||||||
),
|
),
|
||||||
BankAccountDetails(
|
BankAccountDetails(
|
||||||
"PNB", "luv Pro", "Gujrat",
|
"PNB",
|
||||||
mRandom.nextInt().toString() + " ", "Savings"
|
"luv Pro",
|
||||||
|
"Gujrat",
|
||||||
|
mRandom.nextInt().toString() + " ",
|
||||||
|
"Savings",
|
||||||
),
|
),
|
||||||
BankAccountDetails(
|
BankAccountDetails(
|
||||||
"HDF", "Harry potter", "Hogwarts",
|
"HDF",
|
||||||
mRandom.nextInt().toString() + " ", "Savings"
|
"Harry potter",
|
||||||
|
"Hogwarts",
|
||||||
|
mRandom.nextInt().toString() + " ",
|
||||||
|
"Savings",
|
||||||
),
|
),
|
||||||
BankAccountDetails(
|
BankAccountDetails(
|
||||||
"GCI", "JIGME", "JAMMU",
|
"GCI",
|
||||||
mRandom.nextInt().toString() + " ", "Savings"
|
"JIGME",
|
||||||
|
"JAMMU",
|
||||||
|
mRandom.nextInt().toString() + " ",
|
||||||
|
"Savings",
|
||||||
),
|
),
|
||||||
BankAccountDetails(
|
BankAccountDetails(
|
||||||
"FCI", "NISHU BOII", "ASSAM",
|
"FCI",
|
||||||
mRandom.nextInt().toString() + " ", "Savings"
|
"NISHU BOII",
|
||||||
)
|
"ASSAM",
|
||||||
|
mRandom.nextInt().toString() + " ",
|
||||||
|
"Savings",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.bank.accounts
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@ -20,19 +29,21 @@ import com.mifospay.core.model.domain.BankAccountDetails
|
|||||||
import org.mifospay.core.designsystem.component.MifosCard
|
import org.mifospay.core.designsystem.component.MifosCard
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AccountsItem(
|
internal fun AccountsItem(
|
||||||
bankAccountDetails: BankAccountDetails,
|
bankAccountDetails: BankAccountDetails,
|
||||||
onAccountClicked: () -> Unit
|
onAccountClicked: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
MifosCard(
|
MifosCard(
|
||||||
|
modifier = modifier,
|
||||||
onClick = { onAccountClicked.invoke() },
|
onClick = { onAccountClicked.invoke() },
|
||||||
colors = CardDefaults.cardColors(MaterialTheme.colorScheme.surface)
|
colors = CardDefaults.cardColors(MaterialTheme.colorScheme.surface),
|
||||||
) {
|
) {
|
||||||
Column {
|
Column {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 16.dp)
|
.padding(top = 16.dp),
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(id = R.drawable.feature_accounts_ic_bank),
|
painter = painterResource(id = R.drawable.feature_accounts_ic_bank),
|
||||||
@ -40,7 +51,7 @@ fun AccountsItem(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.CenterVertically)
|
.align(Alignment.CenterVertically)
|
||||||
.padding(start = 16.dp, end = 16.dp)
|
.padding(start = 16.dp, end = 16.dp)
|
||||||
.size(39.dp)
|
.size(39.dp),
|
||||||
)
|
)
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@ -52,18 +63,18 @@ fun AccountsItem(
|
|||||||
text = bankAccountDetails.bankName.toString(),
|
text = bankAccountDetails.bankName.toString(),
|
||||||
modifier = Modifier.padding(top = 4.dp),
|
modifier = Modifier.padding(top = 4.dp),
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.End,
|
horizontalAlignment = Alignment.End,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = bankAccountDetails.branch.toString(),
|
text = bankAccountDetails.branch.toString(),
|
||||||
modifier = Modifier.padding(16.dp),
|
modifier = Modifier.padding(16.dp),
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,6 +87,6 @@ fun AccountsItem(
|
|||||||
private fun AccountsItemPreview() {
|
private fun AccountsItemPreview() {
|
||||||
AccountsItem(
|
AccountsItem(
|
||||||
bankAccountDetails = BankAccountDetails("A", "B", "C"),
|
bankAccountDetails = BankAccountDetails("A", "B", "C"),
|
||||||
onAccountClicked = {}
|
onAccountClicked = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.bank.accounts
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
@ -34,15 +43,17 @@ import org.mifospay.core.ui.utility.AddCardChip
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AccountsScreen(
|
fun AccountsScreen(
|
||||||
|
navigateToBankAccountDetailScreen: (BankAccountDetails, Int) -> Unit,
|
||||||
|
navigateToLinkBankAccountScreen: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
viewModel: AccountViewModel = hiltViewModel(),
|
viewModel: AccountViewModel = hiltViewModel(),
|
||||||
navigateToBankAccountDetailScreen: (BankAccountDetails,Int) -> Unit,
|
|
||||||
navigateToLinkBankAccountScreen: () -> Unit
|
|
||||||
) {
|
) {
|
||||||
val accountsUiState by viewModel.accountsUiState.collectAsStateWithLifecycle()
|
val accountsUiState by viewModel.accountsUiState.collectAsStateWithLifecycle()
|
||||||
val isRefreshing by viewModel.isRefreshing.collectAsStateWithLifecycle()
|
val isRefreshing by viewModel.isRefreshing.collectAsStateWithLifecycle()
|
||||||
val bankAccountDetailsList by viewModel.bankAccountDetailsList.collectAsStateWithLifecycle()
|
val bankAccountDetailsList by viewModel.bankAccountDetailsList.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
AccountScreen(
|
AccountScreen(
|
||||||
|
modifier = modifier,
|
||||||
accountsUiState = accountsUiState,
|
accountsUiState = accountsUiState,
|
||||||
onAddAccount = {
|
onAddAccount = {
|
||||||
navigateToLinkBankAccountScreen.invoke()
|
navigateToLinkBankAccountScreen.invoke()
|
||||||
@ -54,27 +65,30 @@ fun AccountsScreen(
|
|||||||
},
|
},
|
||||||
onUpdateAccount = { bankAccountDetails, index ->
|
onUpdateAccount = { bankAccountDetails, index ->
|
||||||
viewModel.updateBankAccount(index, bankAccountDetails)
|
viewModel.updateBankAccount(index, bankAccountDetails)
|
||||||
navigateToBankAccountDetailScreen.invoke(bankAccountDetails,index)
|
navigateToBankAccountDetailScreen.invoke(bankAccountDetails, index)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun AccountScreen(
|
private fun AccountScreen(
|
||||||
accountsUiState: AccountsUiState,
|
accountsUiState: AccountsUiState,
|
||||||
onAddAccount: () -> Unit,
|
onAddAccount: () -> Unit,
|
||||||
bankAccountDetailsList: List<BankAccountDetails>,
|
bankAccountDetailsList: List<BankAccountDetails>,
|
||||||
isRefreshing: Boolean,
|
isRefreshing: Boolean,
|
||||||
onRefresh: () -> Unit,
|
onRefresh: () -> Unit,
|
||||||
onUpdateAccount: (BankAccountDetails, Int) -> Unit
|
onUpdateAccount: (BankAccountDetails, Int) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val pullRefreshState = rememberPullRefreshState(isRefreshing, onRefresh)
|
val pullRefreshState = rememberPullRefreshState(isRefreshing, onRefresh)
|
||||||
Box(Modifier.pullRefresh(pullRefreshState)) {
|
Box(modifier.pullRefresh(pullRefreshState)) {
|
||||||
Column(modifier = Modifier.fillMaxSize()) {
|
Column(modifier = Modifier.fillMaxSize()) {
|
||||||
when (accountsUiState) {
|
when (accountsUiState) {
|
||||||
AccountsUiState.Empty -> {
|
AccountsUiState.Empty -> {
|
||||||
NoLinkedAccountsScreen { onAddAccount.invoke() }
|
NoLinkedAccountsScreen(
|
||||||
|
onAddBtn = onAddAccount,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountsUiState.Error -> {
|
AccountsUiState.Error -> {
|
||||||
@ -83,7 +97,7 @@ fun AccountScreen(
|
|||||||
title = stringResource(id = R.string.feature_accounts_error_oops),
|
title = stringResource(id = R.string.feature_accounts_error_oops),
|
||||||
subTitle = stringResource(id = R.string.feature_accounts_unexpected_error_subtitle),
|
subTitle = stringResource(id = R.string.feature_accounts_unexpected_error_subtitle),
|
||||||
iconTint = MaterialTheme.colorScheme.onSurface,
|
iconTint = MaterialTheme.colorScheme.onSurface,
|
||||||
iconImageVector = Icons.Rounded.Info
|
iconImageVector = Icons.Rounded.Info,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,14 +105,14 @@ fun AccountScreen(
|
|||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.fillMaxSize()
|
.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_accounts_linked_bank_account),
|
text = stringResource(id = R.string.feature_accounts_linked_bank_account),
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
color = MaterialTheme.colorScheme.primary,
|
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 ->
|
items(bankAccountDetailsList) { bankAccountDetails ->
|
||||||
@ -107,10 +121,10 @@ fun AccountScreen(
|
|||||||
bankAccountDetails = bankAccountDetails,
|
bankAccountDetails = bankAccountDetails,
|
||||||
onAccountClicked = {
|
onAccountClicked = {
|
||||||
onUpdateAccount(bankAccountDetails, index)
|
onUpdateAccount(bankAccountDetails, index)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
HorizontalDivider(
|
HorizontalDivider(
|
||||||
modifier = Modifier.padding(8.dp)
|
modifier = Modifier.padding(8.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
@ -118,13 +132,13 @@ fun AccountScreen(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
.background(MaterialTheme.colorScheme.surface)
|
.background(MaterialTheme.colorScheme.surface),
|
||||||
) {
|
) {
|
||||||
AddCardChip(
|
AddCardChip(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center),
|
||||||
onAddBtn = onAddAccount,
|
onAddBtn = onAddAccount,
|
||||||
text = R.string.feature_accounts_add_account,
|
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 -> {
|
AccountsUiState.Loading -> {
|
||||||
MfLoadingWheel(
|
MfLoadingWheel(
|
||||||
contentDesc = stringResource(R.string.feature_accounts_loading),
|
contentDesc = stringResource(R.string.feature_accounts_loading),
|
||||||
backgroundColor = MaterialTheme.colorScheme.surface
|
backgroundColor = MaterialTheme.colorScheme.surface,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,26 +156,29 @@ fun AccountScreen(
|
|||||||
PullRefreshIndicator(
|
PullRefreshIndicator(
|
||||||
refreshing = isRefreshing,
|
refreshing = isRefreshing,
|
||||||
state = pullRefreshState,
|
state = pullRefreshState,
|
||||||
modifier = Modifier.align(Alignment.TopCenter)
|
modifier = Modifier.align(Alignment.TopCenter),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NoLinkedAccountsScreen(onAddBtn: () -> Unit) {
|
private fun NoLinkedAccountsScreen(
|
||||||
|
onAddBtn: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = modifier.fillMaxSize(),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
Text(text = stringResource(R.string.feature_accounts_no_linked_bank_accounts))
|
Text(text = stringResource(R.string.feature_accounts_no_linked_bank_accounts))
|
||||||
AddCardChip(
|
AddCardChip(
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
onAddBtn = onAddBtn,
|
onAddBtn = onAddBtn,
|
||||||
text = R.string.feature_accounts_add_account,
|
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)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
private fun AccountScreenLoadingPreview() {
|
private fun AccountScreenLoadingPreview() {
|
||||||
AccountScreen(accountsUiState = AccountsUiState.Loading, {}, emptyList(), false, {}, { _, _ -> })
|
AccountScreen(
|
||||||
|
accountsUiState = AccountsUiState.Loading,
|
||||||
|
{},
|
||||||
|
emptyList(),
|
||||||
|
false,
|
||||||
|
{},
|
||||||
|
{ _, _ -> },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@ -188,7 +212,7 @@ private fun AccountListScreenPreview() {
|
|||||||
sampleLinkedAccount,
|
sampleLinkedAccount,
|
||||||
false,
|
false,
|
||||||
{},
|
{},
|
||||||
{ _, _ -> }
|
{ _, _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,7 +224,10 @@ private fun AccountErrorScreenPreview() {
|
|||||||
|
|
||||||
val sampleLinkedAccount = List(10) {
|
val sampleLinkedAccount = List(10) {
|
||||||
BankAccountDetails(
|
BankAccountDetails(
|
||||||
"SBI", "Ankur Sharma", "New Delhi",
|
"SBI",
|
||||||
"XXXXXXXX9990XXX " + " ", "Savings"
|
"Ankur Sharma",
|
||||||
|
"New Delhi",
|
||||||
|
"XXXXXXXX9990XXX " + " ",
|
||||||
|
"Savings",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.bank.accounts.choose.sim
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
@ -40,14 +49,14 @@ import org.mifospay.core.designsystem.theme.MifosTheme
|
|||||||
import org.mifospay.feature.bank.accounts.R
|
import org.mifospay.feature.bank.accounts.R
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChooseSimDialogSheet(
|
internal fun ChooseSimDialogSheet(
|
||||||
onSimSelected: (Int) -> Unit,
|
onSimSelected: (Int) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
MifosBottomSheet(
|
MifosBottomSheet(
|
||||||
content = {
|
content = {
|
||||||
ChooseSimDialogSheetContent(
|
ChooseSimDialogSheetContent(
|
||||||
onSimSelected = onSimSelected
|
onSimSelected = onSimSelected,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onDismiss = {
|
onDismiss = {
|
||||||
@ -63,7 +72,7 @@ fun ChooseSimDialogSheet(
|
|||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod")
|
||||||
fun ChooseSimDialogSheetContent(
|
private fun ChooseSimDialogSheetContent(
|
||||||
onSimSelected: (Int) -> Unit,
|
onSimSelected: (Int) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
@ -82,67 +91,67 @@ fun ChooseSimDialogSheetContent(
|
|||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(8.dp)
|
.padding(8.dp),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_accounts_verify_mobile_number),
|
text = stringResource(id = R.string.feature_accounts_verify_mobile_number),
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_accounts_confirm_mobile_number_message),
|
text = stringResource(id = R.string.feature_accounts_confirm_mobile_number_message),
|
||||||
style = MaterialTheme.typography.bodySmall.copy(
|
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))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(horizontal = 24.dp),
|
modifier = Modifier.padding(horizontal = 24.dp),
|
||||||
text = stringResource(id = R.string.feature_accounts_bank_account_mobile_verification_conditions),
|
text = stringResource(id = R.string.feature_accounts_bank_account_mobile_verification_conditions),
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.Center,
|
horizontalArrangement = Arrangement.Center,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
SimCard(
|
SimCard(
|
||||||
simNumber = 1,
|
simNumber = 1,
|
||||||
isSelected = selectedSim == 1,
|
isSelected = selectedSim == 1,
|
||||||
onSimSelected = { selectedSim = 1 }
|
onSimSelected = { selectedSim = 1 },
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(24.dp))
|
Spacer(modifier = Modifier.width(24.dp))
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_accounts_or),
|
text = stringResource(id = R.string.feature_accounts_or),
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(24.dp))
|
Spacer(modifier = Modifier.width(24.dp))
|
||||||
SimCard(
|
SimCard(
|
||||||
simNumber = 2,
|
simNumber = 2,
|
||||||
isSelected = selectedSim == 2,
|
isSelected = selectedSim == 2,
|
||||||
onSimSelected = { selectedSim = 2 }
|
onSimSelected = { selectedSim = 2 },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_accounts_regular_charges_will_apply),
|
text = stringResource(id = R.string.feature_accounts_regular_charges_will_apply),
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
style = MaterialTheme.typography.bodySmall
|
style = MaterialTheme.typography.bodySmall,
|
||||||
)
|
)
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = showMessage
|
visible = showMessage,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = message,
|
text = message,
|
||||||
color = MaterialTheme.colorScheme.error,
|
color = MaterialTheme.colorScheme.error,
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
modifier = Modifier.padding(vertical = 4.dp)
|
modifier = Modifier.padding(vertical = 4.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +165,7 @@ fun ChooseSimDialogSheetContent(
|
|||||||
} else {
|
} else {
|
||||||
onSimSelected(selectedSim)
|
onSimSelected(selectedSim)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
) {
|
) {
|
||||||
Text(text = stringResource(id = R.string.feature_accounts_confirm))
|
Text(text = stringResource(id = R.string.feature_accounts_confirm))
|
||||||
}
|
}
|
||||||
@ -165,7 +174,7 @@ fun ChooseSimDialogSheetContent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SimCard(
|
private fun SimCard(
|
||||||
simNumber: Int,
|
simNumber: Int,
|
||||||
isSelected: Boolean,
|
isSelected: Boolean,
|
||||||
onSimSelected: () -> Unit,
|
onSimSelected: () -> Unit,
|
||||||
@ -174,7 +183,9 @@ fun SimCard(
|
|||||||
val drawable: Painter = painterResource(
|
val drawable: Painter = painterResource(
|
||||||
id = if (isSelected) {
|
id = if (isSelected) {
|
||||||
R.drawable.feature_accounts_sim_card_selected
|
R.drawable.feature_accounts_sim_card_selected
|
||||||
} else R.drawable.feature_accounts_sim_card_unselected
|
} else {
|
||||||
|
R.drawable.feature_accounts_sim_card_unselected
|
||||||
|
},
|
||||||
)
|
)
|
||||||
Image(
|
Image(
|
||||||
painter = drawable,
|
painter = drawable,
|
||||||
@ -182,17 +193,17 @@ fun SimCard(
|
|||||||
contentScale = ContentScale.Fit,
|
contentScale = ContentScale.Fit,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.size(50.dp)
|
.size(50.dp)
|
||||||
.clickable { onSimSelected() }
|
.clickable { onSimSelected() },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun SimSelectionPreview() {
|
private fun SimSelectionPreview() {
|
||||||
MifosTheme {
|
MifosTheme {
|
||||||
Surface {
|
Surface {
|
||||||
ChooseSimDialogSheetContent(
|
ChooseSimDialogSheetContent(
|
||||||
onSimSelected = {}
|
onSimSelected = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.bank.accounts.details
|
||||||
|
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
@ -26,12 +35,12 @@ import org.mifospay.core.designsystem.component.MifosTopBar
|
|||||||
import org.mifospay.feature.bank.accounts.R
|
import org.mifospay.feature.bank.accounts.R
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BankAccountDetailScreen(
|
internal fun BankAccountDetailScreen(
|
||||||
bankAccountDetails: BankAccountDetails,
|
bankAccountDetails: BankAccountDetails,
|
||||||
onSetupUpiPin: () -> Unit,
|
onSetupUpiPin: () -> Unit,
|
||||||
onChangeUpiPin: () -> Unit,
|
onChangeUpiPin: () -> Unit,
|
||||||
onForgotUpiPin: () -> Unit,
|
onForgotUpiPin: () -> Unit,
|
||||||
navigateBack: () -> Unit
|
navigateBack: () -> Unit,
|
||||||
) {
|
) {
|
||||||
BankAccountDetailScreen(
|
BankAccountDetailScreen(
|
||||||
bankName = bankAccountDetails.bankName.toString(),
|
bankName = bankAccountDetails.bankName.toString(),
|
||||||
@ -43,12 +52,12 @@ fun BankAccountDetailScreen(
|
|||||||
onSetupUpiPin = onSetupUpiPin,
|
onSetupUpiPin = onSetupUpiPin,
|
||||||
onChangeUpiPin = onChangeUpiPin,
|
onChangeUpiPin = onChangeUpiPin,
|
||||||
onForgotUpiPin = onForgotUpiPin,
|
onForgotUpiPin = onForgotUpiPin,
|
||||||
navigateBack = navigateBack
|
navigateBack = navigateBack,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BankAccountDetailScreen(
|
private fun BankAccountDetailScreen(
|
||||||
bankName: String,
|
bankName: String,
|
||||||
accountHolderName: String,
|
accountHolderName: String,
|
||||||
branchName: String,
|
branchName: String,
|
||||||
@ -58,109 +67,131 @@ fun BankAccountDetailScreen(
|
|||||||
onSetupUpiPin: () -> Unit,
|
onSetupUpiPin: () -> Unit,
|
||||||
onChangeUpiPin: () -> Unit,
|
onChangeUpiPin: () -> Unit,
|
||||||
onForgotUpiPin: () -> Unit,
|
onForgotUpiPin: () -> Unit,
|
||||||
navigateBack: () -> Unit
|
navigateBack: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.fillMaxSize()) {
|
Column(
|
||||||
MifosTopBar(topBarTitle = R.string.feature_accounts_bank_account_details) { navigateBack.invoke() }
|
modifier = modifier
|
||||||
|
.fillMaxSize(),
|
||||||
|
) {
|
||||||
|
MifosTopBar(
|
||||||
|
topBarTitle = R.string.feature_accounts_bank_account_details,
|
||||||
|
backPress = navigateBack,
|
||||||
|
)
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(20.dp)
|
.padding(20.dp)
|
||||||
.border(2.dp, MaterialTheme.colorScheme.onSurface)
|
.border(2.dp, MaterialTheme.colorScheme.onSurface)
|
||||||
.padding(20.dp)
|
.padding(20.dp),
|
||||||
) {
|
) {
|
||||||
BankAccountDetailRows(
|
BankAccountDetailRows(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
detail = R.string.feature_accounts_bank_name,
|
detail = R.string.feature_accounts_bank_name,
|
||||||
detailValue = bankName
|
detailValue = bankName,
|
||||||
)
|
)
|
||||||
BankAccountDetailRows(
|
BankAccountDetailRows(
|
||||||
modifier = Modifier.fillMaxWidth().padding(top = 10.dp),
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = 10.dp),
|
||||||
detail = R.string.feature_accounts_ac_holder_name,
|
detail = R.string.feature_accounts_ac_holder_name,
|
||||||
detailValue = accountHolderName
|
detailValue = accountHolderName,
|
||||||
)
|
)
|
||||||
BankAccountDetailRows(
|
BankAccountDetailRows(
|
||||||
modifier = Modifier.fillMaxWidth().padding(top = 10.dp),
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = 10.dp),
|
||||||
detail = R.string.feature_accounts_branch_name,
|
detail = R.string.feature_accounts_branch_name,
|
||||||
detailValue = branchName
|
detailValue = branchName,
|
||||||
)
|
)
|
||||||
BankAccountDetailRows(
|
BankAccountDetailRows(
|
||||||
modifier = Modifier.fillMaxWidth().padding(top = 10.dp),
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = 10.dp),
|
||||||
detail = R.string.feature_accounts_ifsc,
|
detail = R.string.feature_accounts_ifsc,
|
||||||
detailValue = ifsc
|
detailValue = ifsc,
|
||||||
)
|
)
|
||||||
BankAccountDetailRows(
|
BankAccountDetailRows(
|
||||||
modifier = Modifier.fillMaxWidth().padding(top = 10.dp),
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = 10.dp),
|
||||||
detail = R.string.feature_accounts_type,
|
detail = R.string.feature_accounts_type,
|
||||||
detailValue = type
|
detailValue = type,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth().padding(20.dp),
|
modifier = Modifier
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
.fillMaxWidth()
|
||||||
|
.padding(20.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
BankAccountDetailButton(
|
BankAccountDetailButton(
|
||||||
btnText = R.string.feature_accounts_setup_upi,
|
btnText = R.string.feature_accounts_setup_upi,
|
||||||
onClick = { onSetupUpiPin.invoke() },
|
onClick = { onSetupUpiPin.invoke() },
|
||||||
isUpiEnabled = !isUpiEnabled,
|
isUpiEnabled = !isUpiEnabled,
|
||||||
hasTrailingIcon = false
|
hasTrailingIcon = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
BankAccountDetailButton(
|
BankAccountDetailButton(
|
||||||
btnText = R.string.feature_accounts_delete_bank,
|
btnText = R.string.feature_accounts_delete_bank,
|
||||||
onClick = {},
|
onClick = {},
|
||||||
isUpiEnabled = !isUpiEnabled
|
isUpiEnabled = !isUpiEnabled,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(20.dp)
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(20.dp),
|
||||||
) {
|
) {
|
||||||
BankAccountDetailButton(
|
BankAccountDetailButton(
|
||||||
btnText = R.string.feature_accounts_change_upi_pin,
|
btnText = R.string.feature_accounts_change_upi_pin,
|
||||||
onClick = { onChangeUpiPin.invoke() },
|
onClick = { onChangeUpiPin.invoke() },
|
||||||
isUpiEnabled = isUpiEnabled,
|
isUpiEnabled = isUpiEnabled,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth(),
|
||||||
)
|
)
|
||||||
BankAccountDetailButton(
|
BankAccountDetailButton(
|
||||||
btnText = R.string.feature_accounts_forgot_upi_pin,
|
btnText = R.string.feature_accounts_forgot_upi_pin,
|
||||||
onClick = { onForgotUpiPin.invoke() },
|
onClick = { onForgotUpiPin.invoke() },
|
||||||
isUpiEnabled = isUpiEnabled,
|
isUpiEnabled = isUpiEnabled,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BankAccountDetailRows(
|
private fun BankAccountDetailRows(
|
||||||
modifier: Modifier, detail: Int, detailValue: String
|
detail: Int,
|
||||||
|
detailValue: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = detail),
|
text = stringResource(id = detail),
|
||||||
modifier = Modifier.padding(end = 10.dp),
|
modifier = Modifier.padding(end = 10.dp),
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
)
|
)
|
||||||
Text(text = detailValue,
|
Text(
|
||||||
|
text = detailValue,
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
color = MaterialTheme.colorScheme.onSurface)
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BankAccountDetailButton(
|
private fun BankAccountDetailButton(
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
btnText: Int,
|
btnText: Int,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
isUpiEnabled: Boolean,
|
isUpiEnabled: Boolean,
|
||||||
hasTrailingIcon: Boolean = false
|
modifier: Modifier = Modifier,
|
||||||
|
hasTrailingIcon: Boolean = false,
|
||||||
) {
|
) {
|
||||||
if (isUpiEnabled) {
|
if (isUpiEnabled) {
|
||||||
Button(
|
Button(
|
||||||
@ -168,23 +199,23 @@ fun BankAccountDetailButton(
|
|||||||
colors = ButtonDefaults.buttonColors(MaterialTheme.colorScheme.primary),
|
colors = ButtonDefaults.buttonColors(MaterialTheme.colorScheme.primary),
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.padding(start = 20.dp, end = 20.dp),
|
.padding(start = 20.dp, end = 20.dp),
|
||||||
contentPadding = PaddingValues(20.dp)
|
contentPadding = PaddingValues(20.dp),
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier,
|
modifier = Modifier,
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = btnText),
|
text = stringResource(id = btnText),
|
||||||
style = MaterialTheme.typography.labelLarge,
|
style = MaterialTheme.typography.labelLarge,
|
||||||
color = MaterialTheme.colorScheme.onPrimary
|
color = MaterialTheme.colorScheme.onPrimary,
|
||||||
)
|
)
|
||||||
if (hasTrailingIcon) {
|
if (hasTrailingIcon) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.ChevronRight,
|
imageVector = Icons.Filled.ChevronRight,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = MaterialTheme.colorScheme.onPrimary
|
tint = MaterialTheme.colorScheme.onPrimary,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -192,29 +223,30 @@ fun BankAccountDetailButton(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
private fun BankAccountDetailUpiDisabledPreview() {
|
private fun BankAccountDetailUpiDisabledPreview() {
|
||||||
BankAccountDetailScreen("Mifos Bank",
|
BankAccountDetailScreen(
|
||||||
|
"Mifos Bank",
|
||||||
"Mifos Account Holder",
|
"Mifos Account Holder",
|
||||||
"Mifos Branch",
|
"Mifos Branch",
|
||||||
"IFSC",
|
"IFSC",
|
||||||
"type",
|
"type",
|
||||||
false,
|
false,
|
||||||
{}, {}, {}, {}
|
{}, {}, {}, {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
private fun BankAccountDetailUpiEnabledPreview() {
|
private fun BankAccountDetailUpiEnabledPreview() {
|
||||||
BankAccountDetailScreen("Mifos Bank",
|
BankAccountDetailScreen(
|
||||||
|
"Mifos Bank",
|
||||||
"Mifos Account Holder",
|
"Mifos Account Holder",
|
||||||
"Mifos Branch",
|
"Mifos Branch",
|
||||||
"IFSC",
|
"IFSC",
|
||||||
"type",
|
"type",
|
||||||
true,
|
true,
|
||||||
{}, {}, {}, {}
|
{}, {}, {}, {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.bank.accounts.link
|
||||||
|
|
||||||
import androidx.compose.foundation.Image
|
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.R
|
||||||
import org.mifospay.feature.bank.accounts.choose.sim.ChooseSimDialogSheet
|
import org.mifospay.feature.bank.accounts.choose.sim.ChooseSimDialogSheet
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LinkBankAccountRoute(
|
internal fun LinkBankAccountRoute(
|
||||||
viewModel: LinkBankAccountViewModel = hiltViewModel(),
|
viewModel: LinkBankAccountViewModel = hiltViewModel(),
|
||||||
onBackClick: () -> Unit
|
onBackClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val bankUiState by viewModel.bankListUiState.collectAsStateWithLifecycle()
|
val bankUiState by viewModel.bankListUiState.collectAsStateWithLifecycle()
|
||||||
var showSimBottomSheet by rememberSaveable { mutableStateOf(false) }
|
var showSimBottomSheet by rememberSaveable { mutableStateOf(false) }
|
||||||
@ -79,7 +87,7 @@ fun LinkBankAccountRoute(
|
|||||||
onBackClick()
|
onBackClick()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,22 +101,23 @@ fun LinkBankAccountRoute(
|
|||||||
viewModel.updateSelectedBank(it)
|
viewModel.updateSelectedBank(it)
|
||||||
showSimBottomSheet = true
|
showSimBottomSheet = true
|
||||||
},
|
},
|
||||||
onBackClick = onBackClick
|
onBackClick = onBackClick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun LinkBankAccountScreen(
|
private fun LinkBankAccountScreen(
|
||||||
bankUiState: BankUiState,
|
bankUiState: BankUiState,
|
||||||
showOverlyProgressBar: Boolean,
|
showOverlyProgressBar: Boolean,
|
||||||
onBankSearch: (String) -> Unit,
|
onBankSearch: (String) -> Unit,
|
||||||
onBankSelected: (Bank) -> Unit,
|
onBankSelected: (Bank) -> Unit,
|
||||||
onBackClick: () -> Unit
|
onBackClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.background(color = MaterialTheme.colorScheme.surface),
|
modifier = modifier
|
||||||
|
.background(color = MaterialTheme.colorScheme.surface),
|
||||||
topBar = {
|
topBar = {
|
||||||
MifosTopAppBar(
|
MifosTopAppBar(
|
||||||
titleRes = R.string.feature_accounts_link_bank_account,
|
titleRes = R.string.feature_accounts_link_bank_account,
|
||||||
@ -119,15 +128,17 @@ fun LinkBankAccountScreen(
|
|||||||
containerColor = MaterialTheme.colorScheme.surface,
|
containerColor = MaterialTheme.colorScheme.surface,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}) { paddingValues ->
|
},
|
||||||
|
) { paddingValues ->
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.padding(paddingValues)
|
modifier = Modifier
|
||||||
|
.padding(paddingValues),
|
||||||
) {
|
) {
|
||||||
when (bankUiState) {
|
when (bankUiState) {
|
||||||
is BankUiState.Loading -> {
|
is BankUiState.Loading -> {
|
||||||
MfLoadingWheel(
|
MfLoadingWheel(
|
||||||
contentDesc = stringResource(R.string.feature_accounts_loading),
|
contentDesc = stringResource(R.string.feature_accounts_loading),
|
||||||
backgroundColor = MaterialTheme.colorScheme.surface
|
backgroundColor = MaterialTheme.colorScheme.surface,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +146,7 @@ fun LinkBankAccountScreen(
|
|||||||
BankListScreenContent(
|
BankListScreenContent(
|
||||||
banks = bankUiState.banks,
|
banks = bankUiState.banks,
|
||||||
onBankSearch = onBankSearch,
|
onBankSearch = onBankSearch,
|
||||||
onBankSelected = onBankSelected
|
onBankSelected = onBankSelected,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,21 +159,23 @@ fun LinkBankAccountScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BankListScreenContent(
|
private fun BankListScreenContent(
|
||||||
banks: List<Bank>,
|
banks: List<Bank>,
|
||||||
onBankSearch: (String) -> Unit,
|
onBankSearch: (String) -> Unit,
|
||||||
onBankSelected: (Bank) -> Unit
|
onBankSelected: (Bank) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
var searchQuery by rememberSaveable { mutableStateOf("") }
|
var searchQuery by rememberSaveable { mutableStateOf("") }
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(color = MaterialTheme.colorScheme.surface)
|
.background(color = MaterialTheme.colorScheme.surface)
|
||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState()),
|
||||||
) {
|
) {
|
||||||
MifosOutlinedTextField(modifier = Modifier
|
MifosOutlinedTextField(
|
||||||
.fillMaxWidth()
|
modifier = Modifier
|
||||||
.padding(horizontal = 16.dp),
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
value = searchQuery,
|
value = searchQuery,
|
||||||
onValueChange = {
|
onValueChange = {
|
||||||
searchQuery = it
|
searchQuery = it
|
||||||
@ -171,7 +184,8 @@ fun BankListScreenContent(
|
|||||||
label = R.string.feature_accounts_search,
|
label = R.string.feature_accounts_search,
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
Icon(imageVector = Icons.Filled.Search, contentDescription = null)
|
Icon(imageVector = Icons.Filled.Search, contentDescription = null)
|
||||||
})
|
},
|
||||||
|
)
|
||||||
|
|
||||||
if (searchQuery.isBlank()) {
|
if (searchQuery.isBlank()) {
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
@ -179,23 +193,23 @@ fun BankListScreenContent(
|
|||||||
text = stringResource(id = R.string.feature_accounts_popular_banks),
|
text = stringResource(id = R.string.feature_accounts_popular_banks),
|
||||||
style = TextStyle(
|
style = TextStyle(
|
||||||
MaterialTheme.colorScheme.onSurface,
|
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))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
PopularBankGridBody(
|
PopularBankGridBody(
|
||||||
banks = banks.filter { it.bankType == BankType.POPULAR },
|
banks = banks.filter { it.bankType == BankType.POPULAR },
|
||||||
onBankSelected = onBankSelected
|
onBankSelected = onBankSelected,
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_accounts_other_banks),
|
text = stringResource(id = R.string.feature_accounts_other_banks),
|
||||||
style = TextStyle(
|
style = TextStyle(
|
||||||
MaterialTheme.colorScheme.onSurface,
|
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))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
}
|
}
|
||||||
@ -203,34 +217,37 @@ fun BankListScreenContent(
|
|||||||
BankListBody(
|
BankListBody(
|
||||||
banks = if (searchQuery.isBlank()) {
|
banks = if (searchQuery.isBlank()) {
|
||||||
banks.filter { it.bankType == BankType.OTHER }
|
banks.filter { it.bankType == BankType.OTHER }
|
||||||
} else banks,
|
} else {
|
||||||
onBankSelected = onBankSelected
|
banks
|
||||||
|
},
|
||||||
|
onBankSelected = onBankSelected,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun PopularBankGridBody(
|
private fun PopularBankGridBody(
|
||||||
banks: List<Bank>,
|
banks: List<Bank>,
|
||||||
onBankSelected: (Bank) -> Unit
|
onBankSelected: (Bank) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
MifosCard(
|
MifosCard(
|
||||||
modifier = Modifier,
|
modifier = modifier,
|
||||||
shape = RoundedCornerShape(0.dp),
|
shape = RoundedCornerShape(0.dp),
|
||||||
elevation = 2.dp,
|
elevation = 2.dp,
|
||||||
colors = CardDefaults.cardColors(MaterialTheme.colorScheme.surface)
|
colors = CardDefaults.cardColors(MaterialTheme.colorScheme.surface),
|
||||||
) {
|
) {
|
||||||
FlowRow(
|
FlowRow(
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
maxItemsInEachRow = 3
|
maxItemsInEachRow = 3,
|
||||||
) {
|
) {
|
||||||
banks.forEach {
|
banks.forEach {
|
||||||
PopularBankItemBody(
|
PopularBankItemBody(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
bank = it,
|
bank = it,
|
||||||
onBankSelected = onBankSelected
|
onBankSelected = onBankSelected,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -238,10 +255,10 @@ fun PopularBankGridBody(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PopularBankItemBody(
|
private fun PopularBankItemBody(
|
||||||
modifier: Modifier,
|
|
||||||
bank: Bank,
|
bank: Bank,
|
||||||
onBankSelected: (Bank) -> Unit
|
onBankSelected: (Bank) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
@ -262,18 +279,19 @@ fun PopularBankItemBody(
|
|||||||
Text(
|
Text(
|
||||||
text = bank.name,
|
text = bank.name,
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
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)
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun BankListBody(
|
private fun BankListBody(
|
||||||
banks: List<Bank>,
|
banks: List<Bank>,
|
||||||
onBankSelected: (Bank) -> Unit
|
onBankSelected: (Bank) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
FlowColumn {
|
FlowColumn(modifier) {
|
||||||
banks.forEach { bank ->
|
banks.forEach { bank ->
|
||||||
BankListItemBody(bank = bank, onBankSelected = onBankSelected)
|
BankListItemBody(bank = bank, onBankSelected = onBankSelected)
|
||||||
}
|
}
|
||||||
@ -281,14 +299,15 @@ fun BankListBody(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BankListItemBody(
|
private fun BankListItemBody(
|
||||||
bank: Bank,
|
bank: Bank,
|
||||||
onBankSelected: (Bank) -> Unit
|
onBankSelected: (Bank) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.clickable { onBankSelected(bank) }
|
.clickable { onBankSelected(bank) },
|
||||||
) {
|
) {
|
||||||
HorizontalDivider()
|
HorizontalDivider()
|
||||||
Row(
|
Row(
|
||||||
@ -296,7 +315,7 @@ fun BankListItemBody(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(start = 24.dp, top = 8.dp, bottom = 8.dp)
|
.padding(start = 24.dp, top = 8.dp, bottom = 8.dp),
|
||||||
) {
|
) {
|
||||||
Image(
|
Image(
|
||||||
modifier = Modifier.size(32.dp),
|
modifier = Modifier.size(32.dp),
|
||||||
@ -305,7 +324,8 @@ fun BankListItemBody(
|
|||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(start = 16.dp, end = 16.dp),
|
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,
|
showOverlyProgressBar = false,
|
||||||
onBankSelected = { },
|
onBankSelected = { },
|
||||||
onBankSearch = { },
|
onBankSearch = { },
|
||||||
onBackClick = { }
|
onBackClick = { },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.bank.accounts.link
|
||||||
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@ -24,17 +33,17 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class LinkBankAccountViewModel @Inject constructor(
|
class LinkBankAccountViewModel @Inject constructor(
|
||||||
localAssetRepository: MifosLocalAssetRepository
|
localAssetRepository: MifosLocalAssetRepository,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _searchQuery = MutableStateFlow("")
|
private val searchQuery = MutableStateFlow("")
|
||||||
private var selectedBank by mutableStateOf<Bank?>(null)
|
private var selectedBank by mutableStateOf<Bank?>(null)
|
||||||
|
|
||||||
private val _bankAccountDetails: MutableStateFlow<BankAccountDetails?> = MutableStateFlow(null)
|
private val accountDetails: MutableStateFlow<BankAccountDetails?> = MutableStateFlow(null)
|
||||||
val bankAccountDetails: StateFlow<BankAccountDetails?> = _bankAccountDetails.asStateFlow()
|
val bankAccountDetails: StateFlow<BankAccountDetails?> = accountDetails.asStateFlow()
|
||||||
|
|
||||||
fun updateSearchQuery(query: String) {
|
fun updateSearchQuery(query: String) {
|
||||||
_searchQuery.update { query }
|
searchQuery.update { query }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateSelectedBank(bank: Bank) {
|
fun updateSelectedBank(bank: Bank) {
|
||||||
@ -42,9 +51,9 @@ class LinkBankAccountViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val bankListUiState: StateFlow<BankUiState> = combine(
|
val bankListUiState: StateFlow<BankUiState> = combine(
|
||||||
_searchQuery,
|
searchQuery,
|
||||||
localAssetRepository.getBanks(),
|
localAssetRepository.getBanks(),
|
||||||
::Pair
|
::Pair,
|
||||||
).map { searchQueryAndBanks ->
|
).map { searchQueryAndBanks ->
|
||||||
val searchQuery = searchQueryAndBanks.first
|
val searchQuery = searchQueryAndBanks.first
|
||||||
val localBanks = searchQueryAndBanks.second.map {
|
val localBanks = searchQueryAndBanks.second.map {
|
||||||
@ -55,7 +64,7 @@ class LinkBankAccountViewModel @Inject constructor(
|
|||||||
addAll(localBanks)
|
addAll(localBanks)
|
||||||
}.distinctBy { it.name }
|
}.distinctBy { it.name }
|
||||||
BankUiState.Success(
|
BankUiState.Success(
|
||||||
banks.filter { it.name.contains(searchQuery.lowercase(), ignoreCase = true) }
|
banks.filter { it.name.contains(searchQuery.lowercase(), ignoreCase = true) },
|
||||||
)
|
)
|
||||||
}.stateIn(
|
}.stateIn(
|
||||||
scope = viewModelScope,
|
scope = viewModelScope,
|
||||||
@ -70,17 +79,20 @@ class LinkBankAccountViewModel @Inject constructor(
|
|||||||
Bank("PNB Bank", R.drawable.feature_accounts_logo_pnb, BankType.POPULAR),
|
Bank("PNB Bank", R.drawable.feature_accounts_logo_pnb, BankType.POPULAR),
|
||||||
Bank("HDFC Bank", R.drawable.feature_accounts_logo_hdfc, 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("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) {
|
fun fetchBankAccountDetails(onBankDetailsSuccess: () -> Unit) {
|
||||||
// TODO:: UPI API implement, Implement with real API,
|
// TODO:: UPI API implement, Implement with real API,
|
||||||
// It revert back to Account Screen after successful BankAccount Add
|
// It revert back to Account Screen after successful BankAccount Add
|
||||||
_bankAccountDetails.update {
|
accountDetails.update {
|
||||||
BankAccountDetails(
|
BankAccountDetails(
|
||||||
selectedBank?.name, "Ankur Sharma", "New Delhi",
|
selectedBank?.name,
|
||||||
mRandom.nextInt().toString() + " ", "Savings"
|
"Ankur Sharma",
|
||||||
|
"New Delhi",
|
||||||
|
mRandom.nextInt().toString() + " ",
|
||||||
|
"Savings",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onBankDetailsSuccess.invoke()
|
onBankDetailsSuccess.invoke()
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.bank.accounts.link
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
@ -20,6 +29,6 @@ class LinkBankUiStatePreviewParameterProvider : PreviewParameterProvider<BankUiS
|
|||||||
}
|
}
|
||||||
|
|
||||||
override val values: Sequence<BankUiState> = sequenceOf(
|
override val values: Sequence<BankUiState> = sequenceOf(
|
||||||
BankUiState.Success(banks = banks)
|
BankUiState.Success(banks = banks),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.bank.accounts.navigation
|
||||||
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
@ -16,14 +25,14 @@ fun NavGraphBuilder.bankAccountDetailScreen(
|
|||||||
onSetupUpiPin: (BankAccountDetails, Int) -> Unit,
|
onSetupUpiPin: (BankAccountDetails, Int) -> Unit,
|
||||||
onChangeUpiPin: (BankAccountDetails, Int) -> Unit,
|
onChangeUpiPin: (BankAccountDetails, Int) -> Unit,
|
||||||
onForgotUpiPin: (BankAccountDetails, Int) -> Unit,
|
onForgotUpiPin: (BankAccountDetails, Int) -> Unit,
|
||||||
onBackClick: (BankAccountDetails, Int) -> Unit
|
onBackClick: (BankAccountDetails, Int) -> Unit,
|
||||||
) {
|
) {
|
||||||
composable(
|
composable(
|
||||||
route = "$BANK_ACCOUNT_DETAIL_ROUTE/{${Constants.BANK_ACCOUNT_DETAILS}}/{${Constants.INDEX}}",
|
route = "$BANK_ACCOUNT_DETAIL_ROUTE/{${Constants.BANK_ACCOUNT_DETAILS}}/{${Constants.INDEX}}",
|
||||||
arguments = listOf(
|
arguments = listOf(
|
||||||
navArgument(Constants.BANK_ACCOUNT_DETAILS) { type = NavType.StringType },
|
navArgument(Constants.BANK_ACCOUNT_DETAILS) { type = NavType.StringType },
|
||||||
navArgument(Constants.INDEX) { type = NavType.IntType }
|
navArgument(Constants.INDEX) { type = NavType.IntType },
|
||||||
)
|
),
|
||||||
) { backStackEntry ->
|
) { backStackEntry ->
|
||||||
val bankAccountDetails =
|
val bankAccountDetails =
|
||||||
backStackEntry.arguments?.getParcelable(Constants.BANK_ACCOUNT_DETAILS)
|
backStackEntry.arguments?.getParcelable(Constants.BANK_ACCOUNT_DETAILS)
|
||||||
@ -47,7 +56,7 @@ fun NavGraphBuilder.bankAccountDetailScreen(
|
|||||||
// TODO: Use global snackbar
|
// TODO: Use global snackbar
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
navigateBack = { onBackClick(bankAccountDetails, index) }
|
navigateBack = { onBackClick(bankAccountDetails, index) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,7 +64,7 @@ fun NavGraphBuilder.bankAccountDetailScreen(
|
|||||||
fun NavController.navigateToBankAccountDetail(
|
fun NavController.navigateToBankAccountDetail(
|
||||||
bankAccountDetails: BankAccountDetails,
|
bankAccountDetails: BankAccountDetails,
|
||||||
index: Int,
|
index: Int,
|
||||||
navOptions: NavOptions? = null
|
navOptions: NavOptions? = null,
|
||||||
) {
|
) {
|
||||||
this.navigate("$BANK_ACCOUNT_DETAIL_ROUTE/$bankAccountDetails/$index", navOptions)
|
this.navigate("$BANK_ACCOUNT_DETAIL_ROUTE/$bankAccountDetails/$index", navOptions)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.bank.accounts.navigation
|
||||||
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
@ -12,11 +21,11 @@ fun NavController.navigateToLinkBankAccount(navOptions: NavOptions? = null) =
|
|||||||
navigate(LINK_BANK_ACCOUNT_ROUTE, navOptions)
|
navigate(LINK_BANK_ACCOUNT_ROUTE, navOptions)
|
||||||
|
|
||||||
fun NavGraphBuilder.linkBankAccountScreen(
|
fun NavGraphBuilder.linkBankAccountScreen(
|
||||||
onBackClick: () -> Unit
|
onBackClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
composable(route = LINK_BANK_ACCOUNT_ROUTE) {
|
composable(route = LINK_BANK_ACCOUNT_ROUTE) {
|
||||||
LinkBankAccountRoute(
|
LinkBankAccountRoute(
|
||||||
onBackClick = onBackClick
|
onBackClick = onBackClick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
|
|||||||
@ -1,4 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="60dp"
|
android:width="60dp"
|
||||||
android:height="60dp"
|
android:height="60dp"
|
||||||
|
|||||||
@ -1,4 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="60dp"
|
android:width="60dp"
|
||||||
android:height="60dp"
|
android:height="60dp"
|
||||||
|
|||||||
@ -1,4 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<color name="feature_accounts_colorBlack87">#DE000000</color>
|
<color name="feature_accounts_colorBlack87">#DE000000</color>
|
||||||
<color name="feature_accounts_colorTextPrimary">@color/feature_accounts_colorBlack87</color>
|
<color name="feature_accounts_colorTextPrimary">@color/feature_accounts_colorBlack87</color>
|
||||||
|
|||||||
@ -1,4 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<string name="feature_accounts_bank_account_details">Bank Account Details</string>
|
<string name="feature_accounts_bank_account_details">Bank Account Details</string>
|
||||||
<string name="feature_accounts_bank_name">Bank Name</string>
|
<string name="feature_accounts_bank_name">Bank Name</string>
|
||||||
|
|||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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 {
|
plugins {
|
||||||
alias(libs.plugins.mifospay.android.feature)
|
alias(libs.plugins.mifospay.android.feature)
|
||||||
alias(libs.plugins.mifospay.android.library.compose)
|
alias(libs.plugins.mifospay.android.library.compose)
|
||||||
|
|||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@ -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
|
package org.mifospay.feature.auth.login
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.auth.login
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@ -53,8 +62,9 @@ import org.mifospay.feature.auth.socialSignup.SocialSignupMethodContentScreen
|
|||||||
import org.mifospay.feature.passcode.PassCodeActivity
|
import org.mifospay.feature.passcode.PassCodeActivity
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LoginScreen(
|
internal fun LoginScreen(
|
||||||
viewModel: LoginViewModel = hiltViewModel()
|
modifier: Modifier = Modifier,
|
||||||
|
viewModel: LoginViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val showProgress by viewModel.showProgress.collectAsStateWithLifecycle()
|
val showProgress by viewModel.showProgress.collectAsStateWithLifecycle()
|
||||||
@ -65,6 +75,7 @@ fun LoginScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
LoginScreenContent(
|
LoginScreenContent(
|
||||||
|
modifier = modifier,
|
||||||
showProgress = showProgress,
|
showProgress = showProgress,
|
||||||
login = { username, password ->
|
login = { username, password ->
|
||||||
viewModel.loginUser(
|
viewModel.loginUser(
|
||||||
@ -72,9 +83,9 @@ fun LoginScreen(
|
|||||||
password = password,
|
password = password,
|
||||||
onLoginFailed = { message ->
|
onLoginFailed = { message ->
|
||||||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if (isLoginSuccess) {
|
if (isLoginSuccess) {
|
||||||
@ -84,20 +95,21 @@ fun LoginScreen(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod")
|
||||||
fun LoginScreenContent(
|
private fun LoginScreenContent(
|
||||||
showProgress: Boolean,
|
showProgress: Boolean,
|
||||||
login: (username: String, password: String) -> Unit,
|
login: (username: String, password: String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
var showSignUpScreen by rememberSaveable { mutableStateOf(false) }
|
var showSignUpScreen by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
var userName by rememberSaveable(stateSaver = TextFieldValue.Saver) {
|
var userName by rememberSaveable(stateSaver = TextFieldValue.Saver) {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
TextFieldValue("")
|
TextFieldValue(""),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
var password by rememberSaveable(stateSaver = TextFieldValue.Saver) {
|
var password by rememberSaveable(stateSaver = TextFieldValue.Saver) {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
TextFieldValue("")
|
TextFieldValue(""),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
var passwordVisibility: Boolean by remember { mutableStateOf(false) }
|
var passwordVisibility: Boolean by remember { mutableStateOf(false) }
|
||||||
@ -108,25 +120,25 @@ fun LoginScreenContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Box {
|
Box(modifier) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.background(MaterialTheme.colorScheme.surface)
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
.padding(top = 100.dp, start = 48.dp, end = 48.dp),
|
.padding(top = 100.dp, start = 48.dp, end = 48.dp),
|
||||||
horizontalAlignment = Alignment.Start
|
horizontalAlignment = Alignment.Start,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_auth_login),
|
text = stringResource(id = R.string.feature_auth_login),
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
color = MaterialTheme.colorScheme.primary
|
color = MaterialTheme.colorScheme.primary,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(top = 32.dp),
|
.padding(top = 32.dp),
|
||||||
text = stringResource(id = R.string.feature_auth_welcome_back),
|
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))
|
Spacer(modifier = Modifier.padding(top = 32.dp))
|
||||||
MifosOutlinedTextField(
|
MifosOutlinedTextField(
|
||||||
@ -135,7 +147,7 @@ fun LoginScreenContent(
|
|||||||
onValueChange = {
|
onValueChange = {
|
||||||
userName = it
|
userName = it
|
||||||
},
|
},
|
||||||
label = R.string.feature_auth_username
|
label = R.string.feature_auth_username,
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.padding(top = 16.dp))
|
Spacer(modifier = Modifier.padding(top = 16.dp))
|
||||||
MifosOutlinedTextField(
|
MifosOutlinedTextField(
|
||||||
@ -147,15 +159,19 @@ fun LoginScreenContent(
|
|||||||
label = R.string.feature_auth_password,
|
label = R.string.feature_auth_password,
|
||||||
visualTransformation = if (passwordVisibility) {
|
visualTransformation = if (passwordVisibility) {
|
||||||
VisualTransformation.None
|
VisualTransformation.None
|
||||||
} else PasswordVisualTransformation(),
|
} else {
|
||||||
|
PasswordVisualTransformation()
|
||||||
|
},
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
val image = if (passwordVisibility)
|
val image = if (passwordVisibility) {
|
||||||
Icons.Filled.Visibility
|
Icons.Filled.Visibility
|
||||||
else Icons.Filled.VisibilityOff
|
} else {
|
||||||
|
Icons.Filled.VisibilityOff
|
||||||
|
}
|
||||||
IconButton(onClick = { passwordVisibility = !passwordVisibility }) {
|
IconButton(onClick = { passwordVisibility = !passwordVisibility }) {
|
||||||
Icon(imageVector = image, null)
|
Icon(imageVector = image, null)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
Button(
|
Button(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -166,12 +182,12 @@ fun LoginScreenContent(
|
|||||||
onClick = {
|
onClick = {
|
||||||
login.invoke(userName.text, password.text)
|
login.invoke(userName.text, password.text)
|
||||||
},
|
},
|
||||||
contentPadding = PaddingValues(12.dp)
|
contentPadding = PaddingValues(12.dp),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_auth_login).uppercase(),
|
text = stringResource(id = R.string.feature_auth_login).uppercase(),
|
||||||
style = MaterialTheme.typography.labelLarge,
|
style = MaterialTheme.typography.labelLarge,
|
||||||
color = MaterialTheme.colorScheme.onPrimary
|
color = MaterialTheme.colorScheme.onPrimary,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Hide reset password for now
|
// Hide reset password for now
|
||||||
@ -197,12 +213,12 @@ fun LoginScreenContent(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 24.dp),
|
.padding(top = 24.dp),
|
||||||
horizontalArrangement = Arrangement.Center
|
horizontalArrangement = Arrangement.Center,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "Don’t have an account yet? ",
|
text = "Don’t have an account yet? ",
|
||||||
style = MaterialTheme.typography.labelLarge,
|
style = MaterialTheme.typography.labelLarge,
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.clickable {
|
modifier = Modifier.clickable {
|
||||||
@ -218,11 +234,10 @@ fun LoginScreenContent(
|
|||||||
|
|
||||||
if (showProgress) {
|
if (showProgress) {
|
||||||
MfOverlayLoadingWheel(
|
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")
|
@Preview(showSystemUi = true, device = "id:pixel_5")
|
||||||
@Composable
|
@Composable
|
||||||
fun LoanScreenPreview() {
|
private fun LoanScreenPreview() {
|
||||||
MifosTheme {
|
MifosTheme {
|
||||||
LoginScreenContent(
|
LoginScreenContent(
|
||||||
showProgress = false,
|
showProgress = false,
|
||||||
login = { _, _ -> }
|
login = { _, _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.auth.login
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@ -43,7 +52,6 @@ class LoginViewModel @Inject constructor(
|
|||||||
_isLoginSuccess.update { isLoginSuccess }
|
_isLoginSuccess.update { isLoginSuccess }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authenticate User with username and password
|
* Authenticate User with username and password
|
||||||
* @param username
|
* @param username
|
||||||
@ -53,14 +61,16 @@ class LoginViewModel @Inject constructor(
|
|||||||
fun loginUser(
|
fun loginUser(
|
||||||
username: String,
|
username: String,
|
||||||
password: String,
|
password: String,
|
||||||
onLoginFailed: (String) -> Unit
|
onLoginFailed: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
updateProgressState(true)
|
updateProgressState(true)
|
||||||
authenticateUserUseCase.walletRequestValues =
|
authenticateUserUseCase.walletRequestValues =
|
||||||
AuthenticateUser.RequestValues(username, password)
|
AuthenticateUser.RequestValues(username, password)
|
||||||
|
|
||||||
val requestValue = authenticateUserUseCase.walletRequestValues
|
val requestValue = authenticateUserUseCase.walletRequestValues
|
||||||
mUsecaseHandler.execute(authenticateUserUseCase, requestValue,
|
mUsecaseHandler.execute(
|
||||||
|
authenticateUserUseCase,
|
||||||
|
requestValue,
|
||||||
object : UseCaseCallback<AuthenticateUser.ResponseValue> {
|
object : UseCaseCallback<AuthenticateUser.ResponseValue> {
|
||||||
override fun onSuccess(response: AuthenticateUser.ResponseValue) {
|
override fun onSuccess(response: AuthenticateUser.ResponseValue) {
|
||||||
saveAuthTokenInPref(response.user)
|
saveAuthTokenInPref(response.user)
|
||||||
@ -72,16 +82,17 @@ class LoginViewModel @Inject constructor(
|
|||||||
updateProgressState(false)
|
updateProgressState(false)
|
||||||
onLoginFailed(message)
|
onLoginFailed(message)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch user details return by authenticated user
|
* Fetch user details return by authenticated user
|
||||||
* @param user
|
* @param user
|
||||||
*/
|
*/
|
||||||
private fun fetchUserDetails(user: User) {
|
private fun fetchUserDetails(user: User) {
|
||||||
mUsecaseHandler.execute(fetchUserDetailsUseCase,
|
mUsecaseHandler.execute(
|
||||||
|
fetchUserDetailsUseCase,
|
||||||
FetchUserDetails.RequestValues(user.userId),
|
FetchUserDetails.RequestValues(user.userId),
|
||||||
object : UseCaseCallback<FetchUserDetails.ResponseValue> {
|
object : UseCaseCallback<FetchUserDetails.ResponseValue> {
|
||||||
override fun onSuccess(response: FetchUserDetails.ResponseValue) {
|
override fun onSuccess(response: FetchUserDetails.ResponseValue) {
|
||||||
@ -92,7 +103,8 @@ class LoginViewModel @Inject constructor(
|
|||||||
updateProgressState(false)
|
updateProgressState(false)
|
||||||
Log.d("Login User Detailed: ", message)
|
Log.d("Login User Detailed: ", message)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -116,7 +128,8 @@ class LoginViewModel @Inject constructor(
|
|||||||
override fun onError(message: String) {
|
override fun onError(message: String) {
|
||||||
updateProgressState(false)
|
updateProgressState(false)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveAuthTokenInPref(user: User) {
|
private fun saveAuthTokenInPref(user: User) {
|
||||||
@ -128,7 +141,7 @@ class LoginViewModel @Inject constructor(
|
|||||||
*/
|
*/
|
||||||
private fun saveUserDetails(
|
private fun saveUserDetails(
|
||||||
user: User,
|
user: User,
|
||||||
userWithRole: UserWithRole
|
userWithRole: UserWithRole,
|
||||||
) {
|
) {
|
||||||
val userName = user.username
|
val userName = user.username
|
||||||
val userID = user.userId
|
val userID = user.userId
|
||||||
@ -147,4 +160,4 @@ class LoginViewModel @Inject constructor(
|
|||||||
preferencesHelper.saveMobile(client.mobileNo)
|
preferencesHelper.saveMobile(client.mobileNo)
|
||||||
preferencesHelper.client = client
|
preferencesHelper.client = client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.auth.mobileVerify
|
||||||
|
|
||||||
import android.widget.Toast
|
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.core.designsystem.theme.MifosTheme
|
||||||
import org.mifospay.feature.auth.R
|
import org.mifospay.feature.auth.R
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MobileVerificationScreen(
|
internal fun MobileVerificationScreen(
|
||||||
|
onOtpVerificationSuccess: (String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
viewModel: MobileVerificationViewModel = hiltViewModel(),
|
viewModel: MobileVerificationViewModel = hiltViewModel(),
|
||||||
onOtpVerificationSuccess: (String) -> Unit
|
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
MobileVerificationScreen(uiState = uiState,
|
MobileVerificationScreen(
|
||||||
|
uiState = uiState,
|
||||||
showProgressState = viewModel.showProgress,
|
showProgressState = viewModel.showProgress,
|
||||||
verifyMobileAndRequestOtp = { phone, fullPhone ->
|
verifyMobileAndRequestOtp = { phone, fullPhone ->
|
||||||
viewModel.verifyMobileAndRequestOtp(fullPhone, phone) {
|
viewModel.verifyMobileAndRequestOtp(fullPhone, phone) {
|
||||||
@ -63,18 +73,19 @@ fun MobileVerificationScreen(
|
|||||||
viewModel.verifyOTP(validatedOtp) {
|
viewModel.verifyOTP(validatedOtp) {
|
||||||
onOtpVerificationSuccess(fullNumber)
|
onOtpVerificationSuccess(fullNumber)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MobileVerificationScreen(
|
private fun MobileVerificationScreen(
|
||||||
uiState: MobileVerificationUiState,
|
uiState: MobileVerificationUiState,
|
||||||
showProgressState: Boolean = false,
|
|
||||||
verifyMobileAndRequestOtp: (String, String) -> Unit,
|
verifyMobileAndRequestOtp: (String, String) -> Unit,
|
||||||
verifyOtp: (String, String) -> Unit
|
verifyOtp: (String, String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
showProgressState: Boolean = false,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
var phoneNumber by rememberSaveable { mutableStateOf("") }
|
var phoneNumber by rememberSaveable { mutableStateOf("") }
|
||||||
var fullPhoneNumber by rememberSaveable { mutableStateOf("") }
|
var fullPhoneNumber by rememberSaveable { mutableStateOf("") }
|
||||||
var isNumberValid: Boolean by rememberSaveable { mutableStateOf(false) }
|
var isNumberValid: Boolean by rememberSaveable { mutableStateOf(false) }
|
||||||
@ -90,19 +101,18 @@ fun MobileVerificationScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Box {
|
Box(modifier) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(color = Color.White)
|
.background(color = Color.White)
|
||||||
.focusable(!showProgressState),
|
.focusable(!showProgressState),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.background(color = MaterialTheme.colorScheme.primary),
|
.background(color = MaterialTheme.colorScheme.primary),
|
||||||
verticalArrangement = Arrangement.Top
|
verticalArrangement = Arrangement.Top,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(top = 48.dp, start = 24.dp, end = 24.dp),
|
modifier = Modifier.padding(top = 48.dp, start = 24.dp, end = 24.dp),
|
||||||
@ -111,11 +121,14 @@ fun MobileVerificationScreen(
|
|||||||
} else {
|
} else {
|
||||||
stringResource(id = R.string.feature_auth_enter_otp)
|
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(
|
Text(
|
||||||
modifier = Modifier.padding(
|
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) {
|
text = if (uiState == MobileVerificationUiState.VerifyPhone) {
|
||||||
stringResource(id = R.string.feature_auth_enter_mobile_number_description)
|
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)
|
stringResource(id = R.string.feature_auth_enter_otp_received_on_your_registered_device)
|
||||||
},
|
},
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.onPrimary
|
color = MaterialTheme.colorScheme.onPrimary,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +150,7 @@ fun MobileVerificationScreen(
|
|||||||
phoneNumber = phone
|
phoneNumber = phone
|
||||||
fullPhoneNumber = fullPhone
|
fullPhoneNumber = fullPhone
|
||||||
isNumberValid = valid
|
isNumberValid = valid
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,11 +158,12 @@ fun MobileVerificationScreen(
|
|||||||
EnterOtpScreen(
|
EnterOtpScreen(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 48.dp, vertical = 24.dp)
|
.padding(horizontal = 48.dp, vertical = 24.dp),
|
||||||
) { isValidated, otp ->
|
onOtpValidated = { isValidated, otp ->
|
||||||
isOtpValidated = isValidated
|
isOtpValidated = isValidated
|
||||||
validatedOtp = otp
|
validatedOtp = otp
|
||||||
}
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,8 +185,9 @@ fun MobileVerificationScreen(
|
|||||||
stringResource(id = R.string.feature_auth_verify_phone).uppercase()
|
stringResource(id = R.string.feature_auth_verify_phone).uppercase()
|
||||||
} else {
|
} else {
|
||||||
stringResource(id = R.string.feature_auth_verify_otp).uppercase()
|
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
|
@Composable
|
||||||
fun EnterPhoneScreen(
|
private fun EnterPhoneScreen(
|
||||||
modifier: Modifier,
|
onNumberUpdated: (String, String, Boolean) -> Unit,
|
||||||
onNumberUpdated: (String, String, Boolean) -> Unit
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val keyboardController = LocalSoftwareKeyboardController.current
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
TogiCountryCodePicker(
|
TogiCountryCodePicker(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
shape = RoundedCornerShape(8.dp),
|
shape = RoundedCornerShape(8.dp),
|
||||||
colors = TextFieldDefaults.outlinedTextFieldColors(
|
colors = TextFieldDefaults.outlinedTextFieldColors(
|
||||||
focusedBorderColor = MaterialTheme.colorScheme.primary
|
focusedBorderColor = MaterialTheme.colorScheme.primary,
|
||||||
),
|
),
|
||||||
onValueChange = { (code, phone), isValid ->
|
onValueChange = { (code, phone), isValid ->
|
||||||
onNumberUpdated(phone, code + phone, isValid)
|
onNumberUpdated(phone, code + phone, isValid)
|
||||||
},
|
},
|
||||||
label = { Text(stringResource(id = R.string.feature_auth_phone_number)) },
|
label = { Text(stringResource(id = R.string.feature_auth_phone_number)) },
|
||||||
keyboardActions = KeyboardActions { keyboardController?.hide() }
|
keyboardActions = KeyboardActions { keyboardController?.hide() },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun EnterOtpScreen(
|
private fun EnterOtpScreen(
|
||||||
modifier: Modifier,
|
onOtpValidated: (Boolean, String) -> Unit,
|
||||||
onOtpValidated: (Boolean, String) -> Unit
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val keyboardController = LocalSoftwareKeyboardController.current
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
var otp by rememberSaveable(stateSaver = TextFieldValue.Saver) {
|
var otp by rememberSaveable(stateSaver = TextFieldValue.Saver) {
|
||||||
@ -221,20 +236,21 @@ fun EnterOtpScreen(
|
|||||||
onOtpValidated(otp.text.length == 6, otp.text)
|
onOtpValidated(otp.text.length == 6, otp.text)
|
||||||
},
|
},
|
||||||
label = R.string.feature_auth_enter_otp,
|
label = R.string.feature_auth_enter_otp,
|
||||||
keyboardActions = KeyboardActions { keyboardController?.hide() }
|
keyboardActions = KeyboardActions { keyboardController?.hide() },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ShowProgressScreen(
|
private fun ShowProgressScreen(
|
||||||
uiState: MobileVerificationUiState,
|
uiState: MobileVerificationUiState,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(color = MaterialTheme.colorScheme.primary.copy(alpha = 0.6f))
|
.background(color = MaterialTheme.colorScheme.primary.copy(alpha = 0.6f))
|
||||||
.focusable(),
|
.focusable(),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
MifosLoadingWheel(
|
MifosLoadingWheel(
|
||||||
modifier = Modifier.wrapContentSize(),
|
modifier = Modifier.wrapContentSize(),
|
||||||
@ -242,31 +258,33 @@ fun ShowProgressScreen(
|
|||||||
Constants.SENDING_OTP_TO_YOUR_MOBILE_NUMBER
|
Constants.SENDING_OTP_TO_YOUR_MOBILE_NUMBER
|
||||||
} else {
|
} else {
|
||||||
Constants.VERIFYING_OTP
|
Constants.VERIFYING_OTP
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun MobileVerificationScreenVerifyPhonePreview() {
|
private fun MobileVerificationScreenVerifyPhonePreview() {
|
||||||
MifosTheme {
|
MifosTheme {
|
||||||
MobileVerificationScreen(uiState = MobileVerificationUiState.VerifyPhone,
|
MobileVerificationScreen(
|
||||||
|
uiState = MobileVerificationUiState.VerifyPhone,
|
||||||
showProgressState = false,
|
showProgressState = false,
|
||||||
verifyMobileAndRequestOtp = { _, _ -> },
|
verifyMobileAndRequestOtp = { _, _ -> },
|
||||||
verifyOtp = { _, _ -> }
|
verifyOtp = { _, _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun MobileVerificationScreenVerifyOtpPreview() {
|
private fun MobileVerificationScreenVerifyOtpPreview() {
|
||||||
MifosTheme {
|
MifosTheme {
|
||||||
MobileVerificationScreen(uiState = MobileVerificationUiState.VerifyOtp,
|
MobileVerificationScreen(
|
||||||
|
uiState = MobileVerificationUiState.VerifyOtp,
|
||||||
showProgressState = false,
|
showProgressState = false,
|
||||||
verifyMobileAndRequestOtp = { _, _ -> },
|
verifyMobileAndRequestOtp = { _, _ -> },
|
||||||
verifyOtp = { _, _ -> }
|
verifyOtp = { _, _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.auth.mobileVerify
|
||||||
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@ -20,7 +29,7 @@ import javax.inject.Inject
|
|||||||
@Suppress("UnusedParameter")
|
@Suppress("UnusedParameter")
|
||||||
class MobileVerificationViewModel @Inject constructor(
|
class MobileVerificationViewModel @Inject constructor(
|
||||||
private val mUseCaseHandler: UseCaseHandler,
|
private val mUseCaseHandler: UseCaseHandler,
|
||||||
private val searchClientUseCase: SearchClient
|
private val searchClientUseCase: SearchClient,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _uiState =
|
private val _uiState =
|
||||||
@ -35,10 +44,11 @@ class MobileVerificationViewModel @Inject constructor(
|
|||||||
fun verifyMobileAndRequestOtp(
|
fun verifyMobileAndRequestOtp(
|
||||||
fullNumber: String,
|
fullNumber: String,
|
||||||
mobileNo: String,
|
mobileNo: String,
|
||||||
onError: (String?) -> Unit
|
onError: (String?) -> Unit,
|
||||||
) {
|
) {
|
||||||
showProgress = true
|
showProgress = true
|
||||||
mUseCaseHandler.execute(searchClientUseCase,
|
mUseCaseHandler.execute(
|
||||||
|
searchClientUseCase,
|
||||||
fullNumber.let { SearchClient.RequestValues(it) },
|
fullNumber.let { SearchClient.RequestValues(it) },
|
||||||
object : UseCase.UseCaseCallback<SearchClient.ResponseValue> {
|
object : UseCase.UseCaseCallback<SearchClient.ResponseValue> {
|
||||||
override fun onSuccess(response: SearchClient.ResponseValue) {
|
override fun onSuccess(response: SearchClient.ResponseValue) {
|
||||||
@ -49,7 +59,8 @@ class MobileVerificationViewModel @Inject constructor(
|
|||||||
override fun onError(message: String) {
|
override fun onError(message: String) {
|
||||||
requestOtp(fullNumber)
|
requestOtp(fullNumber)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.auth.navigation
|
||||||
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
@ -10,7 +19,7 @@ const val LOGIN_ROUTE = "login_route"
|
|||||||
@Suppress("UnusedParameter")
|
@Suppress("UnusedParameter")
|
||||||
fun NavGraphBuilder.loginScreen(
|
fun NavGraphBuilder.loginScreen(
|
||||||
onDismissSignUp: () -> Unit,
|
onDismissSignUp: () -> Unit,
|
||||||
onNavigateToMobileVerificationScreen:(Int,String,String,String,String,) -> Unit
|
onNavigateToMobileVerificationScreen: (Int, String, String, String, String) -> Unit,
|
||||||
) {
|
) {
|
||||||
composable(route = LOGIN_ROUTE) {
|
composable(route = LOGIN_ROUTE) {
|
||||||
LoginScreen(
|
LoginScreen(
|
||||||
@ -22,4 +31,4 @@ fun NavGraphBuilder.loginScreen(
|
|||||||
|
|
||||||
fun NavController.navigateToLogin() {
|
fun NavController.navigateToLogin() {
|
||||||
this.navigate(LOGIN_ROUTE)
|
this.navigate(LOGIN_ROUTE)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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")
|
@file:Suppress("MaxLineLength")
|
||||||
|
|
||||||
package org.mifospay.feature.auth.navigation
|
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"
|
const val MOBILE_VERIFICATION_ROUTE = "mobile_verification_route"
|
||||||
|
|
||||||
fun NavGraphBuilder.mobileVerificationScreen(
|
fun NavGraphBuilder.mobileVerificationScreen(
|
||||||
onOtpVerificationSuccess: (String, Map<String, Any?>) -> Unit
|
onOtpVerificationSuccess: (String, Map<String, Any?>) -> Unit,
|
||||||
) {
|
) {
|
||||||
composable(
|
composable(
|
||||||
route = "$MOBILE_VERIFICATION_ROUTE?mifosSignedUp={mifosSignedUp}&googleDisplayName={googleDisplayName}&googleEmail={googleEmail}&googleFamilyName={googleFamilyName}&googleGivenName={googleGivenName}",
|
route = "$MOBILE_VERIFICATION_ROUTE?mifosSignedUp={mifosSignedUp}&googleDisplayName={googleDisplayName}&googleEmail={googleEmail}&googleFamilyName={googleFamilyName}&googleGivenName={googleGivenName}",
|
||||||
arguments = listOf(
|
arguments = listOf(
|
||||||
navArgument("mifosSignedUp") { type = NavType.IntType; defaultValue = 0 },
|
navArgument("mifosSignedUp") {
|
||||||
navArgument("googleDisplayName") { type = NavType.StringType; nullable = true },
|
type = NavType.IntType
|
||||||
navArgument("googleEmail") { type = NavType.StringType; nullable = true },
|
defaultValue = 0
|
||||||
navArgument("googleFamilyName") { type = NavType.StringType; nullable = true },
|
},
|
||||||
navArgument("googleGivenName") { type = NavType.StringType; nullable = true }
|
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 ->
|
) { backStackEntry ->
|
||||||
val mifosSignedUp = backStackEntry.arguments?.getInt("mifosSignedUp") ?: 0
|
val mifosSignedUp = backStackEntry.arguments?.getInt("mifosSignedUp") ?: 0
|
||||||
val googleDisplayName = backStackEntry.arguments?.getString("googleDisplayName")
|
val googleDisplayName = backStackEntry.arguments?.getString("googleDisplayName")
|
||||||
@ -40,10 +64,10 @@ fun NavGraphBuilder.mobileVerificationScreen(
|
|||||||
Constants.GOOGLE_FAMILY_NAME to googleFamilyName,
|
Constants.GOOGLE_FAMILY_NAME to googleFamilyName,
|
||||||
Constants.GOOGLE_GIVEN_NAME to googleGivenName,
|
Constants.GOOGLE_GIVEN_NAME to googleGivenName,
|
||||||
Constants.COUNTRY to "Canada",
|
Constants.COUNTRY to "Canada",
|
||||||
Constants.MOBILE_NUMBER to fullNumber
|
Constants.MOBILE_NUMBER to fullNumber,
|
||||||
)
|
)
|
||||||
onOtpVerificationSuccess(fullNumber, extraData)
|
onOtpVerificationSuccess(fullNumber, extraData)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,8 +77,7 @@ fun NavController.navigateToMobileVerification(
|
|||||||
googleDisplayName: String?,
|
googleDisplayName: String?,
|
||||||
googleEmail: String?,
|
googleEmail: String?,
|
||||||
googleFamilyName: String?,
|
googleFamilyName: String?,
|
||||||
googleGivenName: String?
|
googleGivenName: String?,
|
||||||
) {
|
) {
|
||||||
this.navigate("$MOBILE_VERIFICATION_ROUTE?mifosSignedUp=$mifosSignedUp&googleDisplayName=$googleDisplayName&googleEmail=$googleEmail&googleFamilyName=$googleFamilyName&googleGivenName=$googleGivenName")
|
this.navigate("$MOBILE_VERIFICATION_ROUTE?mifosSignedUp=$mifosSignedUp&googleDisplayName=$googleDisplayName&googleEmail=$googleEmail&googleFamilyName=$googleFamilyName&googleGivenName=$googleGivenName")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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")
|
@file:Suppress("MaxLineLength")
|
||||||
|
|
||||||
package org.mifospay.feature.auth.navigation
|
package org.mifospay.feature.auth.navigation
|
||||||
@ -14,19 +23,40 @@ const val SIGNUP_ROUTE = "signup_route"
|
|||||||
@Suppress("UnusedParameter")
|
@Suppress("UnusedParameter")
|
||||||
fun NavGraphBuilder.signupScreen(
|
fun NavGraphBuilder.signupScreen(
|
||||||
onLoginSuccess: () -> Unit,
|
onLoginSuccess: () -> Unit,
|
||||||
onRegisterSuccess: () -> Unit
|
onRegisterSuccess: () -> Unit,
|
||||||
) {
|
) {
|
||||||
composable(
|
composable(
|
||||||
route = "$SIGNUP_ROUTE?savingProductId={savingProductId}&mobileNumber={mobileNumber}&country={country}&email={email}&firstName={firstName}&lastName={lastName}&businessName={businessName}",
|
route = "$SIGNUP_ROUTE?savingProductId={savingProductId}&mobileNumber={mobileNumber}&country={country}&email={email}&firstName={firstName}&lastName={lastName}&businessName={businessName}",
|
||||||
arguments = listOf(
|
arguments = listOf(
|
||||||
navArgument("savingProductId") { type = NavType.IntType; defaultValue = 0 },
|
navArgument("savingProductId") {
|
||||||
navArgument("mobileNumber") { type = NavType.StringType; defaultValue = "" },
|
type = NavType.IntType
|
||||||
navArgument("country") { type = NavType.StringType; defaultValue = "" },
|
defaultValue = 0
|
||||||
navArgument("email") { type = NavType.StringType; defaultValue = "" },
|
},
|
||||||
navArgument("firstName") { type = NavType.StringType; defaultValue = "" },
|
navArgument("mobileNumber") {
|
||||||
navArgument("lastName") { type = NavType.StringType; defaultValue = "" },
|
type = NavType.StringType
|
||||||
navArgument("businessName") { type = NavType.StringType; defaultValue = "" }
|
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 ->
|
) { backStackEntry ->
|
||||||
val savingProductId = backStackEntry.arguments?.getInt("savingProductId") ?: 0
|
val savingProductId = backStackEntry.arguments?.getInt("savingProductId") ?: 0
|
||||||
val mobileNumber = backStackEntry.arguments?.getString("mobileNumber") ?: ""
|
val mobileNumber = backStackEntry.arguments?.getString("mobileNumber") ?: ""
|
||||||
@ -44,7 +74,7 @@ fun NavGraphBuilder.signupScreen(
|
|||||||
email = email,
|
email = email,
|
||||||
firstName = firstName,
|
firstName = firstName,
|
||||||
lastName = lastName,
|
lastName = lastName,
|
||||||
businessName = businessName
|
businessName = businessName,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,12 +86,12 @@ fun NavController.navigateToSignup(
|
|||||||
email: String = "",
|
email: String = "",
|
||||||
firstName: String = "",
|
firstName: String = "",
|
||||||
lastName: String = "",
|
lastName: String = "",
|
||||||
businessName: String = ""
|
businessName: String = "",
|
||||||
) {
|
) {
|
||||||
this.navigate(
|
this.navigate(
|
||||||
"$SIGNUP_ROUTE?savingProductId=$savingProductId" +
|
"$SIGNUP_ROUTE?savingProductId=$savingProductId" +
|
||||||
"&mobileNumber=$mobileNumber&country=$country&email=$email" +
|
"&mobileNumber=$mobileNumber&country=$country&email=$email" +
|
||||||
"&firstName=$firstName&lastName=$lastName&businessName=$businessName"
|
"&firstName=$firstName&lastName=$lastName&businessName=$businessName",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,4 +104,4 @@ fun onRegisterSuccess(s: String?) {
|
|||||||
// Toast.makeText(this, "Registered successfully.", Toast.LENGTH_SHORT).show()
|
// Toast.makeText(this, "Registered successfully.", Toast.LENGTH_SHORT).show()
|
||||||
// startActivity(Intent(this@SignupActivity, LoginActivity::class.java))
|
// startActivity(Intent(this@SignupActivity, LoginActivity::class.java))
|
||||||
// finish()
|
// finish()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.auth.signup
|
||||||
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
@ -49,18 +58,18 @@ import org.mifospay.feature.auth.R
|
|||||||
import org.mifospay.feature.auth.utils.ValidateUtil.isValidEmail
|
import org.mifospay.feature.auth.utils.ValidateUtil.isValidEmail
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SignupScreen(
|
internal fun SignupScreen(
|
||||||
viewModel: SignupViewModel = hiltViewModel(),
|
savingProductId: Int,
|
||||||
|
mobileNumber: String,
|
||||||
|
country: String,
|
||||||
|
email: String,
|
||||||
|
firstName: String,
|
||||||
|
lastName: String,
|
||||||
|
businessName: String,
|
||||||
onLoginSuccess: () -> Unit,
|
onLoginSuccess: () -> Unit,
|
||||||
savingProductId:Int,
|
modifier: Modifier = Modifier,
|
||||||
mobileNumber:String,
|
viewModel: SignupViewModel = hiltViewModel(),
|
||||||
country:String,
|
|
||||||
email:String,
|
|
||||||
firstName:String,
|
|
||||||
lastName:String,
|
|
||||||
businessName:String
|
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
@ -72,9 +81,9 @@ fun SignupScreen(
|
|||||||
mobileNumber = mobileNumber,
|
mobileNumber = mobileNumber,
|
||||||
countryName = country,
|
countryName = country,
|
||||||
email = email,
|
email = email,
|
||||||
firstName = firstName,
|
firstName = firstName,
|
||||||
lastName = lastName,
|
lastName = lastName,
|
||||||
businessName = businessName
|
businessName = businessName,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
LaunchedEffect(viewModel.isLoginSuccess) {
|
LaunchedEffect(viewModel.isLoginSuccess) {
|
||||||
@ -84,6 +93,7 @@ fun SignupScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
SignupScreenContent(
|
SignupScreenContent(
|
||||||
|
modifier = modifier,
|
||||||
showProgressState = viewModel.showProgress,
|
showProgressState = viewModel.showProgress,
|
||||||
data = viewModel.signupData,
|
data = viewModel.signupData,
|
||||||
stateList = stateList,
|
stateList = stateList,
|
||||||
@ -91,27 +101,28 @@ fun SignupScreen(
|
|||||||
viewModel.registerUser(it) { message ->
|
viewModel.registerUser(it) { message ->
|
||||||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||||
fun SignupScreenContent(
|
private fun SignupScreenContent(
|
||||||
showProgressState: Boolean = false,
|
|
||||||
data: SignupData,
|
data: SignupData,
|
||||||
stateList: List<State>,
|
stateList: List<State>,
|
||||||
onCompleteRegistration: (SignupData) -> Unit
|
onCompleteRegistration: (SignupData) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
showProgressState: Boolean = false,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
var firstName by rememberSaveable { mutableStateOf(data.firstName ?: "") }
|
var firstName by rememberSaveable { mutableStateOf(data.firstName ?: "") }
|
||||||
var lastName by rememberSaveable { mutableStateOf(data.lastName ?: "") }
|
var lastName by rememberSaveable { mutableStateOf(data.lastName ?: "") }
|
||||||
var email by rememberSaveable { mutableStateOf(data.email ?: "") }
|
var email by rememberSaveable { mutableStateOf(data.email ?: "") }
|
||||||
var userName by rememberSaveable {
|
var userName by rememberSaveable {
|
||||||
mutableStateOf(data.email?.ifEmpty { "" }
|
mutableStateOf(
|
||||||
?: data.email?.let { it.substring(0, it.indexOf('@')) } ?: ""
|
data.email?.ifEmpty { "" }
|
||||||
|
?: data.email?.let { it.substring(0, it.indexOf('@')) } ?: "",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
var addressLine1 by rememberSaveable { mutableStateOf("") }
|
var addressLine1 by rememberSaveable { mutableStateOf("") }
|
||||||
@ -127,16 +138,19 @@ fun SignupScreenContent(
|
|||||||
var selectedState by rememberSaveable { mutableStateOf<State?>(null) }
|
var selectedState by rememberSaveable { mutableStateOf<State?>(null) }
|
||||||
|
|
||||||
fun validateAllFields() {
|
fun validateAllFields() {
|
||||||
val isAnyFieldEmpty = firstName.isEmpty() || lastName.isEmpty() || email.isEmpty()
|
val isAnyFieldEmpty = firstName.isEmpty() || lastName.isEmpty() || email.isEmpty() ||
|
||||||
|| userName.isEmpty() || addressLine1.isEmpty() || addressLine2.isEmpty()
|
userName.isEmpty() || addressLine1.isEmpty() || addressLine2.isEmpty() ||
|
||||||
|| pinCode.isEmpty() || password.isEmpty() || confirmPassword.isEmpty()
|
pinCode.isEmpty() || password.isEmpty() || confirmPassword.isEmpty() ||
|
||||||
|| selectedState == null
|
selectedState == null
|
||||||
val isNameOfBusinessEmpty = data.mifosSavingsProductId == MIFOS_MERCHANT_SAVINGS_PRODUCT_ID
|
|
||||||
&& nameOfBusiness.isEmpty()
|
val isNameOfBusinessEmpty = data.mifosSavingsProductId == MIFOS_MERCHANT_SAVINGS_PRODUCT_ID &&
|
||||||
|
nameOfBusiness.isEmpty()
|
||||||
|
|
||||||
if (!email.isValidEmail()) {
|
if (!email.isValidEmail()) {
|
||||||
Toast.makeText(
|
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()
|
).show()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -145,7 +159,7 @@ fun SignupScreenContent(
|
|||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
context,
|
context,
|
||||||
context.getString(R.string.feature_auth_all_fields_are_mandatory),
|
context.getString(R.string.feature_auth_all_fields_are_mandatory),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT,
|
||||||
).show()
|
).show()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -162,12 +176,12 @@ fun SignupScreenContent(
|
|||||||
pinCode = pinCode,
|
pinCode = pinCode,
|
||||||
businessName = nameOfBusiness,
|
businessName = nameOfBusiness,
|
||||||
password = password,
|
password = password,
|
||||||
stateId = selectedState?.id
|
stateId = selectedState?.id,
|
||||||
)
|
)
|
||||||
onCompleteRegistration.invoke(signUpData)
|
onCompleteRegistration.invoke(signUpData)
|
||||||
}
|
}
|
||||||
|
|
||||||
Box {
|
Box(modifier) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@ -179,33 +193,37 @@ fun SignupScreenContent(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.background(color = MaterialTheme.colorScheme.primary),
|
.background(color = MaterialTheme.colorScheme.primary),
|
||||||
verticalArrangement = Arrangement.Top
|
verticalArrangement = Arrangement.Top,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(top = 48.dp, start = 24.dp, end = 24.dp),
|
modifier = Modifier.padding(top = 48.dp, start = 24.dp, end = 24.dp),
|
||||||
text = stringResource(id = R.string.feature_auth_complete_your_registration),
|
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(
|
Text(
|
||||||
modifier = Modifier.padding(
|
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),
|
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(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 32.dp)
|
.padding(horizontal = 32.dp)
|
||||||
.focusable(!showProgressState)
|
.focusable(!showProgressState),
|
||||||
) {
|
) {
|
||||||
UserInfoTextField(
|
UserInfoTextField(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 16.dp),
|
.padding(top = 16.dp),
|
||||||
label = stringResource(id = R.string.feature_auth_first_name),
|
label = stringResource(id = R.string.feature_auth_first_name),
|
||||||
value = firstName
|
value = firstName,
|
||||||
) {
|
) {
|
||||||
firstName = it
|
firstName = it
|
||||||
}
|
}
|
||||||
@ -214,7 +232,7 @@ fun SignupScreenContent(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 8.dp),
|
.padding(top = 8.dp),
|
||||||
label = stringResource(id = R.string.feature_auth_last_name),
|
label = stringResource(id = R.string.feature_auth_last_name),
|
||||||
value = lastName
|
value = lastName,
|
||||||
) {
|
) {
|
||||||
lastName = it
|
lastName = it
|
||||||
}
|
}
|
||||||
@ -223,7 +241,7 @@ fun SignupScreenContent(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 8.dp),
|
.padding(top = 8.dp),
|
||||||
label = stringResource(id = R.string.feature_auth_username),
|
label = stringResource(id = R.string.feature_auth_username),
|
||||||
value = userName
|
value = userName,
|
||||||
) {
|
) {
|
||||||
userName = it
|
userName = it
|
||||||
}
|
}
|
||||||
@ -244,7 +262,7 @@ fun SignupScreenContent(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 8.dp),
|
.padding(top = 8.dp),
|
||||||
label = stringResource(id = R.string.feature_auth_email),
|
label = stringResource(id = R.string.feature_auth_email),
|
||||||
value = email
|
value = email,
|
||||||
) {
|
) {
|
||||||
email = it
|
email = it
|
||||||
}
|
}
|
||||||
@ -254,7 +272,7 @@ fun SignupScreenContent(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 8.dp),
|
.padding(top = 8.dp),
|
||||||
label = stringResource(id = R.string.feature_auth_name_of_business),
|
label = stringResource(id = R.string.feature_auth_name_of_business),
|
||||||
value = nameOfBusiness
|
value = nameOfBusiness,
|
||||||
) {
|
) {
|
||||||
nameOfBusiness = it
|
nameOfBusiness = it
|
||||||
}
|
}
|
||||||
@ -264,7 +282,7 @@ fun SignupScreenContent(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 8.dp),
|
.padding(top = 8.dp),
|
||||||
label = stringResource(id = R.string.feature_auth_address_line_1),
|
label = stringResource(id = R.string.feature_auth_address_line_1),
|
||||||
value = addressLine1
|
value = addressLine1,
|
||||||
) {
|
) {
|
||||||
addressLine1 = it
|
addressLine1 = it
|
||||||
}
|
}
|
||||||
@ -273,14 +291,14 @@ fun SignupScreenContent(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 8.dp),
|
.padding(top = 8.dp),
|
||||||
label = stringResource(id = R.string.feature_auth_address_line_2),
|
label = stringResource(id = R.string.feature_auth_address_line_2),
|
||||||
value = addressLine2
|
value = addressLine2,
|
||||||
) {
|
) {
|
||||||
addressLine2 = it
|
addressLine2 = it
|
||||||
}
|
}
|
||||||
UserInfoTextField(
|
UserInfoTextField(
|
||||||
modifier = Modifier.padding(top = 8.dp),
|
modifier = Modifier.padding(top = 8.dp),
|
||||||
label = stringResource(id = R.string.feature_auth_pin_code),
|
label = stringResource(id = R.string.feature_auth_pin_code),
|
||||||
value = pinCode
|
value = pinCode,
|
||||||
) {
|
) {
|
||||||
pinCode = it
|
pinCode = it
|
||||||
}
|
}
|
||||||
@ -288,7 +306,7 @@ fun SignupScreenContent(
|
|||||||
MifosStateDropDownOutlinedTextField(
|
MifosStateDropDownOutlinedTextField(
|
||||||
value = selectedState?.name ?: "",
|
value = selectedState?.name ?: "",
|
||||||
label = stringResource(id = R.string.feature_auth_state),
|
label = stringResource(id = R.string.feature_auth_state),
|
||||||
stateList = stateList
|
stateList = stateList,
|
||||||
) {
|
) {
|
||||||
selectedState = it
|
selectedState = it
|
||||||
}
|
}
|
||||||
@ -307,7 +325,7 @@ fun SignupScreenContent(
|
|||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_auth_complete),
|
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)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MifosStateDropDownOutlinedTextField(
|
private fun MifosStateDropDownOutlinedTextField(
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
value: String,
|
value: String,
|
||||||
label: String,
|
label: String,
|
||||||
stateList: List<State>,
|
stateList: List<State>,
|
||||||
onSelectedState: (State) -> Unit
|
modifier: Modifier = Modifier,
|
||||||
|
onSelectedState: (State) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
var expanded by rememberSaveable { mutableStateOf(false) }
|
var expanded by rememberSaveable { mutableStateOf(false) }
|
||||||
ExposedDropdownMenuBox(
|
ExposedDropdownMenuBox(
|
||||||
expanded = expanded,
|
expanded = expanded,
|
||||||
onExpandedChange = {
|
onExpandedChange = {
|
||||||
expanded = !expanded
|
expanded = !expanded
|
||||||
}
|
},
|
||||||
) {
|
) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
modifier = modifier.menuAnchor(),
|
modifier = modifier.menuAnchor(),
|
||||||
@ -343,21 +361,21 @@ fun MifosStateDropDownOutlinedTextField(
|
|||||||
label = { Text(label) },
|
label = { Text(label) },
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
|
ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
DropdownMenu(
|
DropdownMenu(
|
||||||
expanded = expanded,
|
expanded = expanded,
|
||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
expanded = false
|
expanded = false
|
||||||
})
|
},
|
||||||
{
|
) {
|
||||||
stateList.forEach {
|
stateList.forEach {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(text = it.name) },
|
text = { Text(text = it.name) },
|
||||||
onClick = {
|
onClick = {
|
||||||
expanded = false
|
expanded = false
|
||||||
onSelectedState(it)
|
onSelectedState(it)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -365,11 +383,11 @@ fun MifosStateDropDownOutlinedTextField(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun UserInfoTextField(
|
private fun UserInfoTextField(
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
label: String,
|
label: String,
|
||||||
value: String,
|
value: String,
|
||||||
onValueChange: (String) -> Unit
|
modifier: Modifier = Modifier,
|
||||||
|
onValueChange: (String) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
MfOutlinedTextField(
|
MfOutlinedTextField(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
@ -377,12 +395,12 @@ fun UserInfoTextField(
|
|||||||
label = label,
|
label = label,
|
||||||
isError = value.isEmpty(),
|
isError = value.isEmpty(),
|
||||||
errorMessage = stringResource(id = R.string.feature_auth_mandatory),
|
errorMessage = stringResource(id = R.string.feature_auth_mandatory),
|
||||||
onValueChange = onValueChange
|
onValueChange = onValueChange,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PasswordAndConfirmPassword(
|
private fun PasswordAndConfirmPassword(
|
||||||
password: String,
|
password: String,
|
||||||
onPasswordChange: (String) -> Unit,
|
onPasswordChange: (String) -> Unit,
|
||||||
confirmPassword: String,
|
confirmPassword: String,
|
||||||
@ -391,8 +409,9 @@ fun PasswordAndConfirmPassword(
|
|||||||
onTogglePasswordVisibility: () -> Unit,
|
onTogglePasswordVisibility: () -> Unit,
|
||||||
isConfirmPasswordVisible: Boolean,
|
isConfirmPasswordVisible: Boolean,
|
||||||
onConfirmTogglePasswordVisibility: () -> Unit,
|
onConfirmTogglePasswordVisibility: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Column {
|
Column(modifier) {
|
||||||
MfPasswordTextField(
|
MfPasswordTextField(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
password = password,
|
password = password,
|
||||||
@ -402,10 +421,12 @@ fun PasswordAndConfirmPassword(
|
|||||||
stringResource(id = R.string.feature_auth_password_cannot_be_empty)
|
stringResource(id = R.string.feature_auth_password_cannot_be_empty)
|
||||||
} else if (password.length < 6) {
|
} else if (password.length < 6) {
|
||||||
stringResource(id = R.string.feature_auth_password_must_be_least_6_characters)
|
stringResource(id = R.string.feature_auth_password_must_be_least_6_characters)
|
||||||
} else null,
|
} else {
|
||||||
|
null
|
||||||
|
},
|
||||||
onPasswordChange = onPasswordChange,
|
onPasswordChange = onPasswordChange,
|
||||||
isPasswordVisible = isPasswordVisible,
|
isPasswordVisible = isPasswordVisible,
|
||||||
onTogglePasswordVisibility = onTogglePasswordVisibility
|
onTogglePasswordVisibility = onTogglePasswordVisibility,
|
||||||
)
|
)
|
||||||
MfPasswordTextField(
|
MfPasswordTextField(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
@ -416,19 +437,25 @@ fun PasswordAndConfirmPassword(
|
|||||||
stringResource(id = R.string.feature_auth_confirm_password_cannot_empty)
|
stringResource(id = R.string.feature_auth_confirm_password_cannot_empty)
|
||||||
} else if (password != confirmPassword) {
|
} else if (password != confirmPassword) {
|
||||||
stringResource(id = R.string.feature_auth_passwords_do_not_match)
|
stringResource(id = R.string.feature_auth_passwords_do_not_match)
|
||||||
} else null,
|
} else {
|
||||||
|
null
|
||||||
|
},
|
||||||
onPasswordChange = onConfirmPasswordChange,
|
onPasswordChange = onConfirmPasswordChange,
|
||||||
isPasswordVisible = isConfirmPasswordVisible,
|
isPasswordVisible = isConfirmPasswordVisible,
|
||||||
onTogglePasswordVisibility = onConfirmTogglePasswordVisibility
|
onTogglePasswordVisibility = onConfirmTogglePasswordVisibility,
|
||||||
)
|
)
|
||||||
if (password.length >= 6) {
|
if (password.length >= 6) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(top = 8.dp),
|
modifier = Modifier.padding(top = 8.dp),
|
||||||
text = "${stringResource(id = R.string.feature_auth_password_strength)}${
|
text = "${stringResource(id = R.string.feature_auth_password_strength)}${
|
||||||
getPasswordStrength(password).replaceFirstChar {
|
getPasswordStrength(password).replaceFirstChar {
|
||||||
if (it.isLowerCase()) it.titlecase(
|
if (it.isLowerCase()) {
|
||||||
Locale.ENGLISH
|
it.titlecase(
|
||||||
) else it.toString()
|
Locale.ENGLISH,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
it.toString()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}",
|
}",
|
||||||
color = getPasswordStrengthColor(password),
|
color = getPasswordStrengthColor(password),
|
||||||
@ -437,7 +464,6 @@ fun PasswordAndConfirmPassword(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun getPasswordStrength(password: String): String {
|
private fun getPasswordStrength(password: String): String {
|
||||||
val hasUpperCase = password.any { it.isUpperCase() }
|
val hasUpperCase = password.any { it.isUpperCase() }
|
||||||
val hasLowerCase = password.any { it.isLowerCase() }
|
val hasLowerCase = password.any { it.isLowerCase() }
|
||||||
@ -448,12 +474,12 @@ private fun getPasswordStrength(password: String): String {
|
|||||||
hasUpperCase.toInt(),
|
hasUpperCase.toInt(),
|
||||||
hasLowerCase.toInt(),
|
hasLowerCase.toInt(),
|
||||||
hasNumbers.toInt(),
|
hasNumbers.toInt(),
|
||||||
hasSymbols.toInt()
|
hasSymbols.toInt(),
|
||||||
).sum()
|
).sum()
|
||||||
return PasswordStrength.entries[numTypesPresent].name
|
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 {
|
private fun getPasswordStrengthColor(password: String): Color {
|
||||||
val strength = getPasswordStrength(password)
|
val strength = getPasswordStrength(password)
|
||||||
@ -469,11 +495,11 @@ private fun getPasswordStrengthColor(password: String): Color {
|
|||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun SignupScreenPreview() {
|
private fun SignupScreenPreview() {
|
||||||
SignupScreenContent(
|
SignupScreenContent(
|
||||||
showProgressState = false,
|
showProgressState = false,
|
||||||
data = SignupData(),
|
data = SignupData(),
|
||||||
stateList = listOf(),
|
stateList = listOf(),
|
||||||
onCompleteRegistration = { }
|
onCompleteRegistration = { },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.auth.signup
|
||||||
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@ -33,7 +42,6 @@ import org.mifospay.core.data.repository.local.LocalAssetRepository
|
|||||||
import org.mifospay.core.datastore.PreferencesHelper
|
import org.mifospay.core.datastore.PreferencesHelper
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class SignupViewModel @Inject constructor(
|
class SignupViewModel @Inject constructor(
|
||||||
localAssetRepository: LocalAssetRepository,
|
localAssetRepository: LocalAssetRepository,
|
||||||
@ -46,7 +54,7 @@ class SignupViewModel @Inject constructor(
|
|||||||
private val authenticateUserUseCase: AuthenticateUser,
|
private val authenticateUserUseCase: AuthenticateUser,
|
||||||
private val fetchClientDataUseCase: FetchClientData,
|
private val fetchClientDataUseCase: FetchClientData,
|
||||||
private val deleteUserUseCase: DeleteUser,
|
private val deleteUserUseCase: DeleteUser,
|
||||||
private val fetchUserDetailsUseCase: FetchUserDetails
|
private val fetchUserDetailsUseCase: FetchUserDetails,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
var showProgress by mutableStateOf(false)
|
var showProgress by mutableStateOf(false)
|
||||||
@ -62,7 +70,7 @@ class SignupViewModel @Inject constructor(
|
|||||||
email: String?,
|
email: String?,
|
||||||
firstName: String?,
|
firstName: String?,
|
||||||
lastName: String?,
|
lastName: String?,
|
||||||
businessName: String?
|
businessName: String?,
|
||||||
) {
|
) {
|
||||||
signupData = signupData.copy(
|
signupData = signupData.copy(
|
||||||
mifosSavingsProductId = savingProductId,
|
mifosSavingsProductId = savingProductId,
|
||||||
@ -71,19 +79,19 @@ class SignupViewModel @Inject constructor(
|
|||||||
email = email,
|
email = email,
|
||||||
firstName = firstName!!,
|
firstName = firstName!!,
|
||||||
lastName = lastName!!,
|
lastName = lastName!!,
|
||||||
businessName = businessName
|
businessName = businessName,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val states: StateFlow<List<State>> = combine(
|
val states: StateFlow<List<State>> = combine(
|
||||||
localAssetRepository.getCountries(),
|
localAssetRepository.getCountries(),
|
||||||
localAssetRepository.getStateList(),
|
localAssetRepository.getStateList(),
|
||||||
::Pair
|
::Pair,
|
||||||
)
|
)
|
||||||
.map {
|
.map {
|
||||||
val countries = it.first
|
val countries = it.first
|
||||||
signupData = signupData.copy(
|
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 }
|
it.second.filter { it.countryId == signupData.countryId }
|
||||||
}
|
}
|
||||||
@ -101,7 +109,8 @@ class SignupViewModel @Inject constructor(
|
|||||||
// 2. Create user
|
// 2. Create user
|
||||||
// 3. Create Client
|
// 3. Create Client
|
||||||
// 4. Update User and connect client with user
|
// 4. Update User and connect client with user
|
||||||
useCaseHandler.execute(searchClientUseCase,
|
useCaseHandler.execute(
|
||||||
|
searchClientUseCase,
|
||||||
SearchClient.RequestValues("${signupData.userName}@mifos"),
|
SearchClient.RequestValues("${signupData.userName}@mifos"),
|
||||||
object : UseCase.UseCaseCallback<SearchClient.ResponseValue> {
|
object : UseCase.UseCaseCallback<SearchClient.ResponseValue> {
|
||||||
override fun onSuccess(response: SearchClient.ResponseValue) {
|
override fun onSuccess(response: SearchClient.ResponseValue) {
|
||||||
@ -111,15 +120,21 @@ class SignupViewModel @Inject constructor(
|
|||||||
override fun onError(message: String) {
|
override fun onError(message: String) {
|
||||||
createUser(showToastMessage)
|
createUser(showToastMessage)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createUser(showToastMessage: (String) -> Unit) {
|
private fun createUser(showToastMessage: (String) -> Unit) {
|
||||||
val newUser = NewUser(
|
val newUser = NewUser(
|
||||||
signupData.userName, signupData.firstName, signupData.lastName,
|
signupData.userName,
|
||||||
signupData.email, signupData.password
|
signupData.firstName,
|
||||||
|
signupData.lastName,
|
||||||
|
signupData.email,
|
||||||
|
signupData.password,
|
||||||
)
|
)
|
||||||
useCaseHandler.execute(createUserUseCase, CreateUser.RequestValues(newUser),
|
useCaseHandler.execute(
|
||||||
|
createUserUseCase,
|
||||||
|
CreateUser.RequestValues(newUser),
|
||||||
object : UseCase.UseCaseCallback<CreateUser.ResponseValue> {
|
object : UseCase.UseCaseCallback<CreateUser.ResponseValue> {
|
||||||
override fun onSuccess(response: CreateUser.ResponseValue) {
|
override fun onSuccess(response: CreateUser.ResponseValue) {
|
||||||
createClient(response.userId, showToastMessage)
|
createClient(response.userId, showToastMessage)
|
||||||
@ -129,16 +144,18 @@ class SignupViewModel @Inject constructor(
|
|||||||
DebugUtil.log(message)
|
DebugUtil.log(message)
|
||||||
showToastMessage(message)
|
showToastMessage(message)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createClient(userId: Int, showToastMessage: (String) -> Unit) {
|
private fun createClient(userId: Int, showToastMessage: (String) -> Unit) {
|
||||||
val newClient = com.mifospay.core.model.domain.client.NewClient(
|
val newClient = com.mifospay.core.model.domain.client.NewClient(
|
||||||
signupData.businessName, signupData.userName, signupData.addressLine1,
|
signupData.businessName, signupData.userName, signupData.addressLine1,
|
||||||
signupData.addressLine2, signupData.city, signupData.pinCode, signupData.stateId,
|
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),
|
CreateClient.RequestValues(newClient),
|
||||||
object : UseCase.UseCaseCallback<CreateClient.ResponseValue> {
|
object : UseCase.UseCaseCallback<CreateClient.ResponseValue> {
|
||||||
override fun onSuccess(response: CreateClient.ResponseValue) {
|
override fun onSuccess(response: CreateClient.ResponseValue) {
|
||||||
@ -154,15 +171,17 @@ class SignupViewModel @Inject constructor(
|
|||||||
showToastMessage(message)
|
showToastMessage(message)
|
||||||
deleteUser(userId)
|
deleteUser(userId)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateClient(
|
private fun updateClient(
|
||||||
clients: ArrayList<Int>,
|
clients: ArrayList<Int>,
|
||||||
userId: Int,
|
userId: Int,
|
||||||
showToastMessage: (String) -> Unit
|
showToastMessage: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
useCaseHandler.execute(updateUserUseCase,
|
useCaseHandler.execute(
|
||||||
|
updateUserUseCase,
|
||||||
UpdateUser.RequestValues(UpdateUserEntityClients(clients), userId),
|
UpdateUser.RequestValues(UpdateUserEntityClients(clients), userId),
|
||||||
object : UseCase.UseCaseCallback<UpdateUser.ResponseValue?> {
|
object : UseCase.UseCaseCallback<UpdateUser.ResponseValue?> {
|
||||||
override fun onSuccess(response: UpdateUser.ResponseValue?) {
|
override fun onSuccess(response: UpdateUser.ResponseValue?) {
|
||||||
@ -174,17 +193,20 @@ class SignupViewModel @Inject constructor(
|
|||||||
DebugUtil.log(message)
|
DebugUtil.log(message)
|
||||||
showToastMessage("update client error")
|
showToastMessage("update client error")
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loginUser(
|
private fun loginUser(
|
||||||
username: String?,
|
username: String?,
|
||||||
password: String?,
|
password: String?,
|
||||||
showToastMessage: (String) -> Unit
|
showToastMessage: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
authenticateUserUseCase.walletRequestValues = AuthenticateUser.RequestValues(username!!, password!!)
|
authenticateUserUseCase.walletRequestValues = AuthenticateUser.RequestValues(username!!, password!!)
|
||||||
val requestValue = authenticateUserUseCase.walletRequestValues
|
val requestValue = authenticateUserUseCase.walletRequestValues
|
||||||
useCaseHandler.execute(authenticateUserUseCase, requestValue,
|
useCaseHandler.execute(
|
||||||
|
authenticateUserUseCase,
|
||||||
|
requestValue,
|
||||||
object : UseCase.UseCaseCallback<AuthenticateUser.ResponseValue> {
|
object : UseCase.UseCaseCallback<AuthenticateUser.ResponseValue> {
|
||||||
override fun onSuccess(response: AuthenticateUser.ResponseValue) {
|
override fun onSuccess(response: AuthenticateUser.ResponseValue) {
|
||||||
createAuthenticatedService(response.user)
|
createAuthenticatedService(response.user)
|
||||||
@ -195,11 +217,13 @@ class SignupViewModel @Inject constructor(
|
|||||||
override fun onError(message: String) {
|
override fun onError(message: String) {
|
||||||
showToastMessage("Login Failed")
|
showToastMessage("Login Failed")
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fetchUserDetails(user: User) {
|
private fun fetchUserDetails(user: User) {
|
||||||
useCaseHandler.execute(fetchUserDetailsUseCase,
|
useCaseHandler.execute(
|
||||||
|
fetchUserDetailsUseCase,
|
||||||
FetchUserDetails.RequestValues(user.userId),
|
FetchUserDetails.RequestValues(user.userId),
|
||||||
object : UseCase.UseCaseCallback<FetchUserDetails.ResponseValue> {
|
object : UseCase.UseCaseCallback<FetchUserDetails.ResponseValue> {
|
||||||
override fun onSuccess(response: FetchUserDetails.ResponseValue) {
|
override fun onSuccess(response: FetchUserDetails.ResponseValue) {
|
||||||
@ -209,11 +233,14 @@ class SignupViewModel @Inject constructor(
|
|||||||
override fun onError(message: String) {
|
override fun onError(message: String) {
|
||||||
DebugUtil.log(message)
|
DebugUtil.log(message)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fetchClientData(showToastMessage: (String) -> Unit) {
|
private fun fetchClientData(showToastMessage: (String) -> Unit) {
|
||||||
useCaseHandler.execute(fetchClientDataUseCase, null,
|
useCaseHandler.execute(
|
||||||
|
fetchClientDataUseCase,
|
||||||
|
null,
|
||||||
object : UseCase.UseCaseCallback<FetchClientData.ResponseValue> {
|
object : UseCase.UseCaseCallback<FetchClientData.ResponseValue> {
|
||||||
override fun onSuccess(response: FetchClientData.ResponseValue) {
|
override fun onSuccess(response: FetchClientData.ResponseValue) {
|
||||||
saveClientDetails(response.clientDetails)
|
saveClientDetails(response.clientDetails)
|
||||||
@ -225,7 +252,8 @@ class SignupViewModel @Inject constructor(
|
|||||||
override fun onError(message: String) {
|
override fun onError(message: String) {
|
||||||
showToastMessage("Fetch Client Error")
|
showToastMessage("Fetch Client Error")
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createAuthenticatedService(user: User) {
|
private fun createAuthenticatedService(user: User) {
|
||||||
@ -235,7 +263,7 @@ class SignupViewModel @Inject constructor(
|
|||||||
|
|
||||||
private fun saveUserDetails(
|
private fun saveUserDetails(
|
||||||
user: User,
|
user: User,
|
||||||
userWithRole: UserWithRole
|
userWithRole: UserWithRole,
|
||||||
) {
|
) {
|
||||||
val userName = user.username
|
val userName = user.username
|
||||||
val userID = user.userId
|
val userID = user.userId
|
||||||
@ -251,11 +279,14 @@ class SignupViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun deleteUser(userId: Int) {
|
private fun deleteUser(userId: Int) {
|
||||||
useCaseHandler.execute(deleteUserUseCase, DeleteUser.RequestValues(userId),
|
useCaseHandler.execute(
|
||||||
|
deleteUserUseCase,
|
||||||
|
DeleteUser.RequestValues(userId),
|
||||||
object : UseCase.UseCaseCallback<DeleteUser.ResponseValue> {
|
object : UseCase.UseCaseCallback<DeleteUser.ResponseValue> {
|
||||||
override fun onSuccess(response: DeleteUser.ResponseValue) {}
|
override fun onSuccess(response: DeleteUser.ResponseValue) {}
|
||||||
override fun onError(message: String) {}
|
override fun onError(message: String) {}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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")
|
@file:Suppress("MaxLineLength")
|
||||||
|
|
||||||
package org.mifospay.feature.auth.socialSignup
|
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
|
// 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
|
// Keeping until we fix sign up
|
||||||
@Composable
|
@Composable
|
||||||
fun SocialSignupMethodContentScreen(
|
internal fun SocialSignupMethodContentScreen(
|
||||||
onDismissSignUp: () -> Unit
|
modifier: Modifier = Modifier,
|
||||||
|
onDismissSignUp: () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
SocialSignupMethodScreen(onDismissSignUp = onDismissSignUp)
|
SocialSignupMethodScreen(
|
||||||
|
modifier = modifier,
|
||||||
|
onDismissSignUp = onDismissSignUp,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Suppress("NestedBlockDepth")
|
@Suppress("NestedBlockDepth")
|
||||||
fun SocialSignupMethodScreen(
|
private fun SocialSignupMethodScreen(
|
||||||
onDismissSignUp: () -> Unit
|
modifier: Modifier = Modifier,
|
||||||
|
onDismissSignUp: () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
var mifosSavingProductId by remember { mutableIntStateOf(0) }
|
var mifosSavingProductId by remember { mutableIntStateOf(0) }
|
||||||
var showProgress by remember { mutableStateOf(false) }
|
var showProgress by remember { mutableStateOf(false) }
|
||||||
@ -91,7 +104,6 @@ fun SocialSignupMethodScreen(
|
|||||||
.addCredentialOption(googleIdOption)
|
.addCredentialOption(googleIdOption)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
|
||||||
fun signUpWithMifos() {
|
fun signUpWithMifos() {
|
||||||
googleIdTokenCredential.signUpWithMifos(context, mifosSavingProductId) {
|
googleIdTokenCredential.signUpWithMifos(context, mifosSavingProductId) {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
@ -115,7 +127,7 @@ fun SocialSignupMethodScreen(
|
|||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
context,
|
context,
|
||||||
Constants.GOOGLE_SIGN_IN_FAILED,
|
Constants.GOOGLE_SIGN_IN_FAILED,
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT,
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
} catch (e: GoogleIdTokenParsingException) {
|
} catch (e: GoogleIdTokenParsingException) {
|
||||||
@ -134,7 +146,6 @@ fun SocialSignupMethodScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun signUpCredentialManager() {
|
fun signUpCredentialManager() {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
try {
|
try {
|
||||||
@ -161,6 +172,7 @@ fun SocialSignupMethodScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
MifosBottomSheet(
|
MifosBottomSheet(
|
||||||
|
modifier = modifier,
|
||||||
content = {
|
content = {
|
||||||
SignupMethodContentScreen(
|
SignupMethodContentScreen(
|
||||||
showProgress = showProgress,
|
showProgress = showProgress,
|
||||||
@ -171,37 +183,37 @@ fun SocialSignupMethodScreen(
|
|||||||
onSignupAsCustomer = { checkedGoogleAccount ->
|
onSignupAsCustomer = { checkedGoogleAccount ->
|
||||||
mifosSavingProductId = MIFOS_CONSUMER_SAVINGS_PRODUCT_ID
|
mifosSavingProductId = MIFOS_CONSUMER_SAVINGS_PRODUCT_ID
|
||||||
signUp(checkedGoogleAccount)
|
signUp(checkedGoogleAccount)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onDismiss = {
|
onDismiss = {
|
||||||
onDismissSignUp.invoke()
|
onDismissSignUp.invoke()
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod")
|
||||||
fun SignupMethodContentScreen(
|
private fun SignupMethodContentScreen(
|
||||||
showProgress: Boolean,
|
showProgress: Boolean,
|
||||||
onSignUpAsMerchant: (Boolean) -> Unit,
|
onSignUpAsMerchant: (Boolean) -> Unit,
|
||||||
onSignupAsCustomer: (Boolean) -> Unit,
|
onSignupAsCustomer: (Boolean) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
var checkedGoogleAccountState by remember { mutableStateOf(true) }
|
var checkedGoogleAccountState by remember { mutableStateOf(true) }
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier,
|
modifier = modifier,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(color = MaterialTheme.colorScheme.surface),
|
.background(color = MaterialTheme.colorScheme.surface),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(top = 16.dp),
|
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(
|
OutlinedButton(
|
||||||
modifier = Modifier.padding(top = 48.dp),
|
modifier = Modifier.padding(top = 48.dp),
|
||||||
@ -210,36 +222,36 @@ fun SignupMethodContentScreen(
|
|||||||
},
|
},
|
||||||
border = BorderStroke(1.dp, Color.LightGray),
|
border = BorderStroke(1.dp, Color.LightGray),
|
||||||
shape = RoundedCornerShape(4.dp),
|
shape = RoundedCornerShape(4.dp),
|
||||||
colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.primary)
|
colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.primary),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_auth_sign_up_as_merchant).uppercase(),
|
text = stringResource(id = R.string.feature_auth_sign_up_as_merchant).uppercase(),
|
||||||
style = MaterialTheme.typography.labelMedium
|
style = MaterialTheme.typography.labelMedium,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 24.dp),
|
.padding(top = 24.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
HorizontalDivider(
|
HorizontalDivider(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(start = 24.dp, end = 8.dp)
|
.padding(start = 24.dp, end = 8.dp)
|
||||||
.weight(.4f),
|
.weight(.4f),
|
||||||
thickness = 1.dp
|
thickness = 1.dp,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.wrapContentWidth()
|
.wrapContentWidth()
|
||||||
.weight(.1f),
|
.weight(.1f),
|
||||||
text = stringResource(id = R.string.feature_auth_or)
|
text = stringResource(id = R.string.feature_auth_or),
|
||||||
)
|
)
|
||||||
HorizontalDivider(
|
HorizontalDivider(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(start = 8.dp, end = 24.dp)
|
.padding(start = 8.dp, end = 24.dp)
|
||||||
.weight(.4f),
|
.weight(.4f),
|
||||||
thickness = 1.dp
|
thickness = 1.dp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
@ -249,11 +261,11 @@ fun SignupMethodContentScreen(
|
|||||||
},
|
},
|
||||||
border = BorderStroke(1.dp, Color.LightGray),
|
border = BorderStroke(1.dp, Color.LightGray),
|
||||||
shape = RoundedCornerShape(4.dp),
|
shape = RoundedCornerShape(4.dp),
|
||||||
colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.primary)
|
colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.primary),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_auth_sign_up_as_customer).uppercase(),
|
text = stringResource(id = R.string.feature_auth_sign_up_as_customer).uppercase(),
|
||||||
style = MaterialTheme.typography.labelMedium
|
style = MaterialTheme.typography.labelMedium,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Row(
|
Row(
|
||||||
@ -262,18 +274,18 @@ fun SignupMethodContentScreen(
|
|||||||
.clickable {
|
.clickable {
|
||||||
checkedGoogleAccountState = !checkedGoogleAccountState
|
checkedGoogleAccountState = !checkedGoogleAccountState
|
||||||
},
|
},
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Checkbox(
|
Checkbox(
|
||||||
checked = checkedGoogleAccountState,
|
checked = checkedGoogleAccountState,
|
||||||
onCheckedChange = {
|
onCheckedChange = {
|
||||||
checkedGoogleAccountState = !checkedGoogleAccountState
|
checkedGoogleAccountState = !checkedGoogleAccountState
|
||||||
},
|
},
|
||||||
colors = CheckboxDefaults.colors(MaterialTheme.colorScheme.primary)
|
colors = CheckboxDefaults.colors(MaterialTheme.colorScheme.primary),
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_auth_ease_my_sign_up_using_google_account),
|
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)
|
HorizontalDivider(thickness = 48.dp, color = Color.Transparent)
|
||||||
@ -283,7 +295,7 @@ fun SignupMethodContentScreen(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 140.dp),
|
.padding(top = 140.dp),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
modifier = Modifier.size(64.dp),
|
modifier = Modifier.size(64.dp),
|
||||||
@ -296,20 +308,20 @@ fun SignupMethodContentScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UnusedParameter")
|
@Suppress("UnusedParameter")
|
||||||
fun GoogleIdTokenCredential?.signUpWithMifos(
|
private fun GoogleIdTokenCredential?.signUpWithMifos(
|
||||||
context: Context,
|
context: Context,
|
||||||
mifosSavingsProductId: Int,
|
mifosSavingsProductId: Int,
|
||||||
signOutGoogleClient: () -> Unit
|
signOutGoogleClient: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val googleIdTokenCredential = this
|
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")
|
// familyName,mifosSavingsProductId,displayName,data.getString("com.google.android.libraries.identity.googleid.BUNDLE_KEY_ID")
|
||||||
signOutGoogleClient.invoke()
|
signOutGoogleClient.invoke()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun SignupMethodContentScreenPreview() {
|
private fun SignupMethodContentScreenPreview() {
|
||||||
MaterialTheme {
|
MaterialTheme {
|
||||||
SignupMethodContentScreen(true, {}, {})
|
SignupMethodContentScreen(true, {}, {})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.auth.utils
|
||||||
|
|
||||||
import android.util.Patterns
|
import android.util.Patterns
|
||||||
|
|
||||||
object ValidateUtil {
|
object ValidateUtil {
|
||||||
fun String.isValidEmail() = this.isNotEmpty() && Patterns.EMAIL_ADDRESS.matcher(this).matches()
|
fun String.isValidEmail() = this.isNotEmpty() && Patterns.EMAIL_ADDRESS.matcher(this).matches()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<string name="feature_auth_login">Login</string>
|
<string name="feature_auth_login">Login</string>
|
||||||
<string name="feature_auth_welcome_back">Welcome back!</string>
|
<string name="feature_auth_welcome_back">Welcome back!</string>
|
||||||
|
|||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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 {
|
plugins {
|
||||||
alias(libs.plugins.mifospay.android.feature)
|
alias(libs.plugins.mifospay.android.feature)
|
||||||
alias(libs.plugins.mifospay.android.library.compose)
|
alias(libs.plugins.mifospay.android.library.compose)
|
||||||
|
|||||||
@ -1,4 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<manifest>
|
<manifest>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@ -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
|
package org.mifospay.feature.editpassword
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@ -35,13 +44,15 @@ import org.mifospay.core.designsystem.component.MifosScaffold
|
|||||||
import org.mifospay.core.designsystem.theme.MifosTheme
|
import org.mifospay.core.designsystem.theme.MifosTheme
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun EditPasswordScreen(
|
internal fun EditPasswordScreen(
|
||||||
viewModel: EditPasswordViewModel = hiltViewModel(),
|
|
||||||
onBackPress: () -> Unit,
|
onBackPress: () -> Unit,
|
||||||
onCancelChanges: () -> Unit
|
onCancelChanges: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
viewModel: EditPasswordViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
val editPasswordUiState by viewModel.editPasswordUiState.collectAsStateWithLifecycle()
|
val editPasswordUiState by viewModel.editPasswordUiState.collectAsStateWithLifecycle()
|
||||||
EditPasswordScreen(
|
EditPasswordScreen(
|
||||||
|
modifier = modifier,
|
||||||
editPasswordUiState = editPasswordUiState,
|
editPasswordUiState = editPasswordUiState,
|
||||||
onCancelChanges = onCancelChanges,
|
onCancelChanges = onCancelChanges,
|
||||||
onBackPress = onBackPress,
|
onBackPress = onBackPress,
|
||||||
@ -49,18 +60,19 @@ fun EditPasswordScreen(
|
|||||||
viewModel.updatePassword(
|
viewModel.updatePassword(
|
||||||
currentPassword = currentPass,
|
currentPassword = currentPass,
|
||||||
newPassword = newPass,
|
newPassword = newPass,
|
||||||
newPasswordRepeat = confirmPass
|
newPasswordRepeat = confirmPass,
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun EditPasswordScreen(
|
private fun EditPasswordScreen(
|
||||||
editPasswordUiState: EditPasswordUiState,
|
editPasswordUiState: EditPasswordUiState,
|
||||||
onCancelChanges: () -> Unit,
|
onCancelChanges: () -> Unit,
|
||||||
onBackPress: () -> 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
|
val context = LocalContext.current
|
||||||
var currentPassword by rememberSaveable { mutableStateOf("") }
|
var currentPassword by rememberSaveable { mutableStateOf("") }
|
||||||
@ -88,26 +100,25 @@ fun EditPasswordScreen(
|
|||||||
EditPasswordUiState.Success -> {
|
EditPasswordUiState.Success -> {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
currentSnackbarHostState.showSnackbar(
|
currentSnackbarHostState.showSnackbar(
|
||||||
context.getString(R.string.feature_editpassword_password_changed_successfully)
|
context.getString(R.string.feature_editpassword_password_changed_successfully),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MifosScaffold(
|
MifosScaffold(
|
||||||
|
modifier = modifier,
|
||||||
topBarTitle = R.string.feature_editpassword_change_password,
|
topBarTitle = R.string.feature_editpassword_change_password,
|
||||||
snackbarHost = {
|
snackbarHost = {
|
||||||
SnackbarHost(hostState = snackbarHostState)
|
SnackbarHost(hostState = snackbarHostState)
|
||||||
},
|
},
|
||||||
backPress = onBackPress,
|
backPress = onBackPress,
|
||||||
|
|
||||||
scaffoldContent = { paddingValues ->
|
scaffoldContent = { paddingValues ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(paddingValues)
|
.padding(paddingValues),
|
||||||
) {
|
) {
|
||||||
MfPasswordTextField(
|
MfPasswordTextField(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -119,7 +130,6 @@ fun EditPasswordScreen(
|
|||||||
isPasswordVisible = isConfirmPasswordVisible,
|
isPasswordVisible = isConfirmPasswordVisible,
|
||||||
onTogglePasswordVisibility = {
|
onTogglePasswordVisibility = {
|
||||||
isConfirmPasswordVisible = !isConfirmPasswordVisible
|
isConfirmPasswordVisible = !isConfirmPasswordVisible
|
||||||
|
|
||||||
},
|
},
|
||||||
onPasswordChange = { currentPassword = it },
|
onPasswordChange = { currentPassword = it },
|
||||||
)
|
)
|
||||||
@ -131,12 +141,16 @@ fun EditPasswordScreen(
|
|||||||
password = newPassword,
|
password = newPassword,
|
||||||
label = stringResource(id = R.string.feature_editpassword_new_password),
|
label = stringResource(id = R.string.feature_editpassword_new_password),
|
||||||
isError = newPassword.isNotEmpty() && newPassword.length < 6,
|
isError = newPassword.isNotEmpty() && newPassword.length < 6,
|
||||||
errorMessage = if (newPassword.isNotEmpty() && newPassword.length < 6) stringResource(
|
errorMessage = if (newPassword.isNotEmpty() && newPassword.length < 6) {
|
||||||
id = R.string.feature_editpassword_password_length_error
|
stringResource(
|
||||||
) else null,
|
id = R.string.feature_editpassword_password_length_error,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
},
|
||||||
isPasswordVisible = isNewPasswordVisible,
|
isPasswordVisible = isNewPasswordVisible,
|
||||||
onTogglePasswordVisibility = { isNewPasswordVisible = !isNewPasswordVisible },
|
onTogglePasswordVisibility = { isNewPasswordVisible = !isNewPasswordVisible },
|
||||||
onPasswordChange = { newPassword = it }
|
onPasswordChange = { newPassword = it },
|
||||||
)
|
)
|
||||||
MfPasswordTextField(
|
MfPasswordTextField(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -147,21 +161,25 @@ fun EditPasswordScreen(
|
|||||||
isError = newPassword != confirmNewPassword && confirmNewPassword.isNotEmpty(),
|
isError = newPassword != confirmNewPassword && confirmNewPassword.isNotEmpty(),
|
||||||
errorMessage = if (newPassword !=
|
errorMessage = if (newPassword !=
|
||||||
confirmNewPassword && confirmNewPassword.isNotEmpty()
|
confirmNewPassword && confirmNewPassword.isNotEmpty()
|
||||||
) stringResource(
|
) {
|
||||||
id = R.string.feature_editpassword_password_mismatch_error
|
stringResource(
|
||||||
) else null,
|
id = R.string.feature_editpassword_password_mismatch_error,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
},
|
||||||
isPasswordVisible = isConfirmNewPasswordVisible,
|
isPasswordVisible = isConfirmNewPasswordVisible,
|
||||||
onTogglePasswordVisibility = {
|
onTogglePasswordVisibility = {
|
||||||
isConfirmNewPasswordVisible = !isConfirmNewPasswordVisible
|
isConfirmNewPasswordVisible = !isConfirmNewPasswordVisible
|
||||||
},
|
},
|
||||||
onPasswordChange = { confirmNewPassword = it }
|
onPasswordChange = { confirmNewPassword = it },
|
||||||
)
|
)
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 20.dp, start = 16.dp, end = 16.dp),
|
.padding(top = 20.dp, start = 16.dp, end = 16.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
MifosButton(
|
MifosButton(
|
||||||
onClick = { onCancelChanges.invoke() },
|
onClick = { onCancelChanges.invoke() },
|
||||||
@ -169,7 +187,7 @@ fun EditPasswordScreen(
|
|||||||
.weight(1f)
|
.weight(1f)
|
||||||
.padding(8.dp),
|
.padding(8.dp),
|
||||||
contentPadding = PaddingValues(16.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(
|
MifosButton(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -179,11 +197,11 @@ fun EditPasswordScreen(
|
|||||||
onSave.invoke(currentPassword, newPassword, confirmNewPassword)
|
onSave.invoke(currentPassword, newPassword, confirmNewPassword)
|
||||||
},
|
},
|
||||||
contentPadding = PaddingValues(16.dp),
|
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<EditPasswordUiState
|
|||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
EditPasswordUiState.Loading,
|
EditPasswordUiState.Loading,
|
||||||
EditPasswordUiState.Success,
|
EditPasswordUiState.Success,
|
||||||
EditPasswordUiState.Error("Some Error Occurred")
|
EditPasswordUiState.Error("Some Error Occurred"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
private fun EditPasswordScreenPreview(
|
private fun EditPasswordScreenPreview(
|
||||||
@PreviewParameter(EditPasswordUiStateProvider::class) editPasswordUiState: EditPasswordUiState
|
@PreviewParameter(EditPasswordUiStateProvider::class) editPasswordUiState: EditPasswordUiState,
|
||||||
) {
|
) {
|
||||||
MifosTheme {
|
MifosTheme {
|
||||||
EditPasswordScreen(editPasswordUiState = editPasswordUiState, {}, {}, { _, _, _ -> })
|
EditPasswordScreen(editPasswordUiState = editPasswordUiState, {}, {}, { _, _, _ -> })
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.editpassword
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
@ -19,7 +28,7 @@ class EditPasswordViewModel @Inject constructor(
|
|||||||
private val mUseCaseHandler: UseCaseHandler,
|
private val mUseCaseHandler: UseCaseHandler,
|
||||||
private val mPreferencesHelper: PreferencesHelper,
|
private val mPreferencesHelper: PreferencesHelper,
|
||||||
private val authenticateUserUseCase: AuthenticateUser,
|
private val authenticateUserUseCase: AuthenticateUser,
|
||||||
private val updateUserUseCase: UpdateUser
|
private val updateUserUseCase: UpdateUser,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _editPasswordUiState =
|
private val _editPasswordUiState =
|
||||||
@ -29,11 +38,11 @@ class EditPasswordViewModel @Inject constructor(
|
|||||||
fun updatePassword(
|
fun updatePassword(
|
||||||
currentPassword: String?,
|
currentPassword: String?,
|
||||||
newPassword: String?,
|
newPassword: String?,
|
||||||
newPasswordRepeat: String?
|
newPasswordRepeat: String?,
|
||||||
) {
|
) {
|
||||||
_editPasswordUiState.value = EditPasswordUiState.Loading
|
_editPasswordUiState.value = EditPasswordUiState.Loading
|
||||||
if (isNotEmpty(currentPassword) && isNotEmpty(newPassword)
|
if (isNotEmpty(currentPassword) && isNotEmpty(newPassword) &&
|
||||||
&& isNotEmpty(newPasswordRepeat)
|
isNotEmpty(newPasswordRepeat)
|
||||||
) {
|
) {
|
||||||
when {
|
when {
|
||||||
currentPassword == newPassword -> {
|
currentPassword == newPassword -> {
|
||||||
@ -45,7 +54,7 @@ class EditPasswordViewModel @Inject constructor(
|
|||||||
newPasswordRepeat?.let { it1 ->
|
newPasswordRepeat?.let { it1 ->
|
||||||
isNewPasswordValid(
|
isNewPasswordValid(
|
||||||
it,
|
it,
|
||||||
it1
|
it1,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} == true -> {
|
} == true -> {
|
||||||
@ -75,19 +84,21 @@ class EditPasswordViewModel @Inject constructor(
|
|||||||
|
|
||||||
private fun updatePassword(currentPassword: String, newPassword: String) {
|
private fun updatePassword(currentPassword: String, newPassword: String) {
|
||||||
// authenticate and then update
|
// authenticate and then update
|
||||||
mUseCaseHandler.execute(authenticateUserUseCase,
|
mUseCaseHandler.execute(
|
||||||
|
authenticateUserUseCase,
|
||||||
AuthenticateUser.RequestValues(
|
AuthenticateUser.RequestValues(
|
||||||
mPreferencesHelper.username,
|
mPreferencesHelper.username,
|
||||||
currentPassword
|
currentPassword,
|
||||||
),
|
),
|
||||||
object : UseCase.UseCaseCallback<AuthenticateUser.ResponseValue> {
|
object : UseCase.UseCaseCallback<AuthenticateUser.ResponseValue> {
|
||||||
override fun onSuccess(response: AuthenticateUser.ResponseValue) {
|
override fun onSuccess(response: AuthenticateUser.ResponseValue) {
|
||||||
mUseCaseHandler.execute(updateUserUseCase,
|
mUseCaseHandler.execute(
|
||||||
|
updateUserUseCase,
|
||||||
UpdateUser.RequestValues(
|
UpdateUser.RequestValues(
|
||||||
UpdateUserEntityPassword(
|
UpdateUserEntityPassword(
|
||||||
newPassword
|
newPassword,
|
||||||
),
|
),
|
||||||
mPreferencesHelper.userId.toInt()
|
mPreferencesHelper.userId.toInt(),
|
||||||
),
|
),
|
||||||
object : UseCase.UseCaseCallback<UpdateUser.ResponseValue?> {
|
object : UseCase.UseCaseCallback<UpdateUser.ResponseValue?> {
|
||||||
override fun onSuccess(response: UpdateUser.ResponseValue?) {
|
override fun onSuccess(response: UpdateUser.ResponseValue?) {
|
||||||
@ -97,13 +108,15 @@ class EditPasswordViewModel @Inject constructor(
|
|||||||
override fun onError(message: String) {
|
override fun onError(message: String) {
|
||||||
_editPasswordUiState.value = EditPasswordUiState.Error(message)
|
_editPasswordUiState.value = EditPasswordUiState.Error(message)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(message: String) {
|
override fun onError(message: String) {
|
||||||
_editPasswordUiState.value = EditPasswordUiState.Error("Wrong Password")
|
_editPasswordUiState.value = EditPasswordUiState.Error("Wrong Password")
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,4 +124,4 @@ sealed interface EditPasswordUiState {
|
|||||||
data object Loading : EditPasswordUiState
|
data object Loading : EditPasswordUiState
|
||||||
data object Success : EditPasswordUiState
|
data object Success : EditPasswordUiState
|
||||||
data class Error(val message: String) : EditPasswordUiState
|
data class Error(val message: String) : EditPasswordUiState
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.editpassword.navigation
|
||||||
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
@ -9,16 +18,16 @@ const val EDIT_PASSWORD_ROUTE = "edit_password_route"
|
|||||||
|
|
||||||
fun NavGraphBuilder.editPasswordScreen(
|
fun NavGraphBuilder.editPasswordScreen(
|
||||||
onBackPress: () -> Unit,
|
onBackPress: () -> Unit,
|
||||||
onCancelChanges: () -> Unit
|
onCancelChanges: () -> Unit,
|
||||||
) {
|
) {
|
||||||
composable(route = EDIT_PASSWORD_ROUTE) {
|
composable(route = EDIT_PASSWORD_ROUTE) {
|
||||||
EditPasswordScreen(
|
EditPasswordScreen(
|
||||||
onBackPress = onBackPress,
|
onBackPress = onBackPress,
|
||||||
onCancelChanges = onCancelChanges
|
onCancelChanges = onCancelChanges,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun NavController.navigateToEditPassword() {
|
fun NavController.navigateToEditPassword() {
|
||||||
this.navigate(EDIT_PASSWORD_ROUTE)
|
this.navigate(EDIT_PASSWORD_ROUTE)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<string name="feature_editpassword_password_changed_successfully">Password changed successfull</string>
|
<string name="feature_editpassword_password_changed_successfully">Password changed successfull</string>
|
||||||
<string name="feature_editpassword_change_password">Change Password</string>
|
<string name="feature_editpassword_change_password">Change Password</string>
|
||||||
|
|||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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 {
|
plugins {
|
||||||
alias(libs.plugins.mifospay.android.feature)
|
alias(libs.plugins.mifospay.android.feature)
|
||||||
alias(libs.plugins.mifospay.android.library.compose)
|
alias(libs.plugins.mifospay.android.library.compose)
|
||||||
|
|||||||
@ -1,2 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<manifest></manifest>
|
<manifest></manifest>
|
||||||
@ -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
|
package org.mifospay.feature.faq
|
||||||
|
|
||||||
data class FAQ(
|
internal data class FAQ(
|
||||||
var question: Int,
|
var question: Int,
|
||||||
var answer: Int? = null,
|
var answer: Int? = null,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.faq
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
@ -5,7 +14,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class FAQViewModel @Inject constructor() : ViewModel() {
|
internal class FAQViewModel @Inject constructor() : ViewModel() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a list of Frequently Asked Questions (FAQs).
|
* 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_question1, R.string.feature_faq_answer1),
|
||||||
FAQ(R.string.feature_faq_question2, R.string.feature_faq_answer2),
|
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_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),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.faq
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@ -14,31 +23,41 @@ import org.mifospay.core.designsystem.component.MifosTopBar
|
|||||||
import org.mifospay.core.ui.FaqItemScreen
|
import org.mifospay.core.ui.FaqItemScreen
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun FaqScreenRoute(
|
internal fun FaqScreenRoute(
|
||||||
navigateBack: () -> Unit,
|
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
|
@Composable
|
||||||
fun FaqScreen(
|
private fun FaqScreen(
|
||||||
navigateBack: () -> Unit,
|
navigateBack: () -> Unit,
|
||||||
faqList: List<FAQ>
|
faqList: List<FAQ>,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.fillMaxSize()) {
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxSize(),
|
||||||
|
) {
|
||||||
MifosTopBar(
|
MifosTopBar(
|
||||||
topBarTitle = R.string.feature_faq_frequently_asked_questions,
|
topBarTitle = R.string.feature_faq_frequently_asked_questions,
|
||||||
backPress = { navigateBack.invoke() })
|
backPress = { navigateBack.invoke() },
|
||||||
|
)
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
itemsIndexed(items = faqList) { _, faqItem ->
|
itemsIndexed(items = faqList) { _, faqItem ->
|
||||||
FaqItemScreen(
|
FaqItemScreen(
|
||||||
question = stringResource(id = faqItem.question),
|
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)
|
@Preview(showSystemUi = true)
|
||||||
@Composable
|
@Composable
|
||||||
fun FaqScreenPreview() {
|
private fun FaqScreenPreview() {
|
||||||
FaqScreen(
|
FaqScreen(
|
||||||
{}, listOf(
|
{},
|
||||||
|
listOf(
|
||||||
FAQ(R.string.feature_faq_question1, R.string.feature_faq_answer1),
|
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_question2, R.string.feature_faq_answer2),
|
||||||
FAQ(R.string.feature_faq_question3, R.string.feature_faq_answer3),
|
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),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.faq.navigation
|
||||||
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
@ -13,11 +22,11 @@ fun NavController.navigateToFAQ(navOptions: NavOptions? = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun NavGraphBuilder.faqScreen(
|
fun NavGraphBuilder.faqScreen(
|
||||||
navigateBack: () -> Unit
|
navigateBack: () -> Unit,
|
||||||
) {
|
) {
|
||||||
composable(route = FAQ_ROUTE) {
|
composable(route = FAQ_ROUTE) {
|
||||||
FaqScreenRoute(
|
FaqScreenRoute(
|
||||||
navigateBack = navigateBack
|
navigateBack = navigateBack,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<string name="feature_faq_question1">How can I add an Account?</string>
|
<string name="feature_faq_question1">How can I add an Account?</string>
|
||||||
<string name="feature_faq_question2">How can I access my Payment History?</string>
|
<string name="feature_faq_question2">How can I access my Payment History?</string>
|
||||||
|
|||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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 {
|
plugins {
|
||||||
alias(libs.plugins.mifospay.android.feature)
|
alias(libs.plugins.mifospay.android.feature)
|
||||||
alias(libs.plugins.mifospay.android.library.compose)
|
alias(libs.plugins.mifospay.android.library.compose)
|
||||||
@ -8,10 +17,5 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
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)
|
implementation(libs.accompanist.pager)
|
||||||
}
|
}
|
||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@ -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
|
package org.mifospay.feature.finance
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@ -6,50 +15,21 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import com.google.accompanist.pager.rememberPagerState
|
import com.google.accompanist.pager.rememberPagerState
|
||||||
import com.mifospay.core.model.domain.BankAccountDetails
|
|
||||||
import org.mifospay.core.ui.MifosScrollableTabRow
|
import org.mifospay.core.ui.MifosScrollableTabRow
|
||||||
import org.mifospay.core.ui.utility.TabContent
|
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
|
@Composable
|
||||||
fun FinanceRoute(
|
internal fun FinanceRoute(
|
||||||
onAddBtn: () -> Unit,
|
tabContents: List<TabContent>,
|
||||||
onLevel1Clicked: () -> Unit,
|
modifier: Modifier = Modifier,
|
||||||
onLevel2Clicked: () -> Unit,
|
|
||||||
onLevel3Clicked: () -> Unit,
|
|
||||||
navigateToBankAccountDetailScreen: (BankAccountDetails, Int) -> Unit,
|
|
||||||
navigateToLinkBankAccountScreen: () -> Unit
|
|
||||||
) {
|
) {
|
||||||
val pagerState = rememberPagerState(initialPage = 0)
|
val pagerState = rememberPagerState(initialPage = 0)
|
||||||
|
|
||||||
val tabContents = listOf(
|
Column(modifier = modifier.fillMaxSize()) {
|
||||||
TabContent(FinanceScreenContents.ACCOUNTS.name) {
|
MifosScrollableTabRow(
|
||||||
AccountsScreen(
|
tabContents = tabContents,
|
||||||
navigateToBankAccountDetailScreen = navigateToBankAccountDetailScreen,
|
pagerState = pagerState,
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,18 +37,13 @@ enum class FinanceScreenContents {
|
|||||||
ACCOUNTS,
|
ACCOUNTS,
|
||||||
CARDS,
|
CARDS,
|
||||||
MERCHANTS,
|
MERCHANTS,
|
||||||
KYC
|
KYC,
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
private fun FinanceScreenPreview() {
|
private fun FinanceScreenPreview() {
|
||||||
FinanceRoute(
|
FinanceRoute(
|
||||||
onAddBtn = {},
|
tabContents = emptyList(),
|
||||||
onLevel1Clicked = {},
|
|
||||||
onLevel2Clicked = {},
|
|
||||||
onLevel3Clicked = {},
|
|
||||||
navigateToBankAccountDetailScreen = { _, _ -> },
|
|
||||||
navigateToLinkBankAccountScreen = {}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.finance.navigation
|
||||||
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
import androidx.navigation.NavOptions
|
import androidx.navigation.NavOptions
|
||||||
import androidx.navigation.compose.composable
|
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
|
import org.mifospay.feature.finance.FinanceRoute
|
||||||
|
|
||||||
const val FINANCE_ROUTE = "finance_route"
|
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 NavController.navigateToFinance(navOptions: NavOptions) = navigate(FINANCE_ROUTE, navOptions)
|
||||||
|
|
||||||
fun NavGraphBuilder.financeScreen(
|
fun NavGraphBuilder.financeScreen(
|
||||||
onAddBtn: () -> Unit,
|
tabContents: List<TabContent>,
|
||||||
onLevel1Clicked: () -> Unit,
|
|
||||||
onLevel2Clicked: () -> Unit,
|
|
||||||
onLevel3Clicked: () -> Unit,
|
|
||||||
navigateToBankAccountDetailScreen: (BankAccountDetails,Int) -> Unit,
|
|
||||||
navigateToLinkBankAccountScreen: () -> Unit
|
|
||||||
) {
|
) {
|
||||||
composable(route = FINANCE_ROUTE) {
|
composable(route = FINANCE_ROUTE) {
|
||||||
FinanceRoute(
|
FinanceRoute(tabContents = tabContents)
|
||||||
onAddBtn = onAddBtn,
|
|
||||||
onLevel1Clicked = onLevel1Clicked,
|
|
||||||
onLevel2Clicked = onLevel2Clicked,
|
|
||||||
onLevel3Clicked = onLevel3Clicked,
|
|
||||||
navigateToBankAccountDetailScreen = navigateToBankAccountDetailScreen,
|
|
||||||
navigateToLinkBankAccountScreen = navigateToLinkBankAccountScreen
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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 {
|
plugins {
|
||||||
alias(libs.plugins.mifospay.android.feature)
|
alias(libs.plugins.mifospay.android.feature)
|
||||||
alias(libs.plugins.mifospay.android.library.compose)
|
alias(libs.plugins.mifospay.android.library.compose)
|
||||||
@ -8,6 +17,5 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(projects.feature.receipt)
|
|
||||||
implementation(projects.core.data)
|
implementation(projects.core.data)
|
||||||
}
|
}
|
||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@ -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<Transaction>?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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
|
package org.mifospay.feature.history
|
||||||
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
@ -46,9 +55,10 @@ import org.mifospay.feature.transaction.detail.TransactionDetailScreen
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HistoryScreen(
|
fun HistoryScreen(
|
||||||
viewModel: HistoryViewModel = hiltViewModel(),
|
|
||||||
viewReceipt: (String) -> Unit,
|
viewReceipt: (String) -> Unit,
|
||||||
accountClicked: (String, ArrayList<Transaction>) -> Unit,
|
accountClicked: (String, ArrayList<Transaction>) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
viewModel: HistoryViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
val historyUiState by viewModel.historyUiState.collectAsStateWithLifecycle()
|
val historyUiState by viewModel.historyUiState.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
@ -56,14 +66,16 @@ fun HistoryScreen(
|
|||||||
historyUiState = historyUiState,
|
historyUiState = historyUiState,
|
||||||
viewReceipt = viewReceipt,
|
viewReceipt = viewReceipt,
|
||||||
accountClicked = accountClicked,
|
accountClicked = accountClicked,
|
||||||
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HistoryScreen(
|
private fun HistoryScreen(
|
||||||
historyUiState: HistoryUiState,
|
historyUiState: HistoryUiState,
|
||||||
viewReceipt: (String) -> Unit,
|
viewReceipt: (String) -> Unit,
|
||||||
accountClicked: (String, ArrayList<Transaction>) -> Unit,
|
accountClicked: (String, ArrayList<Transaction>) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
var selectedChip by remember { mutableStateOf(TransactionType.OTHER) }
|
var selectedChip by remember { mutableStateOf(TransactionType.OTHER) }
|
||||||
var filteredTransactions by remember { mutableStateOf(emptyList<Transaction>()) }
|
var filteredTransactions by remember { mutableStateOf(emptyList<Transaction>()) }
|
||||||
@ -77,7 +89,7 @@ fun HistoryScreen(
|
|||||||
title = stringResource(id = R.string.feature_history_error_oops),
|
title = stringResource(id = R.string.feature_history_error_oops),
|
||||||
subTitle = stringResource(id = R.string.feature_history_empty_no_transaction_history_title),
|
subTitle = stringResource(id = R.string.feature_history_empty_no_transaction_history_title),
|
||||||
iconTint = MaterialTheme.colorScheme.primary,
|
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),
|
title = stringResource(id = R.string.feature_history_error_oops),
|
||||||
subTitle = stringResource(id = R.string.feature_history_unexpected_error_subtitle),
|
subTitle = stringResource(id = R.string.feature_history_unexpected_error_subtitle),
|
||||||
iconTint = MaterialTheme.colorScheme.primary,
|
iconTint = MaterialTheme.colorScheme.primary,
|
||||||
iconImageVector = Icons.Rounded.Info
|
iconImageVector = Icons.Rounded.Info,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,22 +116,22 @@ fun HistoryScreen(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(10.dp),
|
.padding(10.dp),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
) {
|
) {
|
||||||
Chip(
|
Chip(
|
||||||
selected = selectedChip == TransactionType.OTHER,
|
selected = selectedChip == TransactionType.OTHER,
|
||||||
onClick = { selectedChip = TransactionType.OTHER },
|
onClick = { selectedChip = TransactionType.OTHER },
|
||||||
label = stringResource(R.string.feature_history_all)
|
label = stringResource(R.string.feature_history_all),
|
||||||
)
|
)
|
||||||
Chip(
|
Chip(
|
||||||
selected = selectedChip == TransactionType.CREDIT,
|
selected = selectedChip == TransactionType.CREDIT,
|
||||||
onClick = { selectedChip = TransactionType.CREDIT },
|
onClick = { selectedChip = TransactionType.CREDIT },
|
||||||
label = stringResource(R.string.feature_history_credits)
|
label = stringResource(R.string.feature_history_credits),
|
||||||
)
|
)
|
||||||
Chip(
|
Chip(
|
||||||
selected = selectedChip == TransactionType.DEBIT,
|
selected = selectedChip == TransactionType.DEBIT,
|
||||||
onClick = { 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))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
@ -130,72 +142,74 @@ fun HistoryScreen(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(start = 24.dp, end = 24.dp)
|
.padding(start = 24.dp, end = 24.dp)
|
||||||
.clickable { transactionDetailState = it },
|
.clickable { transactionDetailState = it },
|
||||||
transaction = it
|
transaction = it,
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryUiState.Loading -> {
|
HistoryUiState.Loading -> {
|
||||||
MifosLoadingWheel(
|
MifosLoadingWheel(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
contentDesc = stringResource(R.string.feature_history_loading)
|
contentDesc = stringResource(R.string.feature_history_loading),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transactionDetailState != null) {
|
if (transactionDetailState != null) {
|
||||||
MifosBottomSheet(
|
MifosBottomSheet(
|
||||||
|
modifier = modifier,
|
||||||
content = {
|
content = {
|
||||||
TransactionDetailScreen(
|
TransactionDetailScreen(
|
||||||
transaction = transactionDetailState!!,
|
transaction = transactionDetailState!!,
|
||||||
viewReceipt = { transactionDetailState?.transactionId?.let { viewReceipt(it) } },
|
viewReceipt = { transactionDetailState?.transactionId?.let { viewReceipt(it) } },
|
||||||
accountClicked = { accountClicked(it, ArrayList(transactionsList)) }
|
accountClicked = { accountClicked(it, ArrayList(transactionsList)) },
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onDismiss = { transactionDetailState = null }
|
onDismiss = { transactionDetailState = null },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Chip(
|
private fun Chip(
|
||||||
selected: Boolean,
|
selected: Boolean,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
label: String
|
label: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val backgroundColor = if (selected) MaterialTheme.colorScheme.primary else lightGrey
|
val backgroundColor = if (selected) MaterialTheme.colorScheme.primary else lightGrey
|
||||||
Button(
|
Button(
|
||||||
|
modifier = modifier,
|
||||||
onClick = {
|
onClick = {
|
||||||
onClick()
|
onClick()
|
||||||
Toast.makeText(context, label, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, label, Toast.LENGTH_SHORT).show()
|
||||||
},
|
},
|
||||||
colors = ButtonDefaults.buttonColors(backgroundColor)
|
colors = ButtonDefaults.buttonColors(backgroundColor),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(top = 4.dp, bottom = 4.dp, start = 16.dp, end = 16.dp),
|
modifier = Modifier.padding(top = 4.dp, bottom = 4.dp, start = 16.dp, end = 16.dp),
|
||||||
text = label,
|
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<HistoryUiState> {
|
internal class HistoryPreviewProvider : PreviewParameterProvider<HistoryUiState> {
|
||||||
override val values: Sequence<HistoryUiState>
|
override val values: Sequence<HistoryUiState>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
HistoryUiState.Empty,
|
HistoryUiState.Empty,
|
||||||
HistoryUiState.Loading,
|
HistoryUiState.Loading,
|
||||||
HistoryUiState.Error("Error Screen"),
|
HistoryUiState.Error("Error Screen"),
|
||||||
HistoryUiState.HistoryList(sampleHistoryList)
|
HistoryUiState.HistoryList(sampleHistoryList),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val sampleHistoryList = List(10) { index ->
|
internal val sampleHistoryList = List(10) { index ->
|
||||||
Transaction(
|
Transaction(
|
||||||
transactionId = "txn_123456789",
|
transactionId = "txn_123456789",
|
||||||
clientId = 1001L,
|
clientId = 1001L,
|
||||||
@ -206,14 +220,14 @@ val sampleHistoryList = List(10) { index ->
|
|||||||
transactionType = TransactionType.CREDIT,
|
transactionType = TransactionType.CREDIT,
|
||||||
transferId = 3003L,
|
transferId = 3003L,
|
||||||
transferDetail = TransferDetail(),
|
transferDetail = TransferDetail(),
|
||||||
receiptId = "receipt_123456789"
|
receiptId = "receipt_123456789",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
fun HistoryScreenPreview(
|
private fun HistoryScreenPreview(
|
||||||
@PreviewParameter(HistoryPreviewProvider::class) historyUiState: HistoryUiState
|
@PreviewParameter(HistoryPreviewProvider::class) historyUiState: HistoryUiState,
|
||||||
) {
|
) {
|
||||||
HistoryScreen(historyUiState = historyUiState, viewReceipt = {}, accountClicked = { _, _ -> })
|
HistoryScreen(historyUiState = historyUiState, viewReceipt = {}, accountClicked = { _, _ -> })
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.history
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
@ -17,7 +26,7 @@ class HistoryViewModel @Inject constructor(
|
|||||||
private val mUseCaseHandler: UseCaseHandler,
|
private val mUseCaseHandler: UseCaseHandler,
|
||||||
private val mLocalRepository: LocalRepository,
|
private val mLocalRepository: LocalRepository,
|
||||||
private val mFetchAccountUseCase: FetchAccount,
|
private val mFetchAccountUseCase: FetchAccount,
|
||||||
private val fetchAccountTransactionsUseCase: FetchAccountTransactions
|
private val fetchAccountTransactionsUseCase: FetchAccountTransactions,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _historyUiState = MutableStateFlow<HistoryUiState>(HistoryUiState.Loading)
|
private val _historyUiState = MutableStateFlow<HistoryUiState>(HistoryUiState.Loading)
|
||||||
@ -25,7 +34,8 @@ class HistoryViewModel @Inject constructor(
|
|||||||
|
|
||||||
private fun fetchTransactions() {
|
private fun fetchTransactions() {
|
||||||
_historyUiState.value = HistoryUiState.Loading
|
_historyUiState.value = HistoryUiState.Loading
|
||||||
mUseCaseHandler.execute(mFetchAccountUseCase,
|
mUseCaseHandler.execute(
|
||||||
|
mFetchAccountUseCase,
|
||||||
FetchAccount.RequestValues(mLocalRepository.clientDetails.clientId),
|
FetchAccount.RequestValues(mLocalRepository.clientDetails.clientId),
|
||||||
object : UseCase.UseCaseCallback<FetchAccount.ResponseValue> {
|
object : UseCase.UseCaseCallback<FetchAccount.ResponseValue> {
|
||||||
override fun onSuccess(response: FetchAccount.ResponseValue) {
|
override fun onSuccess(response: FetchAccount.ResponseValue) {
|
||||||
@ -37,23 +47,28 @@ class HistoryViewModel @Inject constructor(
|
|||||||
override fun onError(message: String) {
|
override fun onError(message: String) {
|
||||||
_historyUiState.value = HistoryUiState.Error(message)
|
_historyUiState.value = HistoryUiState.Error(message)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fetchTransactionsHistory(accountId: Long) {
|
fun fetchTransactionsHistory(accountId: Long) {
|
||||||
mUseCaseHandler.execute(fetchAccountTransactionsUseCase,
|
mUseCaseHandler.execute(
|
||||||
|
fetchAccountTransactionsUseCase,
|
||||||
FetchAccountTransactions.RequestValues(accountId),
|
FetchAccountTransactions.RequestValues(accountId),
|
||||||
object : UseCase.UseCaseCallback<FetchAccountTransactions.ResponseValue?> {
|
object : UseCase.UseCaseCallback<FetchAccountTransactions.ResponseValue?> {
|
||||||
override fun onSuccess(response: FetchAccountTransactions.ResponseValue?) {
|
override fun onSuccess(response: FetchAccountTransactions.ResponseValue?) {
|
||||||
if (response?.transactions?.isNotEmpty() == true)
|
if (response?.transactions?.isNotEmpty() == true) {
|
||||||
_historyUiState.value = HistoryUiState.HistoryList(response.transactions)
|
_historyUiState.value = HistoryUiState.HistoryList(response.transactions)
|
||||||
else _historyUiState.value = HistoryUiState.Empty
|
} else {
|
||||||
|
_historyUiState.value = HistoryUiState.Empty
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(message: String) {
|
override fun onError(message: String) {
|
||||||
_historyUiState.value = HistoryUiState.Error(message)
|
_historyUiState.value = HistoryUiState.Error(message)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -66,4 +81,4 @@ sealed class HistoryUiState {
|
|||||||
data object Empty : HistoryUiState()
|
data object Empty : HistoryUiState()
|
||||||
data class Error(val message: String) : HistoryUiState()
|
data class Error(val message: String) : HistoryUiState()
|
||||||
data class HistoryList(val list: List<Transaction>) : HistoryUiState()
|
data class HistoryList(val list: List<Transaction>) : HistoryUiState()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.specific.transactions
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
@ -12,7 +21,7 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.Info
|
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.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
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.TransactionType
|
||||||
import com.mifospay.core.model.domain.client.Client
|
import com.mifospay.core.model.domain.client.Client
|
||||||
import com.mifospay.core.model.entity.accounts.savings.SavingAccount
|
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.MfLoadingWheel
|
||||||
import org.mifospay.core.designsystem.component.MifosScaffold
|
import org.mifospay.core.designsystem.component.MifosScaffold
|
||||||
import org.mifospay.core.designsystem.icon.MifosIcons
|
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.core.ui.ErrorScreenContent
|
||||||
import org.mifospay.feature.history.R
|
import org.mifospay.feature.history.R
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SpecificTransactionsScreen(
|
internal fun SpecificTransactionsScreen(
|
||||||
accountNumber: String,
|
accountNumber: String,
|
||||||
transactions: ArrayList<Transaction>,
|
transactions: List<Transaction>,
|
||||||
viewModel: SpecificTransactionsViewModel = hiltViewModel(),
|
|
||||||
backPress: () -> Unit,
|
backPress: () -> Unit,
|
||||||
transactionItemClicked: (String) -> Unit
|
transactionItemClicked: (String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
viewModel: SpecificTransactionsViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
val uiState = viewModel.uiState.collectAsStateWithLifecycle()
|
val uiState = viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
LaunchedEffect(key1 = Unit) {
|
LaunchedEffect(key1 = Unit) {
|
||||||
viewModel.setArguments(transactions, accountNumber)
|
viewModel.setArguments(transactions.toArrayList(), accountNumber)
|
||||||
viewModel.getSpecificTransactions()
|
viewModel.getSpecificTransactions()
|
||||||
}
|
}
|
||||||
|
|
||||||
SpecificTransactionsScreen(
|
SpecificTransactionsScreen(
|
||||||
uiState = uiState.value,
|
uiState = uiState.value,
|
||||||
backPress = backPress,
|
backPress = backPress,
|
||||||
transactionItemClicked = transactionItemClicked
|
transactionItemClicked = transactionItemClicked,
|
||||||
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SpecificTransactionsScreen(
|
private fun SpecificTransactionsScreen(
|
||||||
uiState: SpecificTransactionsUiState,
|
uiState: SpecificTransactionsUiState,
|
||||||
backPress: () -> Unit,
|
backPress: () -> Unit,
|
||||||
transactionItemClicked: (String) -> Unit,
|
transactionItemClicked: (String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
MifosScaffold(
|
MifosScaffold(
|
||||||
|
modifier = modifier,
|
||||||
topBarTitle = R.string.feature_history_specific_transactions_history,
|
topBarTitle = R.string.feature_history_specific_transactions_history,
|
||||||
backPress = backPress,
|
backPress = backPress,
|
||||||
scaffoldContent = { paddingValues ->
|
scaffoldContent = { paddingValues ->
|
||||||
@ -89,7 +102,7 @@ fun SpecificTransactionsScreen(
|
|||||||
SpecificTransactionsUiState.Loading -> {
|
SpecificTransactionsUiState.Loading -> {
|
||||||
MfLoadingWheel(
|
MfLoadingWheel(
|
||||||
contentDesc = stringResource(R.string.feature_history_loading),
|
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),
|
title = stringResource(id = R.string.feature_history_error_oops),
|
||||||
subTitle = stringResource(id = R.string.feature_history_no_transactions_found),
|
subTitle = stringResource(id = R.string.feature_history_no_transactions_found),
|
||||||
iconTint = MaterialTheme.colorScheme.onSurface,
|
iconTint = MaterialTheme.colorScheme.onSurface,
|
||||||
iconImageVector = Icons.Rounded.Info
|
iconImageVector = Icons.Rounded.Info,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
SpecificTransactionsContent(
|
SpecificTransactionsContent(
|
||||||
transactionList = uiState.transactionsList,
|
transactionList = uiState.transactionsList,
|
||||||
transactionItemClicked = transactionItemClicked
|
transactionItemClicked = transactionItemClicked,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SpecificTransactionsContent(
|
private fun SpecificTransactionsContent(
|
||||||
transactionList: ArrayList<Transaction>,
|
transactionList: List<Transaction>,
|
||||||
transactionItemClicked: (String) -> Unit
|
transactionItemClicked: (String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
LazyColumn {
|
LazyColumn(modifier = modifier) {
|
||||||
itemsIndexed(items = transactionList) { index, transaction ->
|
itemsIndexed(
|
||||||
|
items = transactionList,
|
||||||
|
) { index, transaction ->
|
||||||
SpecificTransactionItem(
|
SpecificTransactionItem(
|
||||||
transaction = transaction,
|
transaction = transaction,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(12.dp)
|
.padding(12.dp)
|
||||||
.clickable {
|
.clickable {
|
||||||
transaction.transactionId?.let { transactionItemClicked(it) }
|
transaction.transactionId?.let { transactionItemClicked(it) }
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
if (index != transactionList.lastIndex) {
|
if (index != transactionList.lastIndex) {
|
||||||
Divider()
|
HorizontalDivider()
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
}
|
}
|
||||||
@ -142,43 +158,43 @@ fun SpecificTransactionsContent(
|
|||||||
@Composable
|
@Composable
|
||||||
fun SpecificTransactionItem(
|
fun SpecificTransactionItem(
|
||||||
transaction: Transaction,
|
transaction: Transaction,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Column(modifier = modifier.padding(horizontal = 12.dp)) {
|
Column(modifier = modifier.padding(horizontal = 12.dp)) {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
SpecificTransactionAccountInfo(
|
SpecificTransactionAccountInfo(
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
account = transaction.transferDetail.fromAccount,
|
account = transaction.transferDetail.fromAccount,
|
||||||
client = transaction.transferDetail.fromClient
|
client = transaction.transferDetail.fromClient,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
)
|
)
|
||||||
Icon(imageVector = MifosIcons.SendRightTilted, contentDescription = null)
|
Icon(imageVector = MifosIcons.SendRightTilted, contentDescription = null)
|
||||||
SpecificTransactionAccountInfo(
|
SpecificTransactionAccountInfo(
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
account = transaction.transferDetail.toAccount,
|
account = transaction.transferDetail.toAccount,
|
||||||
client = transaction.transferDetail.toClient
|
client = transaction.transferDetail.toClient,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.padding(horizontal = 8.dp),
|
modifier = Modifier.padding(horizontal = 8.dp),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Column {
|
Column {
|
||||||
Text(
|
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,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
color = MaterialTheme.colorScheme.primary
|
color = MaterialTheme.colorScheme.primary,
|
||||||
)
|
)
|
||||||
Text(
|
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,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = when (transaction.transactionType) {
|
text = when (transaction.transactionType) {
|
||||||
TransactionType.DEBIT -> stringResource(id = R.string.feature_history_debits)
|
TransactionType.DEBIT -> stringResource(id = R.string.feature_history_debits)
|
||||||
TransactionType.CREDIT -> stringResource(id = R.string.feature_history_credits)
|
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,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
)
|
)
|
||||||
@ -198,11 +214,11 @@ fun SpecificTransactionItem(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SpecificTransactionAccountInfo(
|
internal fun SpecificTransactionAccountInfo(
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
account: SavingAccount,
|
account: SavingAccount,
|
||||||
client: Client,
|
client: Client,
|
||||||
accountClicked: (String) -> Unit = {}
|
modifier: Modifier = Modifier,
|
||||||
|
accountClicked: (String) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier.clickable {
|
modifier = modifier.clickable {
|
||||||
@ -222,7 +238,7 @@ fun SpecificTransactionAccountInfo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SpecificTransactionsUiStateProvider :
|
internal class SpecificTransactionsUiStateProvider :
|
||||||
PreviewParameterProvider<SpecificTransactionsUiState> {
|
PreviewParameterProvider<SpecificTransactionsUiState> {
|
||||||
override val values: Sequence<SpecificTransactionsUiState>
|
override val values: Sequence<SpecificTransactionsUiState>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
@ -235,9 +251,9 @@ class SpecificTransactionsUiStateProvider :
|
|||||||
|
|
||||||
@Preview(showSystemUi = true)
|
@Preview(showSystemUi = true)
|
||||||
@Composable
|
@Composable
|
||||||
fun ShowQrScreenPreview(
|
private fun ShowQrScreenPreview(
|
||||||
@PreviewParameter(SpecificTransactionsUiStateProvider::class)
|
@PreviewParameter(SpecificTransactionsUiStateProvider::class)
|
||||||
uiState: SpecificTransactionsUiState
|
uiState: SpecificTransactionsUiState,
|
||||||
) {
|
) {
|
||||||
MifosTheme {
|
MifosTheme {
|
||||||
SpecificTransactionsScreen(
|
SpecificTransactionsScreen(
|
||||||
@ -250,8 +266,8 @@ fun ShowQrScreenPreview(
|
|||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun SpecificTransactionItemPreview() {
|
private fun SpecificTransactionItemPreview() {
|
||||||
Surface {
|
Surface {
|
||||||
SpecificTransactionItem(transaction = Transaction())
|
SpecificTransactionItem(transaction = Transaction())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.specific.transactions
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
@ -14,7 +23,7 @@ import javax.inject.Inject
|
|||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class SpecificTransactionsViewModel @Inject constructor(
|
class SpecificTransactionsViewModel @Inject constructor(
|
||||||
private val mUseCaseFactory: UseCaseFactory,
|
private val mUseCaseFactory: UseCaseFactory,
|
||||||
private var mTaskLooper: TaskLooper? = null
|
private var mTaskLooper: TaskLooper? = null,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _uiState: MutableStateFlow<SpecificTransactionsUiState> =
|
private val _uiState: MutableStateFlow<SpecificTransactionsUiState> =
|
||||||
@ -39,16 +48,18 @@ class SpecificTransactionsViewModel @Inject constructor(
|
|||||||
val transferId = transaction.transferId
|
val transferId = transaction.transferId
|
||||||
mTaskLooper?.addTask(
|
mTaskLooper?.addTask(
|
||||||
useCase = mUseCaseFactory.getUseCase(Constants.FETCH_ACCOUNT_TRANSFER_USECASE)
|
useCase = mUseCaseFactory.getUseCase(Constants.FETCH_ACCOUNT_TRANSFER_USECASE)
|
||||||
as UseCase<FetchAccountTransfer.RequestValues, FetchAccountTransfer.ResponseValue>,
|
as UseCase<FetchAccountTransfer.RequestValues, FetchAccountTransfer.ResponseValue>,
|
||||||
values = FetchAccountTransfer.RequestValues(transferId),
|
values = FetchAccountTransfer.RequestValues(transferId),
|
||||||
taskData = TaskLooper.TaskData(
|
taskData = TaskLooper.TaskData(
|
||||||
org.mifospay.common.Constants.TRANSFER_DETAILS, i
|
org.mifospay.common.Constants.TRANSFER_DETAILS,
|
||||||
)
|
i,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
mTaskLooper!!.listen(object : TaskLooper.Listener {
|
mTaskLooper!!.listen(object : TaskLooper.Listener {
|
||||||
override fun <R : UseCase.ResponseValue?> onTaskSuccess(
|
override fun <R : UseCase.ResponseValue?> onTaskSuccess(
|
||||||
taskData: TaskLooper.TaskData, response: R
|
taskData: TaskLooper.TaskData,
|
||||||
|
response: R,
|
||||||
) {
|
) {
|
||||||
when (taskData.taskName) {
|
when (taskData.taskName) {
|
||||||
org.mifospay.common.Constants.TRANSFER_DETAILS -> {
|
org.mifospay.common.Constants.TRANSFER_DETAILS -> {
|
||||||
@ -89,4 +100,4 @@ sealed class SpecificTransactionsUiState {
|
|||||||
data object Loading : SpecificTransactionsUiState()
|
data object Loading : SpecificTransactionsUiState()
|
||||||
data object Error : SpecificTransactionsUiState()
|
data object Error : SpecificTransactionsUiState()
|
||||||
data class Success(val transactionsList: ArrayList<Transaction>) : SpecificTransactionsUiState()
|
data class Success(val transactionsList: ArrayList<Transaction>) : SpecificTransactionsUiState()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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")
|
@file:Suppress("MaxLineLength")
|
||||||
|
|
||||||
package org.mifospay.feature.specific.transactions.navigation
|
package org.mifospay.feature.specific.transactions.navigation
|
||||||
@ -15,27 +24,32 @@ const val SPECIFIC_TRANSACTIONS_ROUTE = "specific_transactions_route"
|
|||||||
|
|
||||||
fun NavGraphBuilder.specificTransactionsScreen(
|
fun NavGraphBuilder.specificTransactionsScreen(
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
onTransactionItemClicked: (String) -> Unit
|
onTransactionItemClicked: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
composable(
|
composable(
|
||||||
route = "$SPECIFIC_TRANSACTIONS_ROUTE?${Constants.ACCOUNT_NUMBER}={accountNumber}&${Constants.TRANSACTIONS}={transactions}",
|
route = "$SPECIFIC_TRANSACTIONS_ROUTE?${Constants.ACCOUNT_NUMBER}={accountNumber}&${Constants.TRANSACTIONS}={transactions}",
|
||||||
arguments = listOf(
|
arguments = listOf(
|
||||||
navArgument(Constants.ACCOUNT_NUMBER) { type = NavType.StringType },
|
navArgument(Constants.ACCOUNT_NUMBER) { type = NavType.StringType },
|
||||||
navArgument(Constants.TRANSACTIONS) { type = NavType.StringType }
|
navArgument(Constants.TRANSACTIONS) { type = NavType.StringType },
|
||||||
)
|
),
|
||||||
) { backStackEntry ->
|
) { backStackEntry ->
|
||||||
val accountNumber = backStackEntry.arguments?.getString(Constants.ACCOUNT_NUMBER) ?: ""
|
val accountNumber = backStackEntry.arguments?.getString(Constants.ACCOUNT_NUMBER) ?: ""
|
||||||
val transactions = backStackEntry.arguments?.getParcelableArrayList<Transaction>(Constants.TRANSACTIONS) ?: arrayListOf()
|
val transactions =
|
||||||
|
backStackEntry.arguments?.getParcelableArrayList<Transaction>(Constants.TRANSACTIONS)
|
||||||
|
?: arrayListOf()
|
||||||
|
|
||||||
SpecificTransactionsScreen(
|
SpecificTransactionsScreen(
|
||||||
accountNumber = accountNumber,
|
accountNumber = accountNumber,
|
||||||
transactions = transactions,
|
transactions = transactions.toList(),
|
||||||
backPress = onBackClick,
|
backPress = onBackClick,
|
||||||
transactionItemClicked = onTransactionItemClicked
|
transactionItemClicked = onTransactionItemClicked,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun NavController.navigateToSpecificTransactions(accountNumber: String, transactions: ArrayList<Transaction>) {
|
fun NavController.navigateToSpecificTransactions(
|
||||||
|
accountNumber: String,
|
||||||
|
transactions: ArrayList<Transaction>,
|
||||||
|
) {
|
||||||
this.navigate("$SPECIFIC_TRANSACTIONS_ROUTE?${Constants.ACCOUNT_NUMBER}=$accountNumber&${Constants.TRANSACTIONS}=$transactions")
|
this.navigate("$SPECIFIC_TRANSACTIONS_ROUTE?${Constants.ACCOUNT_NUMBER}=$accountNumber&${Constants.TRANSACTIONS}=$transactions")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.transaction.detail
|
||||||
|
|
||||||
import androidx.compose.foundation.Image
|
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.history.R
|
||||||
import org.mifospay.feature.specific.transactions.SpecificTransactionAccountInfo
|
import org.mifospay.feature.specific.transactions.SpecificTransactionAccountInfo
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TransactionDetailScreen(
|
internal fun TransactionDetailScreen(
|
||||||
viewModel: TransactionDetailViewModel = hiltViewModel(),
|
|
||||||
transaction: Transaction,
|
transaction: Transaction,
|
||||||
viewReceipt: () -> Unit,
|
viewReceipt: () -> Unit,
|
||||||
accountClicked: (String) -> Unit,
|
accountClicked: (String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
viewModel: TransactionDetailViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val uiState = viewModel.transactionDetailUiState.collectAsStateWithLifecycle()
|
val uiState = viewModel.transactionDetailUiState.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
LaunchedEffect(key1 = transaction) {
|
LaunchedEffect(key1 = transaction) {
|
||||||
@ -55,21 +63,23 @@ fun TransactionDetailScreen(
|
|||||||
uiState = uiState.value,
|
uiState = uiState.value,
|
||||||
transaction = transaction,
|
transaction = transaction,
|
||||||
viewReceipt = viewReceipt,
|
viewReceipt = viewReceipt,
|
||||||
accountClicked = accountClicked
|
accountClicked = accountClicked,
|
||||||
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TransactionDetailScreen(
|
private fun TransactionDetailScreen(
|
||||||
uiState: TransactionDetailUiState,
|
uiState: TransactionDetailUiState,
|
||||||
transaction: Transaction,
|
transaction: Transaction,
|
||||||
viewReceipt: () -> Unit,
|
viewReceipt: () -> Unit,
|
||||||
accountClicked: (String) -> Unit,
|
accountClicked: (String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = modifier
|
||||||
.padding(20.dp)
|
.padding(20.dp)
|
||||||
.height(300.dp)
|
.height(300.dp),
|
||||||
) {
|
) {
|
||||||
when (uiState) {
|
when (uiState) {
|
||||||
is TransactionDetailUiState.Error -> {
|
is TransactionDetailUiState.Error -> {
|
||||||
@ -78,7 +88,7 @@ fun TransactionDetailScreen(
|
|||||||
|
|
||||||
is TransactionDetailUiState.Loading -> {
|
is TransactionDetailUiState.Loading -> {
|
||||||
MfLoadingWheel(
|
MfLoadingWheel(
|
||||||
backgroundColor = Color.Black.copy(alpha = 0.6f)
|
backgroundColor = Color.Black.copy(alpha = 0.6f),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +96,7 @@ fun TransactionDetailScreen(
|
|||||||
TransactionsDetailContent(
|
TransactionsDetailContent(
|
||||||
transaction = transaction,
|
transaction = transaction,
|
||||||
viewReceipt = viewReceipt,
|
viewReceipt = viewReceipt,
|
||||||
accountClicked = accountClicked
|
accountClicked = accountClicked,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,36 +104,36 @@ fun TransactionDetailScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TransactionsDetailContent(
|
private fun TransactionsDetailContent(
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
transaction: Transaction,
|
transaction: Transaction,
|
||||||
viewReceipt: () -> Unit,
|
viewReceipt: () -> Unit,
|
||||||
accountClicked: (String) -> Unit,
|
accountClicked: (String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier.padding(12.dp),
|
modifier = modifier.padding(12.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = 12.dp)
|
.padding(horizontal = 12.dp)
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Column {
|
Column {
|
||||||
Text(
|
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,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
color = MaterialTheme.colorScheme.primary
|
color = MaterialTheme.colorScheme.primary,
|
||||||
)
|
)
|
||||||
Text(
|
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,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
)
|
)
|
||||||
if (transaction.receiptId != null) {
|
if (transaction.receiptId != null) {
|
||||||
Text(
|
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,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -145,31 +155,32 @@ fun TransactionsDetailContent(
|
|||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
account = transaction.transferDetail.fromAccount,
|
account = transaction.transferDetail.fromAccount,
|
||||||
client = transaction.transferDetail.fromClient,
|
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(
|
SpecificTransactionAccountInfo(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
account = transaction.transferDetail.toAccount,
|
account = transaction.transferDetail.toAccount,
|
||||||
client = transaction.transferDetail.toClient,
|
client = transaction.transferDetail.toClient,
|
||||||
accountClicked = accountClicked
|
accountClicked = accountClicked,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
HorizontalDivider(modifier = Modifier.padding(vertical = 12.dp))
|
HorizontalDivider(modifier = Modifier.padding(vertical = 12.dp))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_receipt_view_Receipt),
|
text = stringResource(id = R.string.feature_history_view_Receipt),
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
color = MaterialTheme.colorScheme.primary,
|
color = MaterialTheme.colorScheme.primary,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
modifier = Modifier.clickable { viewReceipt() }
|
modifier = Modifier.clickable { viewReceipt() },
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class TransactionDetailUiStateProvider :
|
class TransactionDetailUiStateProvider :
|
||||||
PreviewParameterProvider<TransactionDetailUiState> {
|
PreviewParameterProvider<TransactionDetailUiState> {
|
||||||
override val values: Sequence<TransactionDetailUiState>
|
override val values: Sequence<TransactionDetailUiState>
|
||||||
@ -182,7 +193,10 @@ class TransactionDetailUiStateProvider :
|
|||||||
|
|
||||||
@Preview(showSystemUi = true)
|
@Preview(showSystemUi = true)
|
||||||
@Composable
|
@Composable
|
||||||
fun ShowQrScreenPreview(@PreviewParameter(TransactionDetailUiStateProvider::class) uiState: TransactionDetailUiState) {
|
private fun ShowQrScreenPreview(
|
||||||
|
@PreviewParameter(TransactionDetailUiStateProvider::class)
|
||||||
|
uiState: TransactionDetailUiState,
|
||||||
|
) {
|
||||||
MifosTheme {
|
MifosTheme {
|
||||||
TransactionDetailScreen(
|
TransactionDetailScreen(
|
||||||
uiState = uiState,
|
uiState = uiState,
|
||||||
@ -191,4 +205,4 @@ fun ShowQrScreenPreview(@PreviewParameter(TransactionDetailUiStateProvider::clas
|
|||||||
accountClicked = {},
|
accountClicked = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.transaction.detail
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
@ -12,7 +21,7 @@ import javax.inject.Inject
|
|||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class TransactionDetailViewModel @Inject constructor(
|
class TransactionDetailViewModel @Inject constructor(
|
||||||
private val mUseCaseHandler: UseCaseHandler,
|
private val mUseCaseHandler: UseCaseHandler,
|
||||||
private val mFetchAccountTransferUseCase: FetchAccountTransfer
|
private val mFetchAccountTransferUseCase: FetchAccountTransfer,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _transactionDetailUiState: MutableStateFlow<TransactionDetailUiState> =
|
private val _transactionDetailUiState: MutableStateFlow<TransactionDetailUiState> =
|
||||||
@ -20,7 +29,8 @@ class TransactionDetailViewModel @Inject constructor(
|
|||||||
val transactionDetailUiState get() = _transactionDetailUiState
|
val transactionDetailUiState get() = _transactionDetailUiState
|
||||||
|
|
||||||
fun getTransferDetail(transferId: Long) {
|
fun getTransferDetail(transferId: Long) {
|
||||||
mUseCaseHandler.execute(mFetchAccountTransferUseCase,
|
mUseCaseHandler.execute(
|
||||||
|
mFetchAccountTransferUseCase,
|
||||||
FetchAccountTransfer.RequestValues(transferId),
|
FetchAccountTransfer.RequestValues(transferId),
|
||||||
object : UseCase.UseCaseCallback<FetchAccountTransfer.ResponseValue?> {
|
object : UseCase.UseCaseCallback<FetchAccountTransfer.ResponseValue?> {
|
||||||
override fun onSuccess(response: FetchAccountTransfer.ResponseValue?) {
|
override fun onSuccess(response: FetchAccountTransfer.ResponseValue?) {
|
||||||
@ -31,7 +41,7 @@ class TransactionDetailViewModel @Inject constructor(
|
|||||||
override fun onError(message: String) {
|
override fun onError(message: String) {
|
||||||
_transactionDetailUiState.value = TransactionDetailUiState.Error
|
_transactionDetailUiState.value = TransactionDetailUiState.Error
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,4 +50,4 @@ sealed class TransactionDetailUiState {
|
|||||||
data object Loading : TransactionDetailUiState()
|
data object Loading : TransactionDetailUiState()
|
||||||
data object Error : TransactionDetailUiState()
|
data object Error : TransactionDetailUiState()
|
||||||
data class Success(val transferDetail: TransferDetail?) : TransactionDetailUiState()
|
data class Success(val transferDetail: TransferDetail?) : TransactionDetailUiState()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
|
<path android:fillColor="@android:color/white" android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
|
||||||
|
|||||||
@ -1,5 +1,19 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
|
<string name="feature_history_transaction_id">Transaction ID : </string>
|
||||||
|
<string name="feature_history_transaction_date">Transaction Date : </string>
|
||||||
|
<string name="feature_history_other">Other</string>
|
||||||
|
<string name="feature_history_pan_id">PAN ID</string>
|
||||||
|
<string name="feature_history_view_Receipt">View Receipt</string>
|
||||||
<string name="feature_history_error_oops">Oops!</string>
|
<string name="feature_history_error_oops">Oops!</string>
|
||||||
<string name="feature_history_empty_no_transaction_history_title">Your history is empty</string>
|
<string name="feature_history_empty_no_transaction_history_title">Your history is empty</string>
|
||||||
<string name="feature_history_unexpected_error_subtitle">An unexpected error occurred. Please try again.</string>
|
<string name="feature_history_unexpected_error_subtitle">An unexpected error occurred. Please try again.</string>
|
||||||
|
|||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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 {
|
plugins {
|
||||||
alias(libs.plugins.mifospay.android.feature)
|
alias(libs.plugins.mifospay.android.feature)
|
||||||
alias(libs.plugins.mifospay.android.library.compose)
|
alias(libs.plugins.mifospay.android.library.compose)
|
||||||
@ -8,6 +17,5 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(projects.feature.history)
|
|
||||||
implementation(projects.core.data)
|
implementation(projects.core.data)
|
||||||
}
|
}
|
||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@ -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
|
package org.mifospay.feature.home
|
||||||
|
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
@ -48,10 +57,11 @@ import org.mifospay.core.ui.ErrorScreenContent
|
|||||||
import org.mifospay.core.ui.TransactionItemScreen
|
import org.mifospay.core.ui.TransactionItemScreen
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeRoute(
|
internal fun HomeRoute(
|
||||||
homeViewModel: HomeViewModel = hiltViewModel(),
|
|
||||||
onRequest: (String) -> Unit,
|
onRequest: (String) -> Unit,
|
||||||
onPay: () -> Unit
|
onPay: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
homeViewModel: HomeViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
val homeUIState by homeViewModel
|
val homeUIState by homeViewModel
|
||||||
.homeUIState
|
.homeUIState
|
||||||
@ -61,7 +71,7 @@ fun HomeRoute(
|
|||||||
is HomeUiState.Loading -> {
|
is HomeUiState.Loading -> {
|
||||||
MfLoadingWheel(
|
MfLoadingWheel(
|
||||||
contentDesc = stringResource(R.string.feature_home_loading),
|
contentDesc = stringResource(R.string.feature_home_loading),
|
||||||
backgroundColor = MaterialTheme.colorScheme.surface
|
backgroundColor = MaterialTheme.colorScheme.surface,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +83,8 @@ fun HomeRoute(
|
|||||||
onRequest = {
|
onRequest = {
|
||||||
onRequest.invoke(successState.vpa ?: "")
|
onRequest.invoke(successState.vpa ?: "")
|
||||||
},
|
},
|
||||||
onPay = onPay
|
onPay = onPay,
|
||||||
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,23 +92,23 @@ fun HomeRoute(
|
|||||||
ErrorScreenContent(
|
ErrorScreenContent(
|
||||||
onClickRetry = {
|
onClickRetry = {
|
||||||
homeViewModel.fetchAccountDetails()
|
homeViewModel.fetchAccountDetails()
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeScreen(
|
private fun HomeScreen(
|
||||||
account: Account?,
|
account: Account?,
|
||||||
transactions: List<Transaction>,
|
transactions: List<Transaction>,
|
||||||
onRequest: () -> Unit,
|
onRequest: () -> Unit,
|
||||||
onPay: () -> Unit
|
onPay: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|
||||||
.background(color = MaterialTheme.colorScheme.surface)
|
.background(color = MaterialTheme.colorScheme.surface)
|
||||||
.padding(start = 32.dp, end = 32.dp),
|
.padding(start = 32.dp, end = 32.dp),
|
||||||
) {
|
) {
|
||||||
@ -107,7 +118,7 @@ fun HomeScreen(
|
|||||||
item {
|
item {
|
||||||
PayRequestScreen(
|
PayRequestScreen(
|
||||||
onRequest = onRequest,
|
onRequest = onRequest,
|
||||||
onPay = onPay
|
onPay = onPay,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (transactions.isNotEmpty()) {
|
if (transactions.isNotEmpty()) {
|
||||||
@ -116,7 +127,7 @@ fun HomeScreen(
|
|||||||
modifier = Modifier.padding(top = 32.dp),
|
modifier = Modifier.padding(top = 32.dp),
|
||||||
text = "Recent Transactions",
|
text = "Recent Transactions",
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
}
|
}
|
||||||
@ -131,19 +142,22 @@ fun HomeScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MifosWalletCardScreen(account: Account?) {
|
private fun MifosWalletCardScreen(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
account: Account? = null,
|
||||||
|
) {
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(225.dp)
|
.height(225.dp)
|
||||||
.padding(top = 20.dp, bottom = 32.dp),
|
.padding(top = 20.dp, bottom = 32.dp),
|
||||||
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.onSurface)
|
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.onSurface),
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
.padding(start = 36.dp),
|
.padding(start = 36.dp),
|
||||||
verticalArrangement = Arrangement.Center
|
verticalArrangement = Arrangement.Center,
|
||||||
) {
|
) {
|
||||||
val walletBalanceLabel =
|
val walletBalanceLabel =
|
||||||
if (account != null) "(${account.currency.displayLabel})" else ""
|
if (account != null) "(${account.currency.displayLabel})" else ""
|
||||||
@ -152,96 +166,109 @@ fun MifosWalletCardScreen(account: Account?) {
|
|||||||
style = TextStyle(
|
style = TextStyle(
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
fontWeight = FontWeight.W400,
|
fontWeight = FontWeight.W400,
|
||||||
color = lightGrey
|
color = lightGrey,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(10.dp))
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
val accountBalance =
|
val accountBalance =
|
||||||
if (account != null) Utils.getFormattedAccountBalance(
|
if (account != null) {
|
||||||
account.balance, account.currency.code
|
Utils.getFormattedAccountBalance(
|
||||||
) else "0"
|
account.balance,
|
||||||
|
account.currency.code,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
"0"
|
||||||
|
}
|
||||||
Text(
|
Text(
|
||||||
text = accountBalance,
|
text = accountBalance,
|
||||||
style = TextStyle(
|
style = TextStyle(
|
||||||
fontSize = 42.sp,
|
fontSize = 42.sp,
|
||||||
fontWeight = FontWeight(600),
|
fontWeight = FontWeight(600),
|
||||||
color = MaterialTheme.colorScheme.surface
|
color = MaterialTheme.colorScheme.surface,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(10.dp))
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
val currencyEqual = if (account != null) {
|
val currencyEqual = if (account != null) {
|
||||||
"${account.currency.code}1 ${account.currency.displayLabel}"
|
"${account.currency.code}1 ${account.currency.displayLabel}"
|
||||||
} else ""
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
Text(
|
Text(
|
||||||
text = currencyEqual,
|
text = currencyEqual,
|
||||||
style = TextStyle(
|
style = TextStyle(
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
fontWeight = FontWeight(500),
|
fontWeight = FontWeight(500),
|
||||||
color = lightGrey
|
color = lightGrey,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PayRequestScreen(
|
private fun PayRequestScreen(
|
||||||
onRequest: () -> Unit,
|
onRequest: () -> Unit,
|
||||||
onPay: () -> Unit
|
onPay: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
) {
|
) {
|
||||||
PayCard(
|
PayCard(
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
title = "Request",
|
title = "Request",
|
||||||
icon = R.drawable.core_ui_money_in
|
icon = R.drawable.core_ui_money_in,
|
||||||
) {
|
{
|
||||||
onRequest.invoke()
|
onRequest.invoke()
|
||||||
}
|
},
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
Spacer(modifier = Modifier.width(16.dp))
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
PayCard(
|
PayCard(
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
title = "Pay",
|
title = "Pay",
|
||||||
icon = R.drawable.core_ui_money_out
|
icon = R.drawable.core_ui_money_out,
|
||||||
) {
|
{
|
||||||
onPay.invoke()
|
onPay.invoke()
|
||||||
}
|
},
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PayCard(
|
private fun PayCard(
|
||||||
modifier: Modifier,
|
|
||||||
title: String,
|
title: String,
|
||||||
icon: Int,
|
icon: Int,
|
||||||
onClickCard: () -> Unit
|
onClickCard: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Card(
|
Card(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.height(144.dp)
|
.height(144.dp)
|
||||||
.clickable { onClickCard.invoke() },
|
.clickable { onClickCard.invoke() },
|
||||||
border = BorderStroke(1.dp, border),
|
border = BorderStroke(1.dp, border),
|
||||||
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface)
|
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
.padding(top = 20.dp, bottom = 20.dp, start = 20.dp),
|
.padding(top = 20.dp, bottom = 20.dp, start = 20.dp),
|
||||||
verticalArrangement = Arrangement.SpaceBetween
|
verticalArrangement = Arrangement.SpaceBetween,
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(40.dp)
|
.size(40.dp)
|
||||||
.background(MaterialTheme.colorScheme.onSurface, shape = RoundedCornerShape(4.dp)),
|
.background(
|
||||||
contentAlignment = Alignment.Center
|
MaterialTheme.colorScheme.onSurface,
|
||||||
|
shape = RoundedCornerShape(4.dp),
|
||||||
|
),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
Image(
|
Image(
|
||||||
modifier = Modifier.size(20.dp),
|
modifier = Modifier.size(20.dp),
|
||||||
painter = painterResource(id = icon),
|
painter = painterResource(id = icon),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.surface)
|
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.surface),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Text(text = title)
|
Text(text = title)
|
||||||
@ -251,7 +278,7 @@ fun PayCard(
|
|||||||
|
|
||||||
@Preview(showSystemUi = true, device = "id:pixel_5")
|
@Preview(showSystemUi = true, device = "id:pixel_5")
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeScreenPreview() {
|
private fun HomeScreenPreview() {
|
||||||
HomeScreen(
|
HomeScreen(
|
||||||
account = Account(
|
account = Account(
|
||||||
image = "",
|
image = "",
|
||||||
@ -262,9 +289,9 @@ fun HomeScreenPreview() {
|
|||||||
currency = Currency(
|
currency = Currency(
|
||||||
code = "USD",
|
code = "USD",
|
||||||
displayLabel = "$",
|
displayLabel = "$",
|
||||||
displaySymbol = "$"
|
displaySymbol = "$",
|
||||||
),
|
),
|
||||||
productId = 1223
|
productId = 1223,
|
||||||
),
|
),
|
||||||
transactions = List(25) { index ->
|
transactions = List(25) { index ->
|
||||||
Transaction(
|
Transaction(
|
||||||
@ -273,24 +300,29 @@ fun HomeScreenPreview() {
|
|||||||
currency = Currency(
|
currency = Currency(
|
||||||
code = "USD",
|
code = "USD",
|
||||||
displayLabel = "$",
|
displayLabel = "$",
|
||||||
displaySymbol = "$"
|
displaySymbol = "$",
|
||||||
),
|
),
|
||||||
transactionType = TransactionType.CREDIT
|
transactionType = TransactionType.CREDIT,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onPay = {},
|
onPay = {},
|
||||||
onRequest = {}
|
onRequest = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun PayRequestScreenPreview() {
|
private fun PayRequestScreenPreview() {
|
||||||
PayRequestScreen({}, {})
|
PayRequestScreen({}, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun PayCardPreview() {
|
private fun PayCardPreview() {
|
||||||
PayCard(Modifier.width(150.dp), "Request", R.drawable.feature_home_ic_arrow_back_black_24dp) { }
|
PayCard(
|
||||||
|
"Request",
|
||||||
|
R.drawable.feature_home_ic_arrow_back_black_24dp,
|
||||||
|
{ },
|
||||||
|
Modifier.width(150.dp),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.home
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
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.UseCase.UseCaseCallback
|
||||||
import org.mifospay.core.data.base.UseCaseHandler
|
import org.mifospay.core.data.base.UseCaseHandler
|
||||||
import org.mifospay.core.data.domain.usecase.account.FetchAccount
|
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.data.repository.local.LocalRepository
|
||||||
import org.mifospay.core.datastore.PreferencesHelper
|
import org.mifospay.core.datastore.PreferencesHelper
|
||||||
import org.mifospay.feature.HistoryContract
|
|
||||||
import org.mifospay.feature.TransactionsHistory
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
@ -23,8 +32,8 @@ class HomeViewModel @Inject constructor(
|
|||||||
private val localRepository: LocalRepository,
|
private val localRepository: LocalRepository,
|
||||||
private val preferencesHelper: PreferencesHelper,
|
private val preferencesHelper: PreferencesHelper,
|
||||||
private val fetchAccountUseCase: FetchAccount,
|
private val fetchAccountUseCase: FetchAccount,
|
||||||
private val transactionsHistory: TransactionsHistory
|
private val transactionsHistory: TransactionsHistory,
|
||||||
) : ViewModel(), HistoryContract.TransactionsHistoryAsync{
|
) : ViewModel(), HistoryContract.TransactionsHistoryAsync {
|
||||||
|
|
||||||
// Expose screen UI state
|
// Expose screen UI state
|
||||||
private val _homeUIState: MutableStateFlow<HomeUiState> = MutableStateFlow(HomeUiState.Loading)
|
private val _homeUIState: MutableStateFlow<HomeUiState> = MutableStateFlow(HomeUiState.Loading)
|
||||||
@ -36,7 +45,8 @@ class HomeViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun fetchAccountDetails() {
|
fun fetchAccountDetails() {
|
||||||
useCaseHandler.execute(fetchAccountUseCase,
|
useCaseHandler.execute(
|
||||||
|
fetchAccountUseCase,
|
||||||
FetchAccount.RequestValues(localRepository.clientDetails.clientId),
|
FetchAccount.RequestValues(localRepository.clientDetails.clientId),
|
||||||
object : UseCaseCallback<FetchAccount.ResponseValue> {
|
object : UseCaseCallback<FetchAccount.ResponseValue> {
|
||||||
override fun onSuccess(response: FetchAccount.ResponseValue) {
|
override fun onSuccess(response: FetchAccount.ResponseValue) {
|
||||||
@ -44,7 +54,7 @@ class HomeViewModel @Inject constructor(
|
|||||||
_homeUIState.update {
|
_homeUIState.update {
|
||||||
HomeUiState.Success(
|
HomeUiState.Success(
|
||||||
account = response.account,
|
account = response.account,
|
||||||
vpa = localRepository.clientDetails.externalId
|
vpa = localRepository.clientDetails.externalId,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
response.account.id.let {
|
response.account.id.let {
|
||||||
@ -55,7 +65,8 @@ class HomeViewModel @Inject constructor(
|
|||||||
override fun onError(message: String) {
|
override fun onError(message: String) {
|
||||||
_homeUIState.update { HomeUiState.Error }
|
_homeUIState.update { HomeUiState.Error }
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTransactionsFetchCompleted(transactions: List<Transaction>?) {
|
override fun onTransactionsFetchCompleted(transactions: List<Transaction>?) {
|
||||||
@ -71,8 +82,8 @@ sealed interface HomeUiState {
|
|||||||
data class Success(
|
data class Success(
|
||||||
val account: Account? = null,
|
val account: Account? = null,
|
||||||
val transactions: List<Transaction> = emptyList(),
|
val transactions: List<Transaction> = emptyList(),
|
||||||
val vpa: String? = null
|
val vpa: String? = null,
|
||||||
) : HomeUiState
|
) : HomeUiState
|
||||||
|
|
||||||
data object Error : HomeUiState
|
data object Error : HomeUiState
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.home.navigation
|
||||||
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
@ -12,12 +21,12 @@ fun NavController.navigateToHome(navOptions: NavOptions) = navigate(HOME_ROUTE,
|
|||||||
|
|
||||||
fun NavGraphBuilder.homeScreen(
|
fun NavGraphBuilder.homeScreen(
|
||||||
onRequest: (String) -> Unit,
|
onRequest: (String) -> Unit,
|
||||||
onPay: () -> Unit
|
onPay: () -> Unit,
|
||||||
) {
|
) {
|
||||||
composable(route = HOME_ROUTE) {
|
composable(route = HOME_ROUTE) {
|
||||||
HomeRoute(
|
HomeRoute(
|
||||||
onRequest = onRequest,
|
onRequest = onRequest,
|
||||||
onPay = onPay
|
onPay = onPay,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
|
|||||||
@ -1,4 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<color name="feature_home_colorBlack87">#DE000000</color>
|
<color name="feature_home_colorBlack87">#DE000000</color>
|
||||||
</resources>
|
</resources>
|
||||||
@ -1,4 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<string name="feature_home_loading">Loading</string>
|
<string name="feature_home_loading">Loading</string>
|
||||||
</resources>
|
</resources>
|
||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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 {
|
plugins {
|
||||||
alias(libs.plugins.mifospay.android.feature)
|
alias(libs.plugins.mifospay.android.feature)
|
||||||
alias(libs.plugins.mifospay.android.library.compose)
|
alias(libs.plugins.mifospay.android.library.compose)
|
||||||
@ -9,5 +18,4 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(projects.core.data)
|
implementation(projects.core.data)
|
||||||
implementation(projects.feature.receipt)
|
|
||||||
}
|
}
|
||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@ -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
|
package org.mifospay.feature.invoices
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.gestures.detectTapGestures
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
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.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@ -40,30 +47,31 @@ import org.mifospay.core.ui.ErrorScreenContent
|
|||||||
import org.mifospay.invoices.R
|
import org.mifospay.invoices.R
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun InvoiceDetailScreen(
|
internal fun InvoiceDetailScreen(
|
||||||
viewModel: InvoiceDetailViewModel = hiltViewModel(),
|
|
||||||
data: Uri?,
|
|
||||||
onBackPress: () -> Unit,
|
onBackPress: () -> Unit,
|
||||||
navigateToReceiptScreen: (String) -> Unit
|
navigateToReceiptScreen: (String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
viewModel: InvoiceDetailViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
val invoiceDetailUiState by viewModel.invoiceDetailUiState.collectAsStateWithLifecycle()
|
val invoiceDetailUiState by viewModel.invoiceDetailUiState.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
InvoiceDetailScreen(
|
InvoiceDetailScreen(
|
||||||
invoiceDetailUiState = invoiceDetailUiState,
|
invoiceDetailUiState = invoiceDetailUiState,
|
||||||
onBackPress = onBackPress,
|
onBackPress = onBackPress,
|
||||||
navigateToReceiptScreen = navigateToReceiptScreen
|
navigateToReceiptScreen = navigateToReceiptScreen,
|
||||||
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
LaunchedEffect(key1 = true) {
|
|
||||||
viewModel.getInvoiceDetails(data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun InvoiceDetailScreen(
|
private fun InvoiceDetailScreen(
|
||||||
invoiceDetailUiState: InvoiceDetailUiState,
|
invoiceDetailUiState: InvoiceDetailUiState,
|
||||||
onBackPress: () -> Unit,
|
onBackPress: () -> Unit,
|
||||||
navigateToReceiptScreen: (String) -> Unit
|
navigateToReceiptScreen: (String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
MifosScaffold(
|
MifosScaffold(
|
||||||
|
modifier = modifier,
|
||||||
topBarTitle = R.string.feature_invoices_invoice,
|
topBarTitle = R.string.feature_invoices_invoice,
|
||||||
backPress = { onBackPress.invoke() },
|
backPress = { onBackPress.invoke() },
|
||||||
scaffoldContent = {
|
scaffoldContent = {
|
||||||
@ -71,7 +79,7 @@ fun InvoiceDetailScreen(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(MaterialTheme.colorScheme.surface)
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
.padding(it)
|
.padding(it),
|
||||||
) {
|
) {
|
||||||
when (invoiceDetailUiState) {
|
when (invoiceDetailUiState) {
|
||||||
is InvoiceDetailUiState.Error -> {
|
is InvoiceDetailUiState.Error -> {
|
||||||
@ -80,7 +88,7 @@ fun InvoiceDetailScreen(
|
|||||||
|
|
||||||
InvoiceDetailUiState.Loading -> {
|
InvoiceDetailUiState.Loading -> {
|
||||||
MfOverlayLoadingWheel(
|
MfOverlayLoadingWheel(
|
||||||
contentDesc = stringResource(R.string.feature_invoices_loading)
|
contentDesc = stringResource(R.string.feature_invoices_loading),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,90 +97,92 @@ fun InvoiceDetailScreen(
|
|||||||
invoiceDetailUiState.invoice,
|
invoiceDetailUiState.invoice,
|
||||||
invoiceDetailUiState.merchantId,
|
invoiceDetailUiState.merchantId,
|
||||||
invoiceDetailUiState.paymentLink,
|
invoiceDetailUiState.paymentLink,
|
||||||
navigateToReceiptScreen = navigateToReceiptScreen
|
navigateToReceiptScreen = navigateToReceiptScreen,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod")
|
||||||
fun InvoiceDetailsContent(
|
private fun InvoiceDetailsContent(
|
||||||
invoice: Invoice?,
|
invoice: Invoice?,
|
||||||
merchantId: String?,
|
merchantId: String?,
|
||||||
paymentLink: String?,
|
paymentLink: String?,
|
||||||
navigateToReceiptScreen: (String) -> Unit
|
navigateToReceiptScreen: (String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val clipboardManager = LocalClipboardManager.current
|
val clipboardManager = LocalClipboardManager.current
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = 16.dp),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.feature_invoices_invoice_details),
|
text = stringResource(R.string.feature_invoices_invoice_details),
|
||||||
modifier = Modifier.padding(top = 16.dp)
|
modifier = Modifier.padding(top = 16.dp),
|
||||||
)
|
)
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.feature_invoices_merchant_id),
|
text = stringResource(R.string.feature_invoices_merchant_id),
|
||||||
modifier = Modifier.padding(top = 16.dp)
|
modifier = Modifier.padding(top = 16.dp),
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = merchantId.toString(),
|
text = merchantId.toString(),
|
||||||
modifier = Modifier.padding(top = 16.dp)
|
modifier = Modifier.padding(top = 16.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.feature_invoices_consumer_id),
|
text = stringResource(R.string.feature_invoices_consumer_id),
|
||||||
modifier = Modifier.padding(top = 8.dp)
|
modifier = Modifier.padding(top = 8.dp),
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = (invoice?.consumerName + " " + invoice?.consumerId),
|
text = (invoice?.consumerName + " " + invoice?.consumerId),
|
||||||
modifier = Modifier.padding(top = 8.dp)
|
modifier = Modifier.padding(top = 8.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_invoices_amount),
|
text = stringResource(id = R.string.feature_invoices_amount),
|
||||||
modifier = Modifier.padding(top = 8.dp)
|
modifier = Modifier.padding(top = 8.dp),
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = Constants.INR + " " + invoice?.amount + "",
|
text = Constants.INR + " " + invoice?.amount + "",
|
||||||
modifier = Modifier.padding(top = 8.dp)
|
modifier = Modifier.padding(top = 8.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_invoices_items_bought),
|
text = stringResource(id = R.string.feature_invoices_items_bought),
|
||||||
modifier = Modifier.padding(top = 8.dp)
|
modifier = Modifier.padding(top = 8.dp),
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = invoice?.itemsBought.toString(),
|
text = invoice?.itemsBought.toString(),
|
||||||
modifier = Modifier.padding(top = 8.dp)
|
modifier = Modifier.padding(top = 8.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,42 +190,42 @@ fun InvoiceDetailsContent(
|
|||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_invoices_status),
|
text = stringResource(id = R.string.feature_invoices_status),
|
||||||
modifier = Modifier.padding(top = 8.dp)
|
modifier = Modifier.padding(top = 8.dp),
|
||||||
)
|
)
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = Constants.DONE,
|
text = Constants.DONE,
|
||||||
modifier = Modifier.padding(top = 8.dp)
|
modifier = Modifier.padding(top = 8.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_invoices_transaction_id),
|
text = stringResource(id = R.string.feature_invoices_transaction_id),
|
||||||
color = MaterialTheme.colorScheme.primary,
|
color = MaterialTheme.colorScheme.primary,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(top = 10.dp)
|
.padding(top = 10.dp),
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = invoice.transactionId ?: "",
|
text = invoice.transactionId ?: "",
|
||||||
color = MaterialTheme.colorScheme.primary,
|
color = MaterialTheme.colorScheme.primary,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(top = 10.dp)
|
.padding(top = 10.dp)
|
||||||
.then(Modifier.height(0.dp))
|
.then(Modifier.height(0.dp)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Divider()
|
Divider()
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_invoices_unique_receipt_link),
|
text = stringResource(id = R.string.feature_invoices_unique_receipt_link),
|
||||||
fontWeight = FontWeight.Normal,
|
fontWeight = FontWeight.Normal,
|
||||||
modifier = Modifier.padding(bottom = 10.dp)
|
modifier = Modifier.padding(bottom = 10.dp),
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = invoice.transactionId ?: "",
|
text = invoice.transactionId ?: "",
|
||||||
@ -227,33 +237,34 @@ fun InvoiceDetailsContent(
|
|||||||
onPress = {
|
onPress = {
|
||||||
invoice.transactionId?.let { it1 ->
|
invoice.transactionId?.let { it1 ->
|
||||||
navigateToReceiptScreen.invoke(
|
navigateToReceiptScreen.invoke(
|
||||||
it1
|
it1,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongPress = {
|
onLongPress = {
|
||||||
clipboardManager.setText(
|
clipboardManager.setText(
|
||||||
AnnotatedString(
|
AnnotatedString(
|
||||||
Constants.RECEIPT_DOMAIN + invoice.transactionId
|
Constants.RECEIPT_DOMAIN + invoice.transactionId,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
})
|
},
|
||||||
}
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_invoices_date),
|
text = stringResource(id = R.string.feature_invoices_date),
|
||||||
modifier = Modifier.padding(top = 10.dp)
|
modifier = Modifier.padding(top = 10.dp),
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = DateHelper.getDateAsString(invoice!!.date),
|
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(
|
||||||
text = stringResource(id = R.string.feature_invoices_payment_options_will_be_fetched_from_upi),
|
text = stringResource(id = R.string.feature_invoices_payment_options_will_be_fetched_from_upi),
|
||||||
fontWeight = FontWeight.Normal,
|
fontWeight = FontWeight.Normal,
|
||||||
modifier = Modifier.padding(vertical = 10.dp)
|
modifier = Modifier.padding(vertical = 10.dp),
|
||||||
)
|
)
|
||||||
Divider()
|
Divider()
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.feature_invoices_unique_payment_link),
|
text = stringResource(id = R.string.feature_invoices_unique_payment_link),
|
||||||
fontWeight = FontWeight.Normal,
|
fontWeight = FontWeight.Normal,
|
||||||
modifier = Modifier.padding(bottom = 10.dp)
|
modifier = Modifier.padding(bottom = 10.dp),
|
||||||
)
|
)
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = paymentLink.toString(),
|
text = paymentLink.toString(),
|
||||||
@ -283,21 +294,21 @@ fun InvoiceDetailsContent(
|
|||||||
detectTapGestures(
|
detectTapGestures(
|
||||||
onLongPress = {
|
onLongPress = {
|
||||||
clipboardManager.setText(AnnotatedString(paymentLink.toString()))
|
clipboardManager.setText(AnnotatedString(paymentLink.toString()))
|
||||||
})
|
},
|
||||||
}
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Divider(modifier: Modifier = Modifier) {
|
private fun Divider(modifier: Modifier = Modifier) {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(1.dp)
|
.height(1.dp)
|
||||||
.background(Color(0x44000000))
|
.background(Color(0x44000000)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,21 +317,21 @@ class InvoiceDetailScreenProvider : PreviewParameterProvider<InvoiceDetailUiStat
|
|||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
InvoiceDetailUiState.Loading,
|
InvoiceDetailUiState.Loading,
|
||||||
InvoiceDetailUiState.Error("Some Error Occurred"),
|
InvoiceDetailUiState.Error("Some Error Occurred"),
|
||||||
InvoiceDetailUiState.Success(Invoice(), "", "")
|
InvoiceDetailUiState.Success(Invoice(), "", ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview(showBackground = true, showSystemUi = true)
|
@Preview(showBackground = true, showSystemUi = true)
|
||||||
@Composable
|
@Composable
|
||||||
fun InvoiceDetailScreenPreview(
|
private fun InvoiceDetailScreenPreview(
|
||||||
@PreviewParameter(InvoiceDetailScreenProvider::class) invoiceDetailUiState: InvoiceDetailUiState
|
@PreviewParameter(InvoiceDetailScreenProvider::class)
|
||||||
|
invoiceDetailUiState: InvoiceDetailUiState,
|
||||||
) {
|
) {
|
||||||
MifosTheme {
|
MifosTheme {
|
||||||
InvoiceDetailScreen(
|
InvoiceDetailScreen(
|
||||||
invoiceDetailUiState = invoiceDetailUiState,
|
invoiceDetailUiState = invoiceDetailUiState,
|
||||||
onBackPress = {},
|
onBackPress = {},
|
||||||
navigateToReceiptScreen = {}
|
navigateToReceiptScreen = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +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.invoices
|
package org.mifospay.feature.invoices
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.mifospay.core.model.entity.Invoice
|
import com.mifospay.core.model.entity.Invoice
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
@ -10,34 +20,48 @@ import org.mifospay.core.data.base.UseCase
|
|||||||
import org.mifospay.core.data.base.UseCaseHandler
|
import org.mifospay.core.data.base.UseCaseHandler
|
||||||
import org.mifospay.core.data.domain.usecase.invoice.FetchInvoice
|
import org.mifospay.core.data.domain.usecase.invoice.FetchInvoice
|
||||||
import org.mifospay.core.datastore.PreferencesHelper
|
import org.mifospay.core.datastore.PreferencesHelper
|
||||||
|
import org.mifospay.feature.invoices.navigation.INVOICE_DATA_ARG
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class InvoiceDetailViewModel @Inject constructor(
|
class InvoiceDetailViewModel @Inject constructor(
|
||||||
private val mUseCaseHandler: UseCaseHandler,
|
private val mUseCaseHandler: UseCaseHandler,
|
||||||
private val mPreferencesHelper: PreferencesHelper,
|
private val mPreferencesHelper: PreferencesHelper,
|
||||||
private val fetchInvoiceUseCase: FetchInvoice
|
private val fetchInvoiceUseCase: FetchInvoice,
|
||||||
|
savedStateHandle: SavedStateHandle,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _invoiceDetailUiState =
|
private val _invoiceDetailUiState =
|
||||||
MutableStateFlow<InvoiceDetailUiState>(InvoiceDetailUiState.Loading)
|
MutableStateFlow<InvoiceDetailUiState>(InvoiceDetailUiState.Loading)
|
||||||
val invoiceDetailUiState: StateFlow<InvoiceDetailUiState> = _invoiceDetailUiState
|
val invoiceDetailUiState: StateFlow<InvoiceDetailUiState> = _invoiceDetailUiState
|
||||||
|
|
||||||
fun getInvoiceDetails(data: Uri?) {
|
init {
|
||||||
mUseCaseHandler.execute(fetchInvoiceUseCase, FetchInvoice.RequestValues(data),
|
savedStateHandle.get<String>(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<FetchInvoice.ResponseValue> {
|
object : UseCase.UseCaseCallback<FetchInvoice.ResponseValue> {
|
||||||
override fun onSuccess(response: FetchInvoice.ResponseValue) {
|
override fun onSuccess(response: FetchInvoice.ResponseValue) {
|
||||||
_invoiceDetailUiState.value = InvoiceDetailUiState.Success(
|
_invoiceDetailUiState.value = InvoiceDetailUiState.Success(
|
||||||
response.invoices[0],
|
response.invoices[0],
|
||||||
mPreferencesHelper.fullName + " "
|
mPreferencesHelper.fullName + " " +
|
||||||
+ mPreferencesHelper.clientId, data.toString()
|
mPreferencesHelper.clientId,
|
||||||
|
data.toString(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(message: String) {
|
override fun onError(message: String) {
|
||||||
_invoiceDetailUiState.value = InvoiceDetailUiState.Error(message)
|
_invoiceDetailUiState.value = InvoiceDetailUiState.Error(message)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,8 +70,8 @@ sealed interface InvoiceDetailUiState {
|
|||||||
data class Success(
|
data class Success(
|
||||||
val invoice: Invoice?,
|
val invoice: Invoice?,
|
||||||
val merchantId: String?,
|
val merchantId: String?,
|
||||||
val paymentLink: String?
|
val paymentLink: String?,
|
||||||
) : InvoiceDetailUiState
|
) : InvoiceDetailUiState
|
||||||
|
|
||||||
data class Error(val message: String) : InvoiceDetailUiState
|
data class Error(val message: String) : InvoiceDetailUiState
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.invoices
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
@ -25,81 +34,84 @@ import org.mifospay.core.designsystem.theme.grey
|
|||||||
import org.mifospay.invoices.R
|
import org.mifospay.invoices.R
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun InvoiceItem(
|
internal fun InvoiceItem(
|
||||||
invoiceTitle: String,
|
invoiceTitle: String,
|
||||||
invoiceAmount: String,
|
invoiceAmount: String,
|
||||||
invoiceStatus: String,
|
invoiceStatus: String,
|
||||||
invoiceDate: String,
|
invoiceDate: String,
|
||||||
invoiceId: String,
|
invoiceId: String,
|
||||||
invoiceStatusIcon: Long,
|
invoiceStatusIcon: Long,
|
||||||
onClick: (String) -> Unit
|
onClick: (String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(4.dp)
|
.padding(4.dp)
|
||||||
.clickable { onClick(invoiceId) },
|
.clickable { onClick(invoiceId) },
|
||||||
elevation = CardDefaults.cardElevation(4.dp),
|
elevation = CardDefaults.cardElevation(4.dp),
|
||||||
colors = CardDefaults.cardColors(Color.White)
|
colors = CardDefaults.cardColors(Color.White),
|
||||||
) {
|
) {
|
||||||
Column {
|
Column {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(10.dp),
|
.padding(10.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(
|
painter = painterResource(
|
||||||
id = if (invoiceStatusIcon == 0L)
|
id = if (invoiceStatusIcon == 0L) {
|
||||||
R.drawable.feature_invoices_ic_remove_circle_outline_black_24dp
|
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",
|
contentDescription = "Invoice Status",
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(64.dp)
|
.size(64.dp)
|
||||||
.padding(5.dp),
|
.padding(5.dp),
|
||||||
tint = if (invoiceStatusIcon == 0L) Color.Yellow else Color.Blue
|
tint = if (invoiceStatusIcon == 0L) Color.Yellow else Color.Blue,
|
||||||
)
|
)
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(end = 10.dp)
|
.padding(end = 10.dp),
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = invoiceTitle,
|
text = invoiceTitle,
|
||||||
color = Color.Black,
|
color = Color.Black,
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f),
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = invoiceAmount,
|
text = invoiceAmount,
|
||||||
color = Color.Black,
|
color = Color.Black,
|
||||||
textAlign = TextAlign.End,
|
textAlign = TextAlign.End,
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Text(
|
Text(
|
||||||
text = invoiceStatus,
|
text = invoiceStatus,
|
||||||
color = grey,
|
color = grey,
|
||||||
modifier = Modifier.padding(top = 1.dp)
|
modifier = Modifier.padding(top = 1.dp),
|
||||||
)
|
)
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = invoiceDate,
|
text = invoiceDate,
|
||||||
color = grey,
|
color = grey,
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f),
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = invoiceId,
|
text = invoiceId,
|
||||||
color = grey,
|
color = grey,
|
||||||
textAlign = TextAlign.End,
|
textAlign = TextAlign.End,
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(
|
Spacer(
|
||||||
@ -107,7 +119,7 @@ fun InvoiceItem(
|
|||||||
.padding(top = 10.dp)
|
.padding(top = 10.dp)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(1.dp)
|
.height(1.dp)
|
||||||
.background(Color.Gray)
|
.background(Color.Gray),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,6 +129,14 @@ fun InvoiceItem(
|
|||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
fun PreviewInvoiceItem() {
|
private fun PreviewInvoiceItem() {
|
||||||
InvoiceItem("Logo for Richard", "$3000", "Pending", "12/3/4", "Invoice id:12345", 0L) {}
|
InvoiceItem(
|
||||||
|
invoiceTitle = "Logo for Richard",
|
||||||
|
invoiceAmount = "$3000",
|
||||||
|
invoiceStatus = "Pending",
|
||||||
|
invoiceDate = "12/3/4",
|
||||||
|
invoiceId = "Invoice id:12345",
|
||||||
|
invoiceStatusIcon = 0L,
|
||||||
|
onClick = {},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
package org.mifospay.feature.invoices
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
@ -25,24 +34,26 @@ import org.mifospay.invoices.R
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun InvoiceScreenRoute(
|
fun InvoiceScreenRoute(
|
||||||
|
navigateToInvoiceDetailScreen: (Uri) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
viewModel: InvoicesViewModel = hiltViewModel(),
|
viewModel: InvoicesViewModel = hiltViewModel(),
|
||||||
navigateToInvoiceDetailScreen: (Uri) -> Unit
|
|
||||||
) {
|
) {
|
||||||
val invoiceUiState by viewModel.invoiceUiState.collectAsStateWithLifecycle()
|
val invoiceUiState by viewModel.invoiceUiState.collectAsStateWithLifecycle()
|
||||||
InvoiceScreen(
|
InvoiceScreen(
|
||||||
invoiceUiState = invoiceUiState,
|
invoiceUiState = invoiceUiState,
|
||||||
getUniqueInvoiceLink = { viewModel.getUniqueInvoiceLink(it) },
|
getUniqueInvoiceLink = { viewModel.getUniqueInvoiceLink(it) },
|
||||||
navigateToInvoiceDetailScreen = navigateToInvoiceDetailScreen
|
navigateToInvoiceDetailScreen = navigateToInvoiceDetailScreen,
|
||||||
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun InvoiceScreen(
|
private fun InvoiceScreen(
|
||||||
invoiceUiState: InvoicesUiState,
|
invoiceUiState: InvoicesUiState,
|
||||||
getUniqueInvoiceLink: (Long) -> Uri?,
|
getUniqueInvoiceLink: (Long) -> Uri?,
|
||||||
navigateToInvoiceDetailScreen: (Uri) -> Unit
|
navigateToInvoiceDetailScreen: (Uri) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
when (invoiceUiState) {
|
when (invoiceUiState) {
|
||||||
is InvoicesUiState.Error -> {
|
is InvoicesUiState.Error -> {
|
||||||
EmptyContentScreen(
|
EmptyContentScreen(
|
||||||
@ -50,14 +61,16 @@ fun InvoiceScreen(
|
|||||||
title = stringResource(id = R.string.feature_invoices_error_oops),
|
title = stringResource(id = R.string.feature_invoices_error_oops),
|
||||||
subTitle = stringResource(id = R.string.feature_invoices_unexpected_error_subtitle),
|
subTitle = stringResource(id = R.string.feature_invoices_unexpected_error_subtitle),
|
||||||
iconTint = Color.Black,
|
iconTint = Color.Black,
|
||||||
iconImageVector = Info
|
iconImageVector = Info,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is InvoicesUiState.InvoiceList -> {
|
is InvoicesUiState.InvoiceList -> {
|
||||||
Column(modifier = Modifier.fillMaxSize()) {
|
Column(modifier = modifier.fillMaxSize()) {
|
||||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||||
items(invoiceUiState.list) {
|
items(
|
||||||
|
items = invoiceUiState.list,
|
||||||
|
) {
|
||||||
InvoiceItem(
|
InvoiceItem(
|
||||||
invoiceTitle = it?.title.toString(),
|
invoiceTitle = it?.title.toString(),
|
||||||
invoiceAmount = it?.amount.toString(),
|
invoiceAmount = it?.amount.toString(),
|
||||||
@ -70,7 +83,7 @@ fun InvoiceScreen(
|
|||||||
invoiceUri?.let { uri ->
|
invoiceUri?.let { uri ->
|
||||||
navigateToInvoiceDetailScreen.invoke(uri)
|
navigateToInvoiceDetailScreen.invoke(uri)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,7 +102,7 @@ fun InvoiceScreen(
|
|||||||
InvoicesUiState.Loading -> {
|
InvoicesUiState.Loading -> {
|
||||||
MifosLoadingWheel(
|
MifosLoadingWheel(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
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> {
|
|||||||
InvoicesUiState.Loading,
|
InvoicesUiState.Loading,
|
||||||
InvoicesUiState.Empty,
|
InvoicesUiState.Empty,
|
||||||
InvoicesUiState.InvoiceList(sampleInvoiceList),
|
InvoicesUiState.InvoiceList(sampleInvoiceList),
|
||||||
InvoicesUiState.Error("Some Error Occurred")
|
InvoicesUiState.Error("Some Error Occurred"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview(showBackground = true, showSystemUi = true)
|
@Preview(showBackground = true, showSystemUi = true)
|
||||||
@Composable
|
@Composable
|
||||||
private fun InvoiceScreenPreview(
|
private fun InvoiceScreenPreview(
|
||||||
@PreviewParameter(InvoicesUiStateProvider::class) invoiceUiState: InvoicesUiState
|
@PreviewParameter(InvoicesUiStateProvider::class) invoiceUiState: InvoicesUiState,
|
||||||
) {
|
) {
|
||||||
MifosTheme {
|
MifosTheme {
|
||||||
InvoiceScreen(invoiceUiState = invoiceUiState, getUniqueInvoiceLink = { Uri.EMPTY }, {})
|
InvoiceScreen(invoiceUiState = invoiceUiState, getUniqueInvoiceLink = { Uri.EMPTY }, {})
|
||||||
@ -125,6 +138,6 @@ val sampleInvoiceList = List(10) { index ->
|
|||||||
transactionId = "txn_78910",
|
transactionId = "txn_78910",
|
||||||
id = index.toLong(),
|
id = index.toLong(),
|
||||||
title = "Stationery Purchase",
|
title = "Stationery Purchase",
|
||||||
date = mutableListOf(2024, 3, 23)
|
date = mutableListOf(2024, 3, 23),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user