Skip to content

Commit f0ff23b

Browse files
authored
Feat: Migrated QR Module to KMP (#1810)
1 parent 809e0a3 commit f0ff23b

File tree

81 files changed

+2285
-1065
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+2285
-1065
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ that can be used as a dependency in any other wallet based project. It is develo
4343
| :feature:invoices | Done ||||||
4444
| :feature:kyc | Done ||||||
4545
| :feature:make-transfer | Done ||||||
46-
| :feature:merchants | Not started | | | | | |
46+
| :feature:merchants | Done | | | | | |
4747
| :feature:notification | Done ||||||
48-
| :feature:qr | Not started | | | | | |
48+
| :feature:qr | Done | | | | | |
4949
| :feature:receipt | Done ||||||
5050
| :feature:request-money | Done ||||||
5151
| :feature:saved-cards | Done ||||||

core/datastore/src/commonMain/kotlin/org/mifospay/core/datastore/UserPreferencesDataSource.kt

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ import kotlinx.coroutines.flow.MutableStateFlow
2121
import kotlinx.coroutines.flow.map
2222
import kotlinx.coroutines.withContext
2323
import kotlinx.serialization.ExperimentalSerializationApi
24+
import org.mifospay.core.datastore.UserPreferencesDataSource.Companion.DEFAULT_ACCOUNT
2425
import org.mifospay.core.datastore.proto.ClientPreferences
2526
import org.mifospay.core.datastore.proto.UserInfoPreferences
27+
import org.mifospay.core.model.account.DefaultAccount
2628
import org.mifospay.core.model.client.Client
2729
import org.mifospay.core.model.client.UpdatedClient
2830
import org.mifospay.core.model.user.UserInfo
@@ -57,6 +59,17 @@ class UserPreferencesDataSource(
5759
),
5860
)
5961

62+
private val _defaultAccount = MutableStateFlow(
63+
settings.decodeValue(
64+
key = DEFAULT_ACCOUNT,
65+
serializer = DefaultAccount.serializer(),
66+
defaultValue = settings.decodeValueOrNull(
67+
key = DEFAULT_ACCOUNT,
68+
serializer = DefaultAccount.serializer(),
69+
) ?: DefaultAccount.DEFAULT,
70+
),
71+
)
72+
6073
val token = _userInfo.map {
6174
it.base64EncodedAuthenticationKey
6275
}
@@ -66,6 +79,8 @@ class UserPreferencesDataSource(
6679

6780
val clientId = _clientInfo.map { it.id }
6881

82+
val defaultAccount = _defaultAccount.map { it.takeIf { it.accountId != 0L } }
83+
6984
suspend fun updateClientInfo(client: Client) {
7085
withContext(dispatcher) {
7186
settings.putClientPreference(client.toClientPreferences())
@@ -109,6 +124,12 @@ class UserPreferencesDataSource(
109124
}
110125
}
111126

127+
fun updateDefaultAccount(account: DefaultAccount) {
128+
settings.putDefaultAccount(account)
129+
130+
_defaultAccount.value = account
131+
}
132+
112133
fun updateAuthToken(token: String) {
113134
settings.putString(AUTH_TOKEN, token)
114135
}
@@ -117,14 +138,6 @@ class UserPreferencesDataSource(
117138
return settings.getString(AUTH_TOKEN, "").ifEmpty { null }
118139
}
119140

120-
fun getDefaultAccount(): Long? {
121-
return settings.getLong(DEFAULT_ACCOUNT, 0).takeIf { it != 0L }
122-
}
123-
124-
fun updateDefaultAccount(accountId: Long) {
125-
settings.putLong(DEFAULT_ACCOUNT, accountId)
126-
}
127-
128141
suspend fun clearInfo() {
129142
withContext(dispatcher) {
130143
settings.clear()
@@ -152,3 +165,11 @@ private fun Settings.putUserInfoPreference(preference: UserInfoPreferences) {
152165
value = preference,
153166
)
154167
}
168+
169+
private fun Settings.putDefaultAccount(account: DefaultAccount) {
170+
encodeValue(
171+
key = DEFAULT_ACCOUNT,
172+
serializer = DefaultAccount.serializer(),
173+
value = account,
174+
)
175+
}

core/datastore/src/commonMain/kotlin/org/mifospay/core/datastore/UserPreferencesRepository.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ package org.mifospay.core.datastore
1212
import kotlinx.coroutines.flow.Flow
1313
import kotlinx.coroutines.flow.StateFlow
1414
import org.mifospay.core.common.DataState
15+
import org.mifospay.core.model.account.DefaultAccount
1516
import org.mifospay.core.model.client.Client
1617
import org.mifospay.core.model.client.UpdatedClient
1718
import org.mifospay.core.model.user.UserInfo
@@ -27,7 +28,9 @@ interface UserPreferencesRepository {
2728

2829
val authToken: String?
2930

30-
val defaultAccount: Long?
31+
val defaultAccount: StateFlow<DefaultAccount?>
32+
33+
val defaultAccountId: StateFlow<Long?>
3134

3235
suspend fun updateToken(token: String): DataState<Unit>
3336

@@ -37,7 +40,7 @@ interface UserPreferencesRepository {
3740

3841
suspend fun updateClientProfile(client: UpdatedClient): DataState<Unit>
3942

40-
suspend fun logOut(): Unit
43+
suspend fun updateDefaultAccount(account: DefaultAccount): DataState<Unit>
4144

42-
suspend fun updateDefaultAccount(accountId: Long): DataState<Unit>
45+
suspend fun logOut(): Unit
4346
}

core/datastore/src/commonMain/kotlin/org/mifospay/core/datastore/UserPreferencesRepositoryImpl.kt

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ import kotlinx.coroutines.flow.Flow
1515
import kotlinx.coroutines.flow.SharingStarted
1616
import kotlinx.coroutines.flow.StateFlow
1717
import kotlinx.coroutines.flow.flowOn
18+
import kotlinx.coroutines.flow.map
1819
import kotlinx.coroutines.flow.stateIn
1920
import org.mifospay.core.common.DataState
21+
import org.mifospay.core.model.account.DefaultAccount
2022
import org.mifospay.core.model.client.Client
2123
import org.mifospay.core.model.client.UpdatedClient
2224
import org.mifospay.core.model.user.UserInfo
@@ -55,12 +57,24 @@ class UserPreferencesRepositoryImpl(
5557
override val authToken: String?
5658
get() = preferenceManager.getAuthToken()
5759

58-
override val defaultAccount: Long?
59-
get() = preferenceManager.getDefaultAccount()
60+
override val defaultAccount: StateFlow<DefaultAccount?>
61+
get() = preferenceManager.defaultAccount.stateIn(
62+
scope = unconfinedScope,
63+
initialValue = null,
64+
started = SharingStarted.Eagerly,
65+
)
66+
67+
override val defaultAccountId: StateFlow<Long?>
68+
get() = preferenceManager.defaultAccount
69+
.map { it?.accountId }.stateIn(
70+
scope = unconfinedScope,
71+
initialValue = null,
72+
started = SharingStarted.Eagerly,
73+
)
6074

61-
override suspend fun updateDefaultAccount(accountId: Long): DataState<Unit> {
75+
override suspend fun updateDefaultAccount(account: DefaultAccount): DataState<Unit> {
6276
return try {
63-
val result = preferenceManager.updateDefaultAccount(accountId)
77+
val result = preferenceManager.updateDefaultAccount(account)
6478

6579
DataState.Success(result)
6680
} catch (e: Exception) {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2024 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
7+
*
8+
* See https://github.com/openMF/mobile-wallet/blob/master/LICENSE.md
9+
*/
10+
package org.mifospay.core.model.account
11+
12+
import kotlinx.serialization.Serializable
13+
import org.mifospay.core.common.Parcelable
14+
import org.mifospay.core.common.Parcelize
15+
16+
@Serializable
17+
@Parcelize
18+
data class DefaultAccount(
19+
val accountId: Long,
20+
val accountNo: String,
21+
) : Parcelable {
22+
companion object {
23+
val DEFAULT = DefaultAccount(
24+
accountId = 0,
25+
accountNo = "",
26+
)
27+
}
28+
}

core/model/src/commonMain/kotlin/org/mifospay/core/model/utils/PaymentQrData.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ package org.mifospay.core.model.utils
1111

1212
import org.mifospay.core.common.Parcelable
1313
import org.mifospay.core.common.Parcelize
14+
import org.mifospay.core.model.search.AccountResult
1415

1516
/**
1617
* Parcelable data class representing the UPI payment request details
@@ -44,3 +45,17 @@ data class PaymentQrData(
4445
const val ACCOUNT_TYPE_ID: Long = 2
4546
}
4647
}
48+
49+
fun PaymentQrData.toAccount(): AccountResult {
50+
return AccountResult(
51+
entityId = accountId,
52+
entityAccountNo = accountNo,
53+
entityExternalId = "",
54+
entityName = "WALLET",
55+
entityType = "SAVING",
56+
parentId = clientId,
57+
parentName = clientName,
58+
parentType = "client",
59+
subEntityType = "depositAccountType.savingsDeposit",
60+
)
61+
}

feature/accounts/src/commonMain/kotlin/org/mifospay/feature/accounts/AccountViewModel.kt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import org.mifospay.core.common.DataState
2626
import org.mifospay.core.data.repository.SelfServiceRepository
2727
import org.mifospay.core.datastore.UserPreferencesRepository
2828
import org.mifospay.core.model.account.Account
29+
import org.mifospay.core.model.account.DefaultAccount
2930
import org.mifospay.core.model.beneficiary.Beneficiary
3031
import org.mifospay.core.ui.utils.BaseViewModel
3132
import org.mifospay.feature.accounts.AccountAction.Internal.BeneficiaryDeleteResultReceived
@@ -42,7 +43,7 @@ class AccountViewModel(
4243
) : BaseViewModel<AccountState, AccountEvent, AccountAction>(
4344
initialState = run {
4445
val clientId = requireNotNull(userRepository.clientId.value)
45-
val defaultAccount = userRepository.defaultAccount
46+
val defaultAccount = userRepository.defaultAccountId.value
4647

4748
AccountState(
4849
clientId = clientId,
@@ -125,7 +126,12 @@ class AccountViewModel(
125126

126127
private fun handleSetDefaultAccount(action: AccountAction.SetDefaultAccount) {
127128
viewModelScope.launch {
128-
userRepository.updateDefaultAccount(action.accountId)
129+
userRepository.updateDefaultAccount(
130+
DefaultAccount(
131+
accountId = action.accountId,
132+
accountNo = action.accountNo,
133+
),
134+
)
129135
}
130136

131137
mutableStateFlow.update { it.copy(defaultAccountId = action.accountId) }
@@ -225,7 +231,7 @@ sealed interface AccountAction {
225231
data object CreateSavingsAccount : AccountAction
226232
data class EditSavingsAccount(val accountId: Long) : AccountAction
227233
data class ViewAccountDetails(val accountId: Long) : AccountAction
228-
data class SetDefaultAccount(val accountId: Long) : AccountAction
234+
data class SetDefaultAccount(val accountId: Long, val accountNo: String) : AccountAction
229235

230236
data object DismissDialog : AccountAction
231237

feature/accounts/src/commonMain/kotlin/org/mifospay/feature/accounts/AccountsScreen.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,8 @@ internal fun AccountsScreenContent(
218218
onClickDeleteBeneficiary = {
219219
onAction(AccountAction.DeleteBeneficiary(it))
220220
},
221-
onAccountClicked = {
222-
onAction(AccountAction.SetDefaultAccount(it))
221+
onAccountClicked = { accId, accNo ->
222+
onAction(AccountAction.SetDefaultAccount(accId, accNo))
223223
},
224224
onClickEditAccount = {
225225
onAction(AccountAction.EditSavingsAccount(it))
@@ -235,7 +235,7 @@ private fun AccountsList(
235235
defaultAccountId: Long?,
236236
accounts: List<Account>,
237237
beneficiaryList: List<Beneficiary>,
238-
onAccountClicked: (Long) -> Unit,
238+
onAccountClicked: (Long, String) -> Unit,
239239
onAddTPTBeneficiary: () -> Unit,
240240
onClickEditBeneficiary: (Beneficiary) -> Unit,
241241
onClickDeleteBeneficiary: (Long) -> Unit,
@@ -337,7 +337,7 @@ private fun AccountItem(
337337
account: Account,
338338
isDefault: Boolean,
339339
modifier: Modifier = Modifier,
340-
onClick: (Long) -> Unit,
340+
onClick: (Long, String) -> Unit,
341341
onClickEditAccount: (Long) -> Unit,
342342
onClickViewAccount: (Long) -> Unit,
343343
) {
@@ -396,7 +396,7 @@ private fun AccountItem(
396396
}
397397
},
398398
onContentClick = if (account.status.active) {
399-
{ onClick(account.id) }
399+
{ onClick(account.id, account.number) }
400400
} else {
401401
null
402402
},

feature/home/src/commonMain/kotlin/org/mifospay/feature/home/HomeScreen.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,8 @@ private fun HomeScreenContent(
234234
onClick = {
235235
onAction(HomeAction.AccountDetailsClicked(it))
236236
},
237-
onMarkAsDefault = {
238-
onAction(HomeAction.MarkAsDefault(it))
237+
onMarkAsDefault = { accId, accNo ->
238+
onAction(HomeAction.MarkAsDefault(accId, accNo))
239239
},
240240
)
241241
}
@@ -292,7 +292,7 @@ private fun AccountList(
292292
accounts: List<Account>,
293293
defaultAccountId: Long?,
294294
modifier: Modifier = Modifier,
295-
onMarkAsDefault: (Long) -> Unit,
295+
onMarkAsDefault: (Long, String) -> Unit,
296296
onClick: (Long) -> Unit,
297297
) {
298298
val pagerState = rememberPagerState { accounts.size }
@@ -314,7 +314,7 @@ private fun AccountList(
314314
private fun AccountCard(
315315
account: Account,
316316
defaultAccountId: Long?,
317-
onMarkAsDefault: (Long) -> Unit,
317+
onMarkAsDefault: (Long, String) -> Unit,
318318
modifier: Modifier = Modifier,
319319
onClick: (Long) -> Unit,
320320
) {
@@ -375,7 +375,7 @@ private fun AccountCard(
375375
} else {
376376
CardDropdownBox(
377377
onClickDefault = {
378-
onMarkAsDefault(account.id)
378+
onMarkAsDefault(account.id, account.number)
379379
},
380380
)
381381
}

feature/home/src/commonMain/kotlin/org/mifospay/feature/home/HomeViewModel.kt

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import org.mifospay.core.common.Parcelize
2222
import org.mifospay.core.data.repository.SelfServiceRepository
2323
import org.mifospay.core.datastore.UserPreferencesRepository
2424
import org.mifospay.core.model.account.Account
25+
import org.mifospay.core.model.account.DefaultAccount
2526
import org.mifospay.core.model.client.Client
2627
import org.mifospay.core.model.savingsaccount.Transaction
2728
import org.mifospay.core.ui.utils.BaseViewModel
@@ -35,10 +36,11 @@ class HomeViewModel(
3536
) : BaseViewModel<HomeState, HomeEvent, HomeAction>(
3637
initialState = run {
3738
val client = requireNotNull(preferencesRepository.client.value)
38-
val defaultAccountId = preferencesRepository.defaultAccount
39+
val defaultAccount = preferencesRepository.defaultAccountId.value
40+
3941
HomeState(
4042
client = client,
41-
defaultAccountId = defaultAccountId,
43+
defaultAccountId = defaultAccount,
4244
)
4345
},
4446
) {
@@ -54,7 +56,9 @@ class HomeViewModel(
5456
is DataState.Success -> {
5557
if (state.defaultAccountId == null && result.data.accounts.isNotEmpty()) {
5658
val accountId = result.data.accounts.first().id
57-
preferencesRepository.updateDefaultAccount(accountId)
59+
val accountNo = result.data.accounts.first().number
60+
61+
sendAction(HomeAction.MarkAsDefault(accountId, accountNo))
5862
}
5963

6064
ViewState.Content(
@@ -113,7 +117,12 @@ class HomeViewModel(
113117

114118
is HomeAction.MarkAsDefault -> {
115119
viewModelScope.launch {
116-
val result = preferencesRepository.updateDefaultAccount(action.accountId)
120+
val result = preferencesRepository.updateDefaultAccount(
121+
DefaultAccount(
122+
accountId = action.accountId,
123+
accountNo = action.accountNo,
124+
),
125+
)
117126

118127
when (result) {
119128
is DataState.Loading -> {}
@@ -183,7 +192,7 @@ sealed interface HomeAction {
183192
data object OnDismissDialog : HomeAction
184193
data object OnNavigateBack : HomeAction
185194

186-
data class MarkAsDefault(val accountId: Long) : HomeAction
195+
data class MarkAsDefault(val accountId: Long, val accountNo: String) : HomeAction
187196
data class AccountDetailsClicked(val accountId: Long) : HomeAction
188197
data class TransactionClicked(val accountId: Long, val transactionId: Long) : HomeAction
189198
}

0 commit comments

Comments
 (0)