mirror of
https://github.com/openMF/mobile-wallet.git
synced 2026-02-06 13:56:52 +00:00
feat: Add support for separate main and interbank instance selection
- Add InstanceType enum (MAIN, INTERBANK) to ServerInstance model
- Update InstancesConfig with helper methods to filter by instance type
- Extend datastore to store both selectedInstance and selectedInterbankInstance
- Update InstanceConfigManager to handle both main and interbank instances
- Add DEFAULT_INTERBANK_INSTANCE with apis.flexcore.mx configuration
- Update BaseURL.interBankUrl to use dynamic configuration from InstanceConfigManager
- Update FirebaseInstanceConfigLoader with default instances for both types
- Redesign InstanceSelectorScreen to display main and interbank instances separately
- Update InstanceSelectorViewModel to handle selection of both instance types
- Users can now independently select main server and interbank server instances
Firebase Remote Config JSON format now supports:
{
"instances": [
{
"endpoint": "mifos-bank-2.mifos.community",
"protocol": "https://",
"path": "/fineract-provider/api/v1/",
"platformTenantId": "mifos-bank-2",
"label": "Production Main",
"type": "MAIN",
"isDefault": true
},
{
"endpoint": "apis.flexcore.mx",
"protocol": "https://",
"path": "/v1.0/vnext1/",
"platformTenantId": "mifos-bank-2",
"label": "Production Interbank",
"type": "INTERBANK",
"isDefault": true
}
]
}
This commit is contained in:
parent
98f0b318b9
commit
c309eb0d2e
@ -37,9 +37,11 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
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.model.instance.InstanceType
|
||||
import org.mifospay.core.model.instance.ServerInstance
|
||||
import template.core.base.designsystem.theme.KptTheme
|
||||
|
||||
@ -85,11 +87,12 @@ fun InstanceSelectorScreen(
|
||||
|
||||
is InstanceSelectorUiState.Success -> {
|
||||
InstancesList(
|
||||
instances = state.instances,
|
||||
selectedInstance = state.selectedInstance,
|
||||
mainInstances = state.mainInstances,
|
||||
interbankInstances = state.interbankInstances,
|
||||
selectedMainInstance = state.selectedMainInstance,
|
||||
selectedInterbankInstance = state.selectedInterbankInstance,
|
||||
onInstanceSelected = { instance ->
|
||||
viewModel.selectInstance(instance)
|
||||
onDismiss()
|
||||
},
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
)
|
||||
@ -115,8 +118,10 @@ fun InstanceSelectorScreen(
|
||||
|
||||
@Composable
|
||||
private fun InstancesList(
|
||||
instances: List<ServerInstance>,
|
||||
selectedInstance: ServerInstance?,
|
||||
mainInstances: List<ServerInstance>,
|
||||
interbankInstances: List<ServerInstance>,
|
||||
selectedMainInstance: ServerInstance?,
|
||||
selectedInterbankInstance: ServerInstance?,
|
||||
onInstanceSelected: (ServerInstance) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
@ -126,10 +131,40 @@ private fun InstancesList(
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
items(instances) { instance ->
|
||||
item {
|
||||
Text(
|
||||
text = "Main Server Instances",
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
modifier = Modifier.padding(vertical = 8.dp),
|
||||
)
|
||||
}
|
||||
|
||||
items(mainInstances) { instance ->
|
||||
InstanceItem(
|
||||
instance = instance,
|
||||
isSelected = instance == selectedInstance,
|
||||
isSelected = instance == selectedMainInstance,
|
||||
onClick = { onInstanceSelected(instance) },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
HorizontalDivider()
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Text(
|
||||
text = "Interbank Server Instances",
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
modifier = Modifier.padding(vertical = 8.dp),
|
||||
)
|
||||
}
|
||||
|
||||
items(interbankInstances) { instance ->
|
||||
InstanceItem(
|
||||
instance = instance,
|
||||
isSelected = instance == selectedInterbankInstance,
|
||||
onClick = { onInstanceSelected(instance) },
|
||||
)
|
||||
}
|
||||
@ -194,6 +229,7 @@ private fun InstanceItemPreview() {
|
||||
path = "/fineract-provider/api/v1/",
|
||||
platformTenantId = "mifos-bank-2",
|
||||
label = "Production",
|
||||
type = InstanceType.MAIN,
|
||||
isDefault = true,
|
||||
),
|
||||
isSelected = true,
|
||||
|
||||
@ -17,6 +17,7 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import org.mifospay.core.common.DataState
|
||||
import org.mifospay.core.datastore.UserPreferencesRepository
|
||||
import org.mifospay.core.model.instance.InstanceType
|
||||
import org.mifospay.core.model.instance.InstancesConfig
|
||||
import org.mifospay.core.model.instance.ServerInstance
|
||||
import org.mifospay.core.network.config.InstanceConfigLoader
|
||||
@ -24,8 +25,10 @@ import org.mifospay.core.network.config.InstanceConfigLoader
|
||||
sealed interface InstanceSelectorUiState {
|
||||
data object Loading : InstanceSelectorUiState
|
||||
data class Success(
|
||||
val instances: List<ServerInstance>,
|
||||
val selectedInstance: ServerInstance?,
|
||||
val mainInstances: List<ServerInstance>,
|
||||
val interbankInstances: List<ServerInstance>,
|
||||
val selectedMainInstance: ServerInstance?,
|
||||
val selectedInterbankInstance: ServerInstance?,
|
||||
) : InstanceSelectorUiState
|
||||
data class Error(val message: String) : InstanceSelectorUiState
|
||||
}
|
||||
@ -48,10 +51,13 @@ class InstanceSelectorViewModel(
|
||||
when (val result = instanceConfigLoader.fetchInstancesConfig()) {
|
||||
is DataState.Success -> {
|
||||
val config = result.data
|
||||
val selectedInstance = userPreferencesRepository.selectedInstance.value
|
||||
val selectedMainInstance = userPreferencesRepository.selectedInstance.value
|
||||
val selectedInterbankInstance = userPreferencesRepository.selectedInterbankInstance.value
|
||||
_uiState.value = InstanceSelectorUiState.Success(
|
||||
instances = config.instances,
|
||||
selectedInstance = selectedInstance ?: config.getDefaultInstance(),
|
||||
mainInstances = config.getMainInstances(),
|
||||
interbankInstances = config.getInterbankInstances(),
|
||||
selectedMainInstance = selectedMainInstance ?: config.getDefaultInstance(),
|
||||
selectedInterbankInstance = selectedInterbankInstance ?: config.getDefaultInterbankInstance(),
|
||||
)
|
||||
}
|
||||
is DataState.Error -> {
|
||||
@ -68,10 +74,18 @@ class InstanceSelectorViewModel(
|
||||
|
||||
fun selectInstance(instance: ServerInstance) {
|
||||
viewModelScope.launch {
|
||||
userPreferencesRepository.updateSelectedInstance(instance)
|
||||
val currentState = _uiState.value
|
||||
if (currentState is InstanceSelectorUiState.Success) {
|
||||
_uiState.value = currentState.copy(selectedInstance = instance)
|
||||
when (instance.type) {
|
||||
InstanceType.MAIN -> {
|
||||
userPreferencesRepository.updateSelectedInstance(instance)
|
||||
_uiState.value = currentState.copy(selectedMainInstance = instance)
|
||||
}
|
||||
InstanceType.INTERBANK -> {
|
||||
userPreferencesRepository.updateSelectedInterbankInstance(instance)
|
||||
_uiState.value = currentState.copy(selectedInterbankInstance = instance)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ import org.mifospay.core.model.user.UserInfo
|
||||
private const val USER_INFO_KEY = "userInfo"
|
||||
private const val CLIENT_INFO_KEY = "clientInfo"
|
||||
private const val SELECTED_INSTANCE_KEY = "selectedInstance"
|
||||
private const val SELECTED_INTERBANK_INSTANCE_KEY = "selectedInterbankInstance"
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
class UserPreferencesDataSource(
|
||||
@ -79,6 +80,13 @@ class UserPreferencesDataSource(
|
||||
),
|
||||
)
|
||||
|
||||
private val _selectedInterbankInstance = MutableStateFlow(
|
||||
settings.decodeValueOrNull(
|
||||
key = SELECTED_INTERBANK_INSTANCE_KEY,
|
||||
serializer = ServerInstance.serializer(),
|
||||
),
|
||||
)
|
||||
|
||||
val token = _userInfo.map {
|
||||
it.base64EncodedAuthenticationKey
|
||||
}
|
||||
@ -92,6 +100,8 @@ class UserPreferencesDataSource(
|
||||
|
||||
val selectedInstance = _selectedInstance
|
||||
|
||||
val selectedInterbankInstance = _selectedInterbankInstance
|
||||
|
||||
suspend fun updateClientInfo(client: Client) {
|
||||
withContext(dispatcher) {
|
||||
settings.putClientPreference(client.toClientPreferences())
|
||||
@ -156,6 +166,13 @@ class UserPreferencesDataSource(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateSelectedInterbankInstance(instance: ServerInstance) {
|
||||
withContext(dispatcher) {
|
||||
settings.putSelectedInterbankInstance(instance)
|
||||
_selectedInterbankInstance.value = instance
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun clearInfo() {
|
||||
withContext(dispatcher) {
|
||||
settings.clear()
|
||||
@ -199,3 +216,11 @@ private fun Settings.putSelectedInstance(instance: ServerInstance) {
|
||||
value = instance,
|
||||
)
|
||||
}
|
||||
|
||||
private fun Settings.putSelectedInterbankInstance(instance: ServerInstance) {
|
||||
encodeValue(
|
||||
key = SELECTED_INTERBANK_INSTANCE_KEY,
|
||||
serializer = ServerInstance.serializer(),
|
||||
value = instance,
|
||||
)
|
||||
}
|
||||
|
||||
@ -35,6 +35,8 @@ interface UserPreferencesRepository {
|
||||
|
||||
val selectedInstance: StateFlow<ServerInstance?>
|
||||
|
||||
val selectedInterbankInstance: StateFlow<ServerInstance?>
|
||||
|
||||
suspend fun updateToken(token: String): DataState<Unit>
|
||||
|
||||
suspend fun updateUserInfo(user: UserInfo): DataState<Unit>
|
||||
@ -47,5 +49,7 @@ interface UserPreferencesRepository {
|
||||
|
||||
suspend fun updateSelectedInstance(instance: ServerInstance): DataState<Unit>
|
||||
|
||||
suspend fun updateSelectedInterbankInstance(instance: ServerInstance): DataState<Unit>
|
||||
|
||||
suspend fun logOut(): Unit
|
||||
}
|
||||
|
||||
@ -80,6 +80,13 @@ class UserPreferencesRepositoryImpl(
|
||||
started = SharingStarted.Eagerly,
|
||||
)
|
||||
|
||||
override val selectedInterbankInstance: StateFlow<ServerInstance?>
|
||||
get() = preferenceManager.selectedInterbankInstance.stateIn(
|
||||
scope = unconfinedScope,
|
||||
initialValue = null,
|
||||
started = SharingStarted.Eagerly,
|
||||
)
|
||||
|
||||
override suspend fun updateDefaultAccount(account: DefaultAccount): DataState<Unit> {
|
||||
return try {
|
||||
val result = preferenceManager.updateDefaultAccount(account)
|
||||
@ -99,6 +106,15 @@ class UserPreferencesRepositoryImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updateSelectedInterbankInstance(instance: ServerInstance): DataState<Unit> {
|
||||
return try {
|
||||
preferenceManager.updateSelectedInterbankInstance(instance)
|
||||
DataState.Success(Unit)
|
||||
} catch (e: Exception) {
|
||||
DataState.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updateToken(token: String): DataState<Unit> {
|
||||
return try {
|
||||
val result = preferenceManager.updateAuthToken(token)
|
||||
|
||||
@ -15,5 +15,15 @@ import kotlinx.serialization.Serializable
|
||||
data class InstancesConfig(
|
||||
val instances: List<ServerInstance> = emptyList(),
|
||||
) {
|
||||
fun getDefaultInstance(): ServerInstance? = instances.firstOrNull { it.isDefault }
|
||||
fun getDefaultInstance(): ServerInstance? =
|
||||
instances.firstOrNull { it.isDefault && it.type == InstanceType.MAIN }
|
||||
|
||||
fun getDefaultInterbankInstance(): ServerInstance? =
|
||||
instances.firstOrNull { it.isDefault && it.type == InstanceType.INTERBANK }
|
||||
|
||||
fun getMainInstances(): List<ServerInstance> =
|
||||
instances.filter { it.type == InstanceType.MAIN }
|
||||
|
||||
fun getInterbankInstances(): List<ServerInstance> =
|
||||
instances.filter { it.type == InstanceType.INTERBANK }
|
||||
}
|
||||
|
||||
@ -11,6 +11,11 @@ package org.mifospay.core.model.instance
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
enum class InstanceType {
|
||||
MAIN,
|
||||
INTERBANK,
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ServerInstance(
|
||||
val endpoint: String,
|
||||
@ -18,6 +23,7 @@ data class ServerInstance(
|
||||
val path: String,
|
||||
val platformTenantId: String,
|
||||
val label: String,
|
||||
val type: InstanceType = InstanceType.MAIN,
|
||||
val isDefault: Boolean = false,
|
||||
) {
|
||||
val fullUrl: String
|
||||
|
||||
@ -16,6 +16,7 @@ import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.mifospay.core.common.DataState
|
||||
import org.mifospay.core.model.instance.InstanceType
|
||||
import org.mifospay.core.model.instance.InstancesConfig
|
||||
import org.mifospay.core.model.instance.ServerInstance
|
||||
|
||||
@ -43,7 +44,17 @@ class FirebaseInstanceConfigLoader : InstanceConfigLoader {
|
||||
protocol = "https://",
|
||||
path = "/fineract-provider/api/v1/",
|
||||
platformTenantId = "mifos-bank-2",
|
||||
label = "Default Instance",
|
||||
label = "Default Main Instance",
|
||||
type = InstanceType.MAIN,
|
||||
isDefault = true,
|
||||
),
|
||||
ServerInstance(
|
||||
endpoint = "apis.flexcore.mx",
|
||||
protocol = "https://",
|
||||
path = "/v1.0/vnext1/",
|
||||
platformTenantId = "mifos-bank-2",
|
||||
label = "Default Interbank Instance",
|
||||
type = InstanceType.INTERBANK,
|
||||
isDefault = true,
|
||||
),
|
||||
),
|
||||
|
||||
@ -11,27 +11,45 @@ package org.mifospay.core.network.config
|
||||
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import org.mifospay.core.datastore.UserPreferencesRepository
|
||||
import org.mifospay.core.model.instance.InstanceType
|
||||
import org.mifospay.core.model.instance.ServerInstance
|
||||
|
||||
class InstanceConfigManager(
|
||||
private val userPreferencesRepository: UserPreferencesRepository,
|
||||
) {
|
||||
companion object {
|
||||
// Default instance configuration
|
||||
private val DEFAULT_INSTANCE = ServerInstance(
|
||||
// Default main instance configuration
|
||||
private val DEFAULT_MAIN_INSTANCE = ServerInstance(
|
||||
endpoint = "mifos-bank-2.mifos.community",
|
||||
protocol = "https://",
|
||||
path = "/fineract-provider/api/v1/",
|
||||
platformTenantId = "mifos-bank-2",
|
||||
label = "Default Instance",
|
||||
type = InstanceType.MAIN,
|
||||
isDefault = true,
|
||||
)
|
||||
|
||||
// Default interbank instance configuration
|
||||
private val DEFAULT_INTERBANK_INSTANCE = ServerInstance(
|
||||
endpoint = "apis.flexcore.mx",
|
||||
protocol = "https://",
|
||||
path = "/v1.0/vnext1/",
|
||||
platformTenantId = "mifos-bank-2",
|
||||
label = "Default Interbank",
|
||||
type = InstanceType.INTERBANK,
|
||||
isDefault = true,
|
||||
)
|
||||
}
|
||||
|
||||
val selectedInstance: StateFlow<ServerInstance?> = userPreferencesRepository.selectedInstance
|
||||
val selectedInterbankInstance: StateFlow<ServerInstance?> = userPreferencesRepository.selectedInterbankInstance
|
||||
|
||||
fun getCurrentInstance(): ServerInstance {
|
||||
return selectedInstance.value ?: DEFAULT_INSTANCE
|
||||
return selectedInstance.value ?: DEFAULT_MAIN_INSTANCE
|
||||
}
|
||||
|
||||
fun getCurrentInterbankInstance(): ServerInstance {
|
||||
return selectedInterbankInstance.value ?: DEFAULT_INTERBANK_INSTANCE
|
||||
}
|
||||
|
||||
fun getEndpoint(): String = getCurrentInstance().endpoint
|
||||
@ -48,4 +66,6 @@ class InstanceConfigManager(
|
||||
val instance = getCurrentInstance()
|
||||
return "${instance.protocol}${instance.endpoint}${instance.path}self/"
|
||||
}
|
||||
|
||||
fun getInterbankUrl(): String = getCurrentInterbankInstance().fullUrl
|
||||
}
|
||||
|
||||
@ -18,10 +18,6 @@ class BaseURL(
|
||||
const val HEADER_TENANT = "Fineract-Platform-TenantId"
|
||||
const val HEADER_AUTH = "Authorization"
|
||||
const val DEFAULT = "default"
|
||||
|
||||
const val API_ENDPOINT_INTERBANK = "apis.flexcore.mx"
|
||||
const val API_PATH_INTERBANK = "/v1.0/vnext1/"
|
||||
private const val PROTOCOL_HTTPS = "https://"
|
||||
}
|
||||
|
||||
val url: String
|
||||
@ -31,7 +27,7 @@ class BaseURL(
|
||||
get() = configManager.getSelfServiceUrl()
|
||||
|
||||
val interBankUrl: String
|
||||
get() = PROTOCOL_HTTPS + API_ENDPOINT_INTERBANK + API_PATH_INTERBANK
|
||||
get() = configManager.getInterbankUrl()
|
||||
|
||||
val fineractPlatformTenantId: String
|
||||
get() = configManager.getPlatformTenantId()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user