diff --git a/README.md b/README.md
index 2396869e5..cc83fca94 100644
--- a/README.md
+++ b/README.md
@@ -6,16 +6,63 @@ Mobile Wallet is an Android-based framework for mobile wallets based on top of <
clean architecture and contains a core library module
that can be used as a dependency in any other wallet based project. It is developed at MIFOS together with a global community.
+## KMP Status for modules
+
+| Module | Progress | Desktop supported | Android supported | iOS supported | Web supported(JS) | Web supported(WASM-JS) |
+|-------------------------------|-------------------|-------------------|--------------------|-------------------|-------------------|------------------------|
+| mifospay-android | In progress | ✔️ | ✔️ | ✔️ | ✔️ | ❔ |
+| mifospay-desktop | In progress | ✔️ | ✔️ | ✔️ | ✔️ | ❔ |
+| mifospay-web | In progress | ✔️ | ✔️ | ✔️ | ✔️ | ❔ |
+| mifospay-ios | No implementation | No implementation | No implementation | No implementation | No implementation | No implementation |
+| :core:analytics | Done | ❌ | ✔️ | ❌ | ❌ | ❌ |
+| :core:common | Done | ✅ | ✅ | ❔ | ✅ | ❔ |
+| :core:data | Done | ✅ | ✅ | ❔ | ✅ | ❔ |
+| :core:datastore | Done | ✅ | ✅ | ❔ | ✅ | ❔ |
+| :core:datastore-proto | Done | ✅ | ✅ | ❔ | ✅ | ❔ |
+| :core:designsystem | Done | ✅ | ✅ | ❔ | ✅ | ❔ |
+| :core:domain | Done | ✅ | ✅ | ❔ | ✅ | ❔ |
+| :core:model | Done | ✅ | ✅ | ❔ | ✅ | ❔ |
+| :core:network | Done | ✅ | ✅ | ❔ | ✅ | ❔ |
+| :core:ui | Done | ✅ | ✅ | ❔ | ✅ | ❔ |
+| :feature:auth | Done | ✅ | ✅ | ❔ | ✅ | ❔ |
+| :feature:editpassword | Done | ✅ | ✅ | ❔ | ✅ | ❔ |
+| :feature:faq | Done | ✅ | ✅ | ❔ | ✅ | ❔ |
+| :feature:history | Done | ✅ | ✅ | ❔ | ✅ | ❔ |
+| :feature:home | Done | ✅ | ✅ | ❔ | ✅ | ❔ |
+| :feature:profile | Done | ✅ | ✅ | ❔ | ✅ | ❔ |
+| :feature:settings | Done | ✅ | ✅ | ❔ | ✅ | ❔ |
+| :feature:payments | Done | ✅ | ✅ | ✅ | ✅ | ❔ |
+| :feature:account | Not started | ❌ | ❌ | ❌ | ❌ | ❌ |
+| :feature:finance | Not started | ❌ | ❌ | ❌ | ❌ | ❌ |
+| :feature:invoices | Not started | ❌ | ❌ | ❌ | ❌ | ❌ |
+| :feature:kyc | Not started | ❌ | ❌ | ❌ | ❌ | ❌ |
+| :feature:make-transfer | Not started | ❌ | ❌ | ❌ | ❌ | ❌ |
+| :feature:merchants | Not started | ❌ | ❌ | ❌ | ❌ | ❌ |
+| :feature:notification | Not started | ❌ | ❌ | ❌ | ❌ | ❌ |
+| :feature:qr | Not started | ❌ | ❌ | ❌ | ❌ | ❌ |
+| :feature:receipt | Not started | ❌ | ❌ | ❌ | ❌ | ❌ |
+| :feature:request-money | Not started | ❌ | ❌ | ❌ | ❌ | ❌ |
+| :feature:saved-cards | Not started | ❌ | ❌ | ❌ | ❌ | ❌ |
+| :feature:search | Not started | ❌ | ❌ | ❌ | ❌ | ❌ |
+| :feature:send-money | Not started | ❌ | ❌ | ❌ | ❌ | ❌ |
+| :feature:standing-instruction | Not started | ❌ | ❌ | ❌ | ❌ | ❌ |
+| :feature:upi-setup | Not started | ❌ | ❌ | ❌ | ❌ | ❌ |
+| lint | Not started | ❌ | ❌ | ❌ | ❌ | ❌ |
+
+✅: Functioning properly
+❔: Not yet tested, but expected to work
+✔️: Successfully compiled
+❌: Not functioning, requires further attention
+
## Notice
:warning: We are fully committed to implement [Jetpack Compose](https://developer.android.com/jetpack/compose) and moving ourself to support
-`kotlin multi-platform`. **If you are sending any PR regarding `XML changes` we will `not` consider at this moment but converting XML to jetpack compose are most welcome.** If you sending any PR regarding logical changes in Activity/Fragment you are most welcome.
-
+`kotlin multi-platform`. **If you are sending any PR regarding `XML changes` we will `not` consider at this moment but converting XML to jetpack compose are most welcome.** If you sending any PR regarding logical changes in Activity/Fragment you are most welcome.
-Development | Chat |
-|-----------------|-----------------|
-![Mobile-Wallet CI[Master/Dev]](https://github.com/openMF/mobile-wallet/workflows/Mobile-Wallet%20CI%5BMaster/Dev%5D/badge.svg?branch=dev) | [](https://mifos.slack.com/) |
+| Development | Chat |
+|--------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|
+| ![Mobile-Wallet CI[Master/Dev]](https://github.com/openMF/mobile-wallet/workflows/Mobile-Wallet%20CI%5BMaster/Dev%5D/badge.svg?branch=dev) | [](https://mifos.slack.com/) |
## Join Us on Slack
diff --git a/core/designsystem/src/commonMain/kotlin/org/mifospay/core/designsystem/component/MifosTab.kt b/core/designsystem/src/commonMain/kotlin/org/mifospay/core/designsystem/component/MifosTab.kt
index 0aac6d9e6..cdd34932b 100644
--- a/core/designsystem/src/commonMain/kotlin/org/mifospay/core/designsystem/component/MifosTab.kt
+++ b/core/designsystem/src/commonMain/kotlin/org/mifospay/core/designsystem/component/MifosTab.kt
@@ -9,12 +9,18 @@
*/
package org.mifospay.core.designsystem.component
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Tab
import androidx.compose.material3.Text
+import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
@Composable
fun MifosTab(
@@ -22,20 +28,20 @@ fun MifosTab(
selected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
- selectedContentColor: Color = MaterialTheme.colorScheme.onSurface,
- unselectedContentColor: Color = Color.LightGray,
+ selectedColor: Color = MaterialTheme.colorScheme.primary,
+ unselectedColor: Color = MaterialTheme.colorScheme.primaryContainer,
) {
Tab(
text = {
- Text(
- text = text,
- color = MaterialTheme.colorScheme.onSurface,
- )
+ Text(text = text)
},
selected = selected,
- modifier = modifier,
- selectedContentColor = selectedContentColor,
- unselectedContentColor = unselectedContentColor,
onClick = onClick,
+ selectedContentColor = contentColorFor(selectedColor),
+ unselectedContentColor = contentColorFor(unselectedColor),
+ modifier = modifier
+ .clip(RoundedCornerShape(25.dp))
+ .background(if (selected) selectedColor else unselectedColor)
+ .padding(horizontal = 20.dp),
)
}
diff --git a/core/ui/src/commonMain/kotlin/org/mifospay/core/ui/ScrollableTabRow.kt b/core/ui/src/commonMain/kotlin/org/mifospay/core/ui/ScrollableTabRow.kt
index b668cc682..467cc5720 100644
--- a/core/ui/src/commonMain/kotlin/org/mifospay/core/ui/ScrollableTabRow.kt
+++ b/core/ui/src/commonMain/kotlin/org/mifospay/core/ui/ScrollableTabRow.kt
@@ -9,7 +9,6 @@
*/
package org.mifospay.core.ui
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.material3.MaterialTheme
@@ -24,16 +23,15 @@ import kotlinx.coroutines.launch
import org.mifospay.core.designsystem.component.MifosTab
import org.mifospay.core.ui.utility.TabContent
-@OptIn(ExperimentalFoundationApi::class)
@Suppress("MultipleEmitters")
@Composable
fun MifosScrollableTabRow(
tabContents: List,
pagerState: PagerState,
modifier: Modifier = Modifier,
- containerColor: Color = MaterialTheme.colorScheme.surface,
- selectedContentColor: Color = MaterialTheme.colorScheme.onSurface,
- unselectedContentColor: Color = Color.LightGray,
+ containerColor: Color = MaterialTheme.colorScheme.primaryContainer,
+ selectedContentColor: Color = MaterialTheme.colorScheme.primary,
+ unselectedContentColor: Color = MaterialTheme.colorScheme.primaryContainer,
edgePadding: Dp = 8.dp,
) {
val scope = rememberCoroutineScope()
@@ -43,13 +41,15 @@ fun MifosScrollableTabRow(
containerColor = containerColor,
selectedTabIndex = pagerState.currentPage,
edgePadding = edgePadding,
+ indicator = {},
+ divider = {},
) {
tabContents.forEachIndexed { index, currentTab ->
MifosTab(
text = currentTab.tabName,
selected = pagerState.currentPage == index,
- selectedContentColor = selectedContentColor,
- unselectedContentColor = unselectedContentColor,
+ selectedColor = selectedContentColor,
+ unselectedColor = unselectedContentColor,
onClick = {
scope.launch {
pagerState.animateScrollToPage(index)
diff --git a/feature/history/src/commonMain/kotlin/org/mifospay/feature/history/HistoryScreen.kt b/feature/history/src/commonMain/kotlin/org/mifospay/feature/history/HistoryScreen.kt
index 3335634e5..03272c9ae 100644
--- a/feature/history/src/commonMain/kotlin/org/mifospay/feature/history/HistoryScreen.kt
+++ b/feature/history/src/commonMain/kotlin/org/mifospay/feature/history/HistoryScreen.kt
@@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -37,7 +38,7 @@ import org.mifospay.feature.history.components.HistoryScreenFilter
import org.mifospay.feature.history.components.TransactionList
@Composable
-internal fun HistoryScreen(
+fun HistoryScreen(
viewTransferDetail: (Long) -> Unit,
modifier: Modifier = Modifier,
viewModel: HistoryViewModel = koinViewModel(),
@@ -125,6 +126,7 @@ private fun HistoryScreenContent(
HistoryScreenFilter(
selectedTransactionType = selectedTransactionType,
onAction = onAction,
+ modifier = Modifier.padding(top = 8.dp),
)
TransactionList(
diff --git a/feature/history/src/commonMain/kotlin/org/mifospay/feature/history/HistoryViewModel.kt b/feature/history/src/commonMain/kotlin/org/mifospay/feature/history/HistoryViewModel.kt
index 1d295fc34..a6c7c4d6a 100644
--- a/feature/history/src/commonMain/kotlin/org/mifospay/feature/history/HistoryViewModel.kt
+++ b/feature/history/src/commonMain/kotlin/org/mifospay/feature/history/HistoryViewModel.kt
@@ -22,7 +22,7 @@ import org.mifospay.core.model.savingsaccount.Transaction
import org.mifospay.core.model.savingsaccount.TransactionType
import org.mifospay.core.ui.utils.BaseViewModel
-internal class HistoryViewModel(
+class HistoryViewModel(
private val preferencesRepository: UserPreferencesRepository,
private val repository: SelfServiceRepository,
) : BaseViewModel(
@@ -114,7 +114,7 @@ internal class HistoryViewModel(
}
}
-internal data class HistoryState(
+data class HistoryState(
val clientId: Long,
val viewState: ViewState,
val transactionType: TransactionType,
diff --git a/feature/payments/build.gradle.kts b/feature/payments/build.gradle.kts
index ae1b679f8..7bf0fb852 100644
--- a/feature/payments/build.gradle.kts
+++ b/feature/payments/build.gradle.kts
@@ -8,14 +8,25 @@
* See https://github.com/openMF/mobile-wallet/blob/master/LICENSE.md
*/
plugins {
- alias(libs.plugins.mifospay.android.feature)
- alias(libs.plugins.mifospay.android.library.compose)
+ alias(libs.plugins.mifospay.cmp.feature)
+ alias(libs.plugins.kotlin.parcelize)
}
android {
namespace = "org.mifospay.feature.payments"
}
-dependencies {
- implementation(libs.accompanist.pager)
+kotlin {
+ sourceSets {
+ commonMain.dependencies {
+ implementation(compose.ui)
+ implementation(compose.foundation)
+ implementation(compose.material3)
+ implementation(compose.components.resources)
+ implementation(compose.components.uiToolingPreview)
+
+ implementation(libs.koin.compose.viewmodel)
+ implementation(libs.koin.compose)
+ }
+ }
}
\ No newline at end of file
diff --git a/feature/payments/consumer-rules.pro b/feature/payments/consumer-rules.pro
deleted file mode 100644
index e69de29bb..000000000
diff --git a/feature/payments/proguard-rules.pro b/feature/payments/proguard-rules.pro
deleted file mode 100644
index 481bb4348..000000000
--- a/feature/payments/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/feature/payments/src/main/AndroidManifest.xml b/feature/payments/src/androidMain/AndroidManifest.xml
similarity index 100%
rename from feature/payments/src/main/AndroidManifest.xml
rename to feature/payments/src/androidMain/AndroidManifest.xml
diff --git a/feature/payments/src/commonMain/composeResources/drawable/baseline_content_copy.xml b/feature/payments/src/commonMain/composeResources/drawable/baseline_content_copy.xml
new file mode 100644
index 000000000..4887f60c1
--- /dev/null
+++ b/feature/payments/src/commonMain/composeResources/drawable/baseline_content_copy.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/feature/payments/src/main/res/values/strings.xml b/feature/payments/src/commonMain/composeResources/values/strings.xml
similarity index 85%
rename from feature/payments/src/main/res/values/strings.xml
rename to feature/payments/src/commonMain/composeResources/values/strings.xml
index 5eb8935d6..819d3ccf9 100644
--- a/feature/payments/src/main/res/values/strings.xml
+++ b/feature/payments/src/commonMain/composeResources/values/strings.xml
@@ -9,7 +9,7 @@
See https://github.com/openMF/mobile-wallet/blob/master/LICENSE.md
-->
- Virtual Payment Address (VPA)
+ Virtual Payment Address (VPA)
Mobile Number
Receive
Show code
diff --git a/feature/payments/src/main/kotlin/org/mifospay/feature/payments/PaymentNavigation.kt b/feature/payments/src/commonMain/kotlin/org/mifospay/feature/payments/PaymentNavigation.kt
similarity index 100%
rename from feature/payments/src/main/kotlin/org/mifospay/feature/payments/PaymentNavigation.kt
rename to feature/payments/src/commonMain/kotlin/org/mifospay/feature/payments/PaymentNavigation.kt
diff --git a/feature/payments/src/main/kotlin/org/mifospay/feature/payments/PaymentsScreen.kt b/feature/payments/src/commonMain/kotlin/org/mifospay/feature/payments/PaymentsScreen.kt
similarity index 82%
rename from feature/payments/src/main/kotlin/org/mifospay/feature/payments/PaymentsScreen.kt
rename to feature/payments/src/commonMain/kotlin/org/mifospay/feature/payments/PaymentsScreen.kt
index c8ff57c43..a260e3bb4 100644
--- a/feature/payments/src/main/kotlin/org/mifospay/feature/payments/PaymentsScreen.kt
+++ b/feature/payments/src/commonMain/kotlin/org/mifospay/feature/payments/PaymentsScreen.kt
@@ -11,10 +11,10 @@ package org.mifospay.feature.payments
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.tooling.preview.Preview
-import com.google.accompanist.pager.rememberPagerState
+import org.jetbrains.compose.ui.tooling.preview.Preview
import org.mifospay.core.ui.MifosScrollableTabRow
import org.mifospay.core.ui.utility.TabContent
@@ -34,9 +34,12 @@ private fun PaymentScreenContent(
tabContents: List,
modifier: Modifier = Modifier,
) {
- val pagerState = rememberPagerState(initialPage = 0)
+ val pagerState = rememberPagerState(pageCount = { tabContents.size })
- Column(modifier = modifier.fillMaxSize()) {
+ Column(
+ modifier = modifier
+ .fillMaxSize(),
+ ) {
MifosScrollableTabRow(
tabContents = tabContents,
pagerState = pagerState,
@@ -52,7 +55,7 @@ enum class PaymentsScreenContents {
INVOICES,
}
-@Preview(showBackground = true)
+@Preview
@Composable
private fun PaymentsScreenPreview() {
PaymentScreenContent(
diff --git a/feature/payments/src/commonMain/kotlin/org/mifospay/feature/payments/RequestScreen.kt b/feature/payments/src/commonMain/kotlin/org/mifospay/feature/payments/RequestScreen.kt
new file mode 100644
index 000000000..1cef5ab50
--- /dev/null
+++ b/feature/payments/src/commonMain/kotlin/org/mifospay/feature/payments/RequestScreen.kt
@@ -0,0 +1,179 @@
+/*
+ * 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.payments
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.FilledTonalIconButton
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalClipboardManager
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import mobile_wallet.feature.payments.generated.resources.Res
+import mobile_wallet.feature.payments.generated.resources.baseline_content_copy
+import mobile_wallet.feature.payments.generated.resources.feature_payments_mobile_number
+import mobile_wallet.feature.payments.generated.resources.feature_payments_receive
+import mobile_wallet.feature.payments.generated.resources.feature_payments_show_code
+import mobile_wallet.feature.payments.generated.resources.feature_payments_vpa
+import org.jetbrains.compose.resources.stringResource
+import org.jetbrains.compose.resources.vectorResource
+import org.jetbrains.compose.ui.tooling.preview.Preview
+import org.koin.compose.viewmodel.koinViewModel
+import org.mifospay.core.designsystem.icon.MifosIcons
+import org.mifospay.core.designsystem.theme.MifosTheme
+import org.mifospay.core.ui.utils.EventsEffect
+
+@Composable
+fun RequestScreen(
+ showQr: (String) -> Unit,
+ modifier: Modifier = Modifier,
+ viewModel: TransferViewModel = koinViewModel(),
+) {
+ val clipboard = LocalClipboardManager.current
+ val state by viewModel.stateFlow.collectAsStateWithLifecycle()
+
+ EventsEffect(viewModel) { event ->
+ when (event) {
+ is TransferEvent.OnShowQR -> showQr.invoke(event.externalId)
+
+ is TransferEvent.OnCopyTextToClipboard -> {
+ clipboard.setText(AnnotatedString(event.text))
+ }
+ }
+ }
+
+ RequestScreenContent(
+ state = state,
+ modifier = modifier,
+ onAction = remember(viewModel) {
+ { viewModel.trySendAction(it) }
+ },
+ )
+}
+
+@Composable
+private fun RequestScreenContent(
+ state: TransferState,
+ onAction: (TransferAction) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ modifier = modifier
+ .fillMaxSize()
+ .padding(20.dp),
+ verticalArrangement = Arrangement.spacedBy(12.dp),
+ ) {
+ Text(
+ modifier = Modifier.padding(top = 10.dp),
+ text = stringResource(Res.string.feature_payments_receive),
+ style = MaterialTheme.typography.titleMedium,
+ color = MaterialTheme.colorScheme.primary,
+ )
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth(),
+ verticalArrangement = Arrangement.spacedBy(12.dp),
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Column(
+ verticalArrangement = Arrangement.spacedBy(4.dp),
+ horizontalAlignment = Alignment.Start,
+ ) {
+ Text(text = stringResource(Res.string.feature_payments_vpa))
+ Text(
+ text = state.externalId,
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onSurface,
+ )
+ }
+
+ FilledTonalIconButton(
+ onClick = {
+ onAction(TransferAction.ShowQR(state.externalId))
+ },
+ ) {
+ Icon(
+ imageVector = MifosIcons.QrCode,
+ tint = MaterialTheme.colorScheme.onSurface,
+ contentDescription = stringResource(Res.string.feature_payments_show_code),
+ )
+ }
+ }
+
+ HorizontalDivider(
+ thickness = 1.dp,
+ color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.05f),
+ )
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Column(
+ modifier = Modifier,
+ verticalArrangement = Arrangement.spacedBy(4.dp),
+ ) {
+ Text(text = stringResource(Res.string.feature_payments_mobile_number))
+ Text(
+ text = state.mobileNo,
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onSurface,
+ )
+ }
+
+ FilledTonalIconButton(
+ onClick = {
+ onAction(TransferAction.CopyTextToClipboard(state.mobileNo))
+ },
+ ) {
+ Icon(
+ imageVector = vectorResource(Res.drawable.baseline_content_copy),
+ tint = MaterialTheme.colorScheme.onSurface,
+ contentDescription = "Copy Text",
+ )
+ }
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun RequestScreenPreview() {
+ MifosTheme {
+ RequestScreenContent(
+ state = TransferState(
+ mobileNo = "iisque",
+ externalId = "nonumes",
+ ),
+ onAction = {},
+ modifier = Modifier,
+ )
+ }
+}
diff --git a/feature/payments/src/commonMain/kotlin/org/mifospay/feature/payments/TransferViewModel.kt b/feature/payments/src/commonMain/kotlin/org/mifospay/feature/payments/TransferViewModel.kt
new file mode 100644
index 000000000..18f133b78
--- /dev/null
+++ b/feature/payments/src/commonMain/kotlin/org/mifospay/feature/payments/TransferViewModel.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.payments
+
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import org.mifospay.core.common.Parcelable
+import org.mifospay.core.common.Parcelize
+import org.mifospay.core.datastore.UserPreferencesRepository
+import org.mifospay.core.ui.utils.BaseViewModel
+
+private const val KEY = "TransferState"
+
+class TransferViewModel(
+ private val repository: UserPreferencesRepository,
+ savedStateHandle: SavedStateHandle,
+) : BaseViewModel(
+ initialState = savedStateHandle[KEY] ?: run {
+ val client = requireNotNull(repository.client.value)
+
+ TransferState(
+ mobileNo = client.mobileNo,
+ externalId = client.externalId,
+ )
+ },
+) {
+ init {
+ stateFlow
+ .onEach { savedStateHandle[KEY] = it }
+ .launchIn(viewModelScope)
+ }
+
+ override fun handleAction(action: TransferAction) {
+ when (action) {
+ is TransferAction.ShowQR -> {
+ sendEvent(TransferEvent.OnShowQR(action.externalId))
+ }
+
+ is TransferAction.CopyTextToClipboard -> {
+ sendEvent(TransferEvent.OnCopyTextToClipboard(action.text))
+ }
+ }
+ }
+}
+
+@Parcelize
+data class TransferState(
+ val mobileNo: String,
+ val externalId: String,
+) : Parcelable
+
+sealed interface TransferEvent {
+ data class OnCopyTextToClipboard(val text: String) : TransferEvent
+ data class OnShowQR(val externalId: String) : TransferEvent
+}
+
+sealed interface TransferAction {
+ data class CopyTextToClipboard(val text: String) : TransferAction
+ data class ShowQR(val externalId: String) : TransferAction
+}
diff --git a/feature/payments/src/main/kotlin/org/mifospay/feature/payments/di/PaymentsModule.kt b/feature/payments/src/commonMain/kotlin/org/mifospay/feature/payments/di/PaymentsModule.kt
similarity index 69%
rename from feature/payments/src/main/kotlin/org/mifospay/feature/payments/di/PaymentsModule.kt
rename to feature/payments/src/commonMain/kotlin/org/mifospay/feature/payments/di/PaymentsModule.kt
index 0abf8b38f..fd45ccaf8 100644
--- a/feature/payments/src/main/kotlin/org/mifospay/feature/payments/di/PaymentsModule.kt
+++ b/feature/payments/src/commonMain/kotlin/org/mifospay/feature/payments/di/PaymentsModule.kt
@@ -9,16 +9,10 @@
*/
package org.mifospay.feature.payments.di
-import org.koin.core.module.dsl.viewModel
+import org.koin.core.module.dsl.viewModelOf
import org.koin.dsl.module
import org.mifospay.feature.payments.TransferViewModel
val PaymentsModule = module {
- viewModel {
- TransferViewModel(
- mUsecaseHandler = get(),
- localRepository = get(),
- mFetchAccount = get(),
- )
- }
+ viewModelOf(::TransferViewModel)
}
diff --git a/feature/payments/src/main/kotlin/org/mifospay/feature/payments/RequestScreen.kt b/feature/payments/src/main/kotlin/org/mifospay/feature/payments/RequestScreen.kt
deleted file mode 100644
index 222a24c3c..000000000
--- a/feature/payments/src/main/kotlin/org/mifospay/feature/payments/RequestScreen.kt
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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.payments
-
-import androidx.annotation.VisibleForTesting
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import org.koin.androidx.compose.koinViewModel
-import org.mifospay.core.designsystem.icon.MifosIcons
-import org.mifospay.core.designsystem.theme.MifosTheme
-
-@Composable
-fun RequestScreen(
- showQr: (String) -> Unit,
- modifier: Modifier = Modifier,
- viewModel: TransferViewModel = koinViewModel(),
-) {
- val vpa by viewModel.vpa.collectAsState()
- val mobile by viewModel.mobile.collectAsState()
-
- RequestScreenContent(
- vpa = vpa,
- mobile = mobile,
- showQr = showQr,
- modifier = modifier,
- )
-}
-
-@Composable
-@VisibleForTesting
-internal fun RequestScreenContent(
- vpa: String,
- mobile: String,
- showQr: (String) -> Unit,
- modifier: Modifier = Modifier,
-) {
- Column(
- modifier = modifier
- .fillMaxSize(),
- ) {
- Text(
- modifier = Modifier.padding(start = 20.dp, top = 30.dp),
- text = stringResource(id = R.string.feature_payments_receive),
- style = MaterialTheme.typography.titleMedium,
- color = MaterialTheme.colorScheme.primary,
- )
-
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(start = 20.dp),
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Column(
- modifier = Modifier
- .fillMaxWidth()
- .padding(top = 20.dp)
- .weight(1f),
- ) {
- Column {
- Text(text = stringResource(id = R.string.feature_payments_virtual_payment_address_vpa))
- Text(
- text = vpa,
- style = MaterialTheme.typography.bodyMedium,
- color = MaterialTheme.colorScheme.onSurface,
- )
- }
-
- Column(modifier = Modifier.padding(top = 10.dp)) {
- Text(text = stringResource(id = R.string.feature_payments_mobile_number))
- Text(
- text = mobile,
- style = MaterialTheme.typography.bodyMedium,
- color = MaterialTheme.colorScheme.onSurface,
- )
- }
- }
-
- Column(
- modifier = Modifier
- .fillMaxWidth()
- .weight(1f),
- horizontalAlignment = Alignment.CenterHorizontally,
- ) {
- IconButton(
- onClick = { showQr(vpa) },
- ) {
- Icon(
- imageVector = MifosIcons.QrCode,
- tint = MaterialTheme.colorScheme.primary,
- contentDescription = stringResource(id = R.string.feature_payments_show_code),
- )
- }
-
- Text(
- text = stringResource(id = R.string.feature_payments_show_code),
- )
- }
- }
- }
-}
-
-@Preview(showBackground = true, showSystemUi = true)
-@Composable
-private fun RequestScreenPreview() {
- MifosTheme {
- RequestScreenContent(
- vpa = "1234567898",
- mobile = "9078563421",
- showQr = {},
- modifier = Modifier,
- )
- }
-}
diff --git a/feature/payments/src/main/kotlin/org/mifospay/feature/payments/TransferViewModel.kt b/feature/payments/src/main/kotlin/org/mifospay/feature/payments/TransferViewModel.kt
deleted file mode 100644
index 4fb2fb8e0..000000000
--- a/feature/payments/src/main/kotlin/org/mifospay/feature/payments/TransferViewModel.kt
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.payments
-
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.launch
-import org.mifospay.core.data.base.UseCase.UseCaseCallback
-import org.mifospay.core.data.base.UseCaseHandler
-import org.mifospay.core.data.domain.usecase.account.FetchAccount
-import org.mifospay.core.data.repository.local.LocalRepository
-
-class TransferViewModel(
- val mUsecaseHandler: UseCaseHandler,
- val localRepository: LocalRepository,
- val mFetchAccount: FetchAccount,
-) : ViewModel() {
-
- private val mVpa = MutableStateFlow("")
- val vpa: StateFlow = mVpa
-
- private val mMobile = MutableStateFlow("")
- val mobile: StateFlow = mMobile
-
- private var mTransferUiState = MutableStateFlow(TransferUiState.Loading)
- var transferUiState: StateFlow = mTransferUiState
-
- private var mUpdateSuccess = MutableStateFlow(false)
- var updateSuccess: StateFlow = mUpdateSuccess
-
- init {
- fetchVpa()
- fetchMobile()
- }
-
- private fun fetchVpa() {
- viewModelScope.launch {
- mVpa.value = localRepository.clientDetails.externalId.toString()
- }
- }
-
- private fun fetchMobile() {
- viewModelScope.launch {
- mMobile.value = localRepository.preferencesHelper.mobile.toString()
- }
- }
-
- fun checkSelfTransfer(externalId: String?): Boolean {
- return externalId == localRepository.clientDetails.externalId
- }
-
- fun checkBalanceAvailability(externalId: String, transferAmount: Double) {
- mUsecaseHandler.execute(
- mFetchAccount,
- FetchAccount.RequestValues(localRepository.clientDetails.clientId),
- object : UseCaseCallback {
- override fun onSuccess(response: FetchAccount.ResponseValue) {
- mTransferUiState.value = TransferUiState.Loading
- if (transferAmount > response.account.balance) {
- mUpdateSuccess.value = true
- } else {
- mTransferUiState.value =
- TransferUiState.ShowClientDetails(externalId, transferAmount)
- }
- }
-
- override fun onError(message: String) {
- mUpdateSuccess.value = false
- }
- },
- )
- }
-}
-
-sealed interface TransferUiState {
- data object Loading : TransferUiState
- data class ShowClientDetails(
- val externalId: String,
- val transferAmount: Double,
- ) : TransferUiState
-}
diff --git a/mifospay-android/dependencies/prodReleaseRuntimeClasspath.tree.txt b/mifospay-android/dependencies/prodReleaseRuntimeClasspath.tree.txt
index 042bd93d7..cb013779c 100644
--- a/mifospay-android/dependencies/prodReleaseRuntimeClasspath.tree.txt
+++ b/mifospay-android/dependencies/prodReleaseRuntimeClasspath.tree.txt
@@ -2130,6 +2130,37 @@
| | +--- org.jetbrains.compose.components:components-resources:1.7.0-rc01 (*)
| | +--- org.jetbrains.compose.components:components-ui-tooling-preview:1.7.0-rc01 (*)
| | \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.20 (*)
+| +--- project :feature:payments
+| | +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.6 (*)
+| | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.6 (*)
+| | +--- androidx.tracing:tracing-ktx:1.3.0-alpha02 (*)
+| | +--- io.insert-koin:koin-bom:4.0.0-RC2 (*)
+| | +--- io.insert-koin:koin-android:4.0.0-RC2 (*)
+| | +--- io.insert-koin:koin-androidx-compose:4.0.0-RC2 (*)
+| | +--- io.insert-koin:koin-androidx-navigation:4.0.0-RC2 (*)
+| | +--- io.insert-koin:koin-core-viewmodel:4.0.0-RC2 (*)
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| | +--- io.insert-koin:koin-core:4.0.0-RC2 (*)
+| | +--- io.insert-koin:koin-annotations:1.4.0-RC4 (*)
+| | +--- project :core:ui (*)
+| | +--- project :core:designsystem (*)
+| | +--- project :core:data (*)
+| | +--- io.insert-koin:koin-compose:1.2.0-Beta4 -> 4.0.0-RC2 (*)
+| | +--- io.insert-koin:koin-compose-viewmodel:1.2.0-Beta4 -> 4.0.0-RC2 (*)
+| | +--- org.jetbrains.compose.runtime:runtime:1.7.0-rc01 (*)
+| | +--- org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.8.2 (*)
+| | +--- org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.8.2 -> 2.8.3-rc01 (*)
+| | +--- org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.2 (*)
+| | +--- org.jetbrains.androidx.savedstate:savedstate:1.2.2 (*)
+| | +--- org.jetbrains.androidx.core:core-bundle:1.0.1 (*)
+| | +--- org.jetbrains.androidx.navigation:navigation-compose:2.8.0-alpha10 (*)
+| | +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.8 (*)
+| | +--- org.jetbrains.compose.ui:ui:1.7.0-rc01 (*)
+| | +--- org.jetbrains.compose.foundation:foundation:1.7.0-rc01 (*)
+| | +--- org.jetbrains.compose.material3:material3:1.7.0-rc01 (*)
+| | +--- org.jetbrains.compose.components:components-resources:1.7.0-rc01 (*)
+| | +--- org.jetbrains.compose.components:components-ui-tooling-preview:1.7.0-rc01 (*)
+| | \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.20 (*)
| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
| +--- io.insert-koin:koin-annotations:1.4.0-RC4 (*)
| +--- project :core:ui (*)
diff --git a/mifospay-android/dependencies/prodReleaseRuntimeClasspath.txt b/mifospay-android/dependencies/prodReleaseRuntimeClasspath.txt
index 8523f857b..46ee2bf2a 100644
--- a/mifospay-android/dependencies/prodReleaseRuntimeClasspath.txt
+++ b/mifospay-android/dependencies/prodReleaseRuntimeClasspath.txt
@@ -13,6 +13,7 @@
:feature:faq
:feature:history
:feature:home
+:feature:payments
:feature:profile
:feature:settings
:libs:country-code-picker
diff --git a/mifospay-shared/build.gradle.kts b/mifospay-shared/build.gradle.kts
index d6cb1cc33..033b0f8e1 100644
--- a/mifospay-shared/build.gradle.kts
+++ b/mifospay-shared/build.gradle.kts
@@ -42,6 +42,7 @@ kotlin {
api(projects.feature.editpassword)
api(projects.feature.profile)
api(projects.feature.history)
+ api(projects.feature.payments)
}
desktopMain.dependencies {
diff --git a/mifospay-shared/src/commonMain/kotlin/org/mifospay/shared/di/KoinModules.kt b/mifospay-shared/src/commonMain/kotlin/org/mifospay/shared/di/KoinModules.kt
index a546dc942..de11c05b3 100644
--- a/mifospay-shared/src/commonMain/kotlin/org/mifospay/shared/di/KoinModules.kt
+++ b/mifospay-shared/src/commonMain/kotlin/org/mifospay/shared/di/KoinModules.kt
@@ -26,6 +26,7 @@ import org.mifospay.feature.editpassword.di.EditPasswordModule
import org.mifospay.feature.faq.di.FaqModule
import org.mifospay.feature.history.di.HistoryModule
import org.mifospay.feature.home.di.HomeModule
+import org.mifospay.feature.payments.di.PaymentsModule
import org.mifospay.feature.profile.di.ProfileModule
import org.mifospay.feature.settings.di.SettingsModule
import org.mifospay.shared.MifosPayViewModel
@@ -58,6 +59,7 @@ object KoinModules {
EditPasswordModule,
ProfileModule,
HistoryModule,
+ PaymentsModule,
)
}
private val LibraryModule = module {
diff --git a/mifospay-shared/src/commonMain/kotlin/org/mifospay/shared/navigation/MifosNavHost.kt b/mifospay-shared/src/commonMain/kotlin/org/mifospay/shared/navigation/MifosNavHost.kt
index 976de9f08..9e4882f9e 100644
--- a/mifospay-shared/src/commonMain/kotlin/org/mifospay/shared/navigation/MifosNavHost.kt
+++ b/mifospay-shared/src/commonMain/kotlin/org/mifospay/shared/navigation/MifosNavHost.kt
@@ -12,10 +12,12 @@ package org.mifospay.shared.navigation
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.compose.NavHost
+import org.mifospay.core.ui.utility.TabContent
import org.mifospay.feature.editpassword.navigation.editPasswordScreen
import org.mifospay.feature.editpassword.navigation.navigateToEditPassword
import org.mifospay.feature.faq.navigation.faqScreen
import org.mifospay.feature.faq.navigation.navigateToFAQ
+import org.mifospay.feature.history.HistoryScreen
import org.mifospay.feature.history.navigation.historyNavigation
import org.mifospay.feature.history.navigation.navigateToSpecificTransaction
import org.mifospay.feature.history.navigation.navigateToTransactionDetail
@@ -23,6 +25,9 @@ import org.mifospay.feature.history.navigation.specificTransactionsScreen
import org.mifospay.feature.history.navigation.transactionDetailNavigation
import org.mifospay.feature.home.navigation.HOME_ROUTE
import org.mifospay.feature.home.navigation.homeScreen
+import org.mifospay.feature.payments.PaymentsScreenContents
+import org.mifospay.feature.payments.RequestScreen
+import org.mifospay.feature.payments.paymentsScreen
import org.mifospay.feature.profile.navigation.profileNavGraph
import org.mifospay.feature.settings.navigation.settingsScreen
import org.mifospay.shared.ui.MifosAppState
@@ -35,6 +40,23 @@ internal fun MifosNavHost(
) {
val navController = appState.navController
+ val paymentsTabContents = listOf(
+ TabContent(PaymentsScreenContents.SEND.name) {
+ },
+ TabContent(PaymentsScreenContents.REQUEST.name) {
+ RequestScreen(showQr = {})
+ },
+ TabContent(PaymentsScreenContents.HISTORY.name) {
+ HistoryScreen(
+ viewTransferDetail = navController::navigateToTransactionDetail,
+ )
+ },
+ TabContent(PaymentsScreenContents.SI.name) {
+ },
+ TabContent(PaymentsScreenContents.INVOICES.name) {
+ },
+ )
+
NavHost(
route = MifosNavGraph.MAIN_GRAPH,
startDestination = HOME_ROUTE,
@@ -75,6 +97,10 @@ internal fun MifosNavHost(
viewTransactionDetail = navController::navigateToTransactionDetail,
)
+ paymentsScreen(
+ tabContents = paymentsTabContents,
+ )
+
specificTransactionsScreen(
navigateBack = navController::navigateUp,
viewTransactionDetail = navController::navigateToTransactionDetail,
diff --git a/mifospay-shared/src/commonMain/kotlin/org/mifospay/shared/ui/MifosAppState.kt b/mifospay-shared/src/commonMain/kotlin/org/mifospay/shared/ui/MifosAppState.kt
index bed6ea94c..4206b894c 100644
--- a/mifospay-shared/src/commonMain/kotlin/org/mifospay/shared/ui/MifosAppState.kt
+++ b/mifospay-shared/src/commonMain/kotlin/org/mifospay/shared/ui/MifosAppState.kt
@@ -31,10 +31,10 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.datetime.TimeZone
import org.mifospay.core.data.util.NetworkMonitor
import org.mifospay.core.data.util.TimeZoneMonitor
-import org.mifospay.feature.history.navigation.HISTORY_ROUTE
-import org.mifospay.feature.history.navigation.navigateToHistory
import org.mifospay.feature.home.navigation.HOME_ROUTE
import org.mifospay.feature.home.navigation.navigateToHome
+import org.mifospay.feature.payments.PAYMENTS_ROUTE
+import org.mifospay.feature.payments.navigateToPayments
import org.mifospay.feature.profile.navigation.PROFILE_ROUTE
import org.mifospay.feature.profile.navigation.navigateToProfile
import org.mifospay.shared.utils.TopLevelDestination
@@ -80,7 +80,7 @@ internal class MifosAppState(
val currentTopLevelDestination: TopLevelDestination?
@Composable get() = when (currentDestination?.route) {
HOME_ROUTE -> TopLevelDestination.HOME
- HISTORY_ROUTE -> TopLevelDestination.PAYMENTS
+ PAYMENTS_ROUTE -> TopLevelDestination.PAYMENTS
// FINANCE_ROUTE -> TopLevelDestination.FINANCE
PROFILE_ROUTE -> TopLevelDestination.PROFILE
else -> null
@@ -127,7 +127,7 @@ internal class MifosAppState(
when (topLevelDestination) {
TopLevelDestination.HOME -> navController.navigateToHome(topLevelNavOptions)
- TopLevelDestination.PAYMENTS -> navController.navigateToHistory(topLevelNavOptions)
+ TopLevelDestination.PAYMENTS -> navController.navigateToPayments(topLevelNavOptions)
// TopLevelDestination.FINANCE -> navController.navigateToFinance(topLevelNavOptions)
TopLevelDestination.PROFILE -> navController.navigateToProfile(topLevelNavOptions)
else -> Unit