From 46023e5bfd6a271b9a11bed18d1d64bcd7c885be Mon Sep 17 00:00:00 2001 From: Aditya-gupta99 Date: Tue, 2 Jul 2024 20:47:38 +0530 Subject: [PATCH] refactor: refactor Activate fragment to compose --- .../java/com/mifos/core/data/di/DataModule.kt | 11 +- .../data/repository/ActivateRepository.kt | 30 +++ .../repository_imp/ActivateRepositoryImp.kt | 43 ++++ .../domain/use_cases/ActivateCenterUseCase.kt | 43 ++++ .../domain/use_cases/ActivateClientUseCase.kt | 43 ++++ .../domain/use_cases/ActivateGroupUseCase.kt | 42 ++++ feature/activate/.gitignore | 1 + feature/activate/build.gradle.kts | 23 ++ feature/activate/consumer-rules.pro | 0 feature/activate/proguard-rules.pro | 21 ++ .../activate/ExampleInstrumentedTest.kt | 24 ++ feature/activate/src/main/AndroidManifest.xml | 4 + .../mifos/feature/activate/ActivateScreen.kt | 224 ++++++++++++++++++ .../mifos/feature/activate/ActivateUiState.kt | 15 ++ .../feature/activate/ActivateViewModel.kt | 72 ++++++ .../activate/src/main/res/values/strings.xml | 14 ++ .../mifos/feature/activate/ExampleUnitTest.kt | 17 ++ mifosng-android/build.gradle.kts | 1 + .../online/activate/ActivateFragment.kt | 124 ++-------- settings.gradle.kts | 1 + 20 files changed, 652 insertions(+), 101 deletions(-) create mode 100644 core/data/src/main/java/com/mifos/core/data/repository/ActivateRepository.kt create mode 100644 core/data/src/main/java/com/mifos/core/data/repository_imp/ActivateRepositoryImp.kt create mode 100644 core/domain/src/main/java/com/mifos/core/domain/use_cases/ActivateCenterUseCase.kt create mode 100644 core/domain/src/main/java/com/mifos/core/domain/use_cases/ActivateClientUseCase.kt create mode 100644 core/domain/src/main/java/com/mifos/core/domain/use_cases/ActivateGroupUseCase.kt create mode 100644 feature/activate/.gitignore create mode 100644 feature/activate/build.gradle.kts create mode 100644 feature/activate/consumer-rules.pro create mode 100644 feature/activate/proguard-rules.pro create mode 100644 feature/activate/src/androidTest/java/com/mifos/feature/activate/ExampleInstrumentedTest.kt create mode 100644 feature/activate/src/main/AndroidManifest.xml create mode 100644 feature/activate/src/main/java/com/mifos/feature/activate/ActivateScreen.kt create mode 100644 feature/activate/src/main/java/com/mifos/feature/activate/ActivateUiState.kt create mode 100644 feature/activate/src/main/java/com/mifos/feature/activate/ActivateViewModel.kt create mode 100644 feature/activate/src/main/res/values/strings.xml create mode 100644 feature/activate/src/test/java/com/mifos/feature/activate/ExampleUnitTest.kt diff --git a/core/data/src/main/java/com/mifos/core/data/di/DataModule.kt b/core/data/src/main/java/com/mifos/core/data/di/DataModule.kt index b35813f0eb5..b45e2a64755 100644 --- a/core/data/src/main/java/com/mifos/core/data/di/DataModule.kt +++ b/core/data/src/main/java/com/mifos/core/data/di/DataModule.kt @@ -1,24 +1,26 @@ package com.mifos.core.data.di +import com.mifos.core.data.repository.ActivateRepository import com.mifos.core.data.repository.CenterDetailsRepository import com.mifos.core.data.repository.CenterListRepository import com.mifos.core.data.repository.CheckerInboxRepository import com.mifos.core.data.repository.CheckerInboxTasksRepository -import com.mifos.core.data.repository.ClientIdentifiersRepository import com.mifos.core.data.repository.ClientChargeRepository +import com.mifos.core.data.repository.ClientIdentifiersRepository import com.mifos.core.data.repository.CreateNewCenterRepository import com.mifos.core.data.repository.GroupDetailsRepository import com.mifos.core.data.repository.GroupsListRepository import com.mifos.core.data.repository.NewIndividualCollectionSheetRepository import com.mifos.core.data.repository.PathTrackingRepository import com.mifos.core.data.repository.ReportCategoryRepository +import com.mifos.core.data.repository_imp.ActivateRepositoryImp import com.mifos.core.data.repository_imp.CenterDetailsRepositoryImp import com.mifos.core.data.repository_imp.CenterListRepositoryImp import com.mifos.core.data.repository_imp.CheckerInboxRepositoryImp import com.mifos.core.data.repository_imp.CheckerInboxTasksRepositoryImp -import com.mifos.core.data.repository_imp.ClientIdentifiersRepositoryImp import com.mifos.core.data.repository_imp.ClientChargeRepositoryImp +import com.mifos.core.data.repository_imp.ClientIdentifiersRepositoryImp import com.mifos.core.data.repository_imp.CreateNewCenterRepositoryImp import com.mifos.core.data.repository_imp.GroupDetailsRepositoryImp import com.mifos.core.data.repository_imp.GroupsListRepositoryImpl @@ -65,10 +67,13 @@ abstract class DataModule { @Binds internal abstract fun bindClientChargeRepository(impl: ClientChargeRepositoryImp): ClientChargeRepository - + @Binds internal abstract fun bindCreateNewCenterRepository(impl: CreateNewCenterRepositoryImp): CreateNewCenterRepository @Binds internal abstract fun bindClientIdentifiersRepository(impl: ClientIdentifiersRepositoryImp): ClientIdentifiersRepository + + @Binds + internal abstract fun bindActivateRepository(impl: ActivateRepositoryImp): ActivateRepository } \ No newline at end of file diff --git a/core/data/src/main/java/com/mifos/core/data/repository/ActivateRepository.kt b/core/data/src/main/java/com/mifos/core/data/repository/ActivateRepository.kt new file mode 100644 index 00000000000..8c8ac928ad4 --- /dev/null +++ b/core/data/src/main/java/com/mifos/core/data/repository/ActivateRepository.kt @@ -0,0 +1,30 @@ +package com.mifos.core.data.repository + +import com.mifos.core.network.GenericResponse +import com.mifos.core.objects.client.ActivatePayload +import org.apache.fineract.client.models.PostCentersCenterIdResponse +import org.apache.fineract.client.models.PostClientsClientIdResponse +import rx.Observable + +/** + * Created by Aditya Gupta on 06/08/23. + */ + +interface ActivateRepository { + + fun activateClient( + clientId: Int, + clientActivate: ActivatePayload? + ): Observable + + fun activateCenter( + centerId: Int, + activatePayload: ActivatePayload? + ): Observable + + fun activateGroup( + groupId: Int, + activatePayload: ActivatePayload? + ): Observable + +} \ No newline at end of file diff --git a/core/data/src/main/java/com/mifos/core/data/repository_imp/ActivateRepositoryImp.kt b/core/data/src/main/java/com/mifos/core/data/repository_imp/ActivateRepositoryImp.kt new file mode 100644 index 00000000000..d208a65b3c9 --- /dev/null +++ b/core/data/src/main/java/com/mifos/core/data/repository_imp/ActivateRepositoryImp.kt @@ -0,0 +1,43 @@ +package com.mifos.core.data.repository_imp + +import com.mifos.core.data.repository.ActivateRepository +import com.mifos.core.network.GenericResponse +import com.mifos.core.network.datamanager.DataManagerCenter +import com.mifos.core.network.datamanager.DataManagerClient +import com.mifos.core.network.datamanager.DataManagerGroups +import com.mifos.core.objects.client.ActivatePayload +import org.apache.fineract.client.models.PostCentersCenterIdResponse +import org.apache.fineract.client.models.PostClientsClientIdResponse +import rx.Observable +import javax.inject.Inject + +/** + * Created by Aditya Gupta on 06/08/23. + */ +class ActivateRepositoryImp @Inject constructor( + private val dataManagerClient: DataManagerClient, + private val dataManagerCenter: DataManagerCenter, + private val dataManagerGroups: DataManagerGroups +) : ActivateRepository { + + override fun activateClient( + clientId: Int, + clientActivate: ActivatePayload? + ): Observable { + return dataManagerClient.activateClient(clientId, clientActivate) + } + + override fun activateCenter( + centerId: Int, + activatePayload: ActivatePayload? + ): Observable { + return dataManagerCenter.activateCenter(centerId, activatePayload) + } + + override fun activateGroup( + groupId: Int, + activatePayload: ActivatePayload? + ): Observable { + return dataManagerGroups.activateGroup(groupId, activatePayload) + } +} \ No newline at end of file diff --git a/core/domain/src/main/java/com/mifos/core/domain/use_cases/ActivateCenterUseCase.kt b/core/domain/src/main/java/com/mifos/core/domain/use_cases/ActivateCenterUseCase.kt new file mode 100644 index 00000000000..5b560fb8da0 --- /dev/null +++ b/core/domain/src/main/java/com/mifos/core/domain/use_cases/ActivateCenterUseCase.kt @@ -0,0 +1,43 @@ +package com.mifos.core.domain.use_cases + +import com.mifos.core.common.utils.Resource +import com.mifos.core.data.repository.ActivateRepository +import com.mifos.core.objects.client.ActivatePayload +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import org.apache.fineract.client.models.PostCentersCenterIdResponse +import rx.Subscriber +import rx.android.schedulers.AndroidSchedulers +import rx.schedulers.Schedulers +import javax.inject.Inject + +class ActivateCenterUseCase @Inject constructor(private val activateRepository: ActivateRepository) { + + suspend operator fun invoke( + centerId: Int, + centerPayload: ActivatePayload + ): Flow> = callbackFlow { + try { + + trySend(Resource.Loading()) + activateRepository.activateCenter(centerId, centerPayload) + .subscribeOn(AndroidSchedulers.mainThread()) + .observeOn(Schedulers.io()) + .subscribe(object : Subscriber() { + override fun onCompleted() {} + + override fun onError(exception: Throwable) { + trySend(Resource.Error(exception.message.toString())) + } + + override fun onNext(response: PostCentersCenterIdResponse) { + trySend(Resource.Success(response)) + } + }) + awaitClose { channel.close() } + } catch (exception: Exception) { + trySend(Resource.Error(exception.message.toString())) + } + } +} \ No newline at end of file diff --git a/core/domain/src/main/java/com/mifos/core/domain/use_cases/ActivateClientUseCase.kt b/core/domain/src/main/java/com/mifos/core/domain/use_cases/ActivateClientUseCase.kt new file mode 100644 index 00000000000..85e1773b21c --- /dev/null +++ b/core/domain/src/main/java/com/mifos/core/domain/use_cases/ActivateClientUseCase.kt @@ -0,0 +1,43 @@ +package com.mifos.core.domain.use_cases + +import com.mifos.core.common.utils.Resource +import com.mifos.core.data.repository.ActivateRepository +import com.mifos.core.objects.client.ActivatePayload +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import org.apache.fineract.client.models.PostClientsClientIdResponse +import rx.Subscriber +import rx.android.schedulers.AndroidSchedulers +import rx.schedulers.Schedulers +import javax.inject.Inject + +class ActivateClientUseCase @Inject constructor(private val activateRepository: ActivateRepository) { + + suspend operator fun invoke( + clientId: Int, + clientPayload: ActivatePayload + ): Flow> = callbackFlow { + try { + + trySend(Resource.Loading()) + activateRepository.activateClient(clientId, clientPayload) + .subscribeOn(AndroidSchedulers.mainThread()) + .observeOn(Schedulers.io()) + .subscribe(object : Subscriber() { + override fun onCompleted() {} + + override fun onError(exception: Throwable) { + trySend(Resource.Error(exception.message.toString())) + } + + override fun onNext(response: PostClientsClientIdResponse) { + trySend(Resource.Success(response)) + } + }) + awaitClose { channel.close() } + } catch (exception: Exception) { + trySend(Resource.Error(exception.message.toString())) + } + } +} \ No newline at end of file diff --git a/core/domain/src/main/java/com/mifos/core/domain/use_cases/ActivateGroupUseCase.kt b/core/domain/src/main/java/com/mifos/core/domain/use_cases/ActivateGroupUseCase.kt new file mode 100644 index 00000000000..cc3e2442d43 --- /dev/null +++ b/core/domain/src/main/java/com/mifos/core/domain/use_cases/ActivateGroupUseCase.kt @@ -0,0 +1,42 @@ +package com.mifos.core.domain.use_cases + +import com.mifos.core.common.utils.Resource +import com.mifos.core.data.repository.ActivateRepository +import com.mifos.core.network.GenericResponse +import com.mifos.core.objects.client.ActivatePayload +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import rx.Subscriber +import rx.android.schedulers.AndroidSchedulers +import rx.schedulers.Schedulers +import javax.inject.Inject + +class ActivateGroupUseCase @Inject constructor(private val activateRepository: ActivateRepository) { + + suspend operator fun invoke( + groupId: Int, + groupPayload: ActivatePayload + ): Flow> = callbackFlow { + try { + trySend(Resource.Loading()) + activateRepository.activateGroup(groupId, groupPayload) + .subscribeOn(AndroidSchedulers.mainThread()) + .observeOn(Schedulers.io()) + .subscribe(object : Subscriber() { + override fun onCompleted() {} + + override fun onError(exception: Throwable) { + trySend(Resource.Error(exception.message.toString())) + } + + override fun onNext(response: GenericResponse) { + trySend(Resource.Success(response)) + } + }) + awaitClose { channel.close() } + } catch (exception: Exception) { + trySend(Resource.Error(exception.message.toString())) + } + } +} \ No newline at end of file diff --git a/feature/activate/.gitignore b/feature/activate/.gitignore new file mode 100644 index 00000000000..42afabfd2ab --- /dev/null +++ b/feature/activate/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/activate/build.gradle.kts b/feature/activate/build.gradle.kts new file mode 100644 index 00000000000..fe0e2b5c1c3 --- /dev/null +++ b/feature/activate/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + alias(libs.plugins.mifos.android.feature) + alias(libs.plugins.mifos.android.library.compose) + alias(libs.plugins.mifos.android.library.jacoco) +} + +android { + namespace = "com.mifos.feature.activate" +} + +dependencies { + + implementation(projects.core.domain) + + //DBFlow dependencies + kapt(libs.dbflow.processor) + implementation(libs.dbflow) + kapt(libs.github.dbflow.processor) + testImplementation(libs.hilt.android.testing) + testImplementation(projects.core.testing) + + androidTestImplementation(projects.core.testing) +} \ No newline at end of file diff --git a/feature/activate/consumer-rules.pro b/feature/activate/consumer-rules.pro new file mode 100644 index 00000000000..e69de29bb2d diff --git a/feature/activate/proguard-rules.pro b/feature/activate/proguard-rules.pro new file mode 100644 index 00000000000..481bb434814 --- /dev/null +++ b/feature/activate/proguard-rules.pro @@ -0,0 +1,21 @@ +# 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/activate/src/androidTest/java/com/mifos/feature/activate/ExampleInstrumentedTest.kt b/feature/activate/src/androidTest/java/com/mifos/feature/activate/ExampleInstrumentedTest.kt new file mode 100644 index 00000000000..5a0e6a38da5 --- /dev/null +++ b/feature/activate/src/androidTest/java/com/mifos/feature/activate/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.mifos.feature.activate + +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("com.mifos.feature.activate.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/activate/src/main/AndroidManifest.xml b/feature/activate/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..a5918e68abc --- /dev/null +++ b/feature/activate/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/feature/activate/src/main/java/com/mifos/feature/activate/ActivateScreen.kt b/feature/activate/src/main/java/com/mifos/feature/activate/ActivateScreen.kt new file mode 100644 index 00000000000..6c9beba63f6 --- /dev/null +++ b/feature/activate/src/main/java/com/mifos/feature/activate/ActivateScreen.kt @@ -0,0 +1,224 @@ +@file:OptIn(ExperimentalMaterial3Api::class) + +package com.mifos.feature.activate + +import android.widget.Toast +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.DatePicker +import androidx.compose.material3.DatePickerDialog +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.SelectableDates +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.rememberDatePickerState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableLongStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.mifos.core.common.utils.Constants +import com.mifos.core.designsystem.component.MifosCircularProgress +import com.mifos.core.designsystem.component.MifosDatePickerTextField +import com.mifos.core.designsystem.component.MifosScaffold +import com.mifos.core.designsystem.component.MifosSweetError +import com.mifos.core.designsystem.icon.MifosIcons +import com.mifos.core.designsystem.theme.BluePrimary +import com.mifos.core.designsystem.theme.BluePrimaryDark +import com.mifos.core.objects.client.ActivatePayload +import java.text.SimpleDateFormat +import java.util.Locale + +@Composable +fun ActivateScreen( + id: Int, + activateType: String, + onBackPressed: () -> Unit +) { + + val viewModel: ActivateViewModel = hiltViewModel() + val state by viewModel.activateUiState.collectAsStateWithLifecycle() + + ActivateScreen( + state = state, + onActivate = { + when (activateType) { + Constants.ACTIVATE_CLIENT -> viewModel.activateClient( + clientId = id, + clientPayload = it + ) + + Constants.ACTIVATE_CENTER -> viewModel.activateCenter( + centerId = id, + centerPayload = it + ) + + Constants.ACTIVATE_GROUP -> viewModel.activateGroup( + groupId = id, + groupPayload = it + ) + + else -> {} + } + }, + onBackPressed = onBackPressed + ) +} + +@Composable +fun ActivateScreen( + state: ActivateUiState, + onActivate: (ActivatePayload) -> Unit, + onBackPressed: () -> Unit +) { + + val snackbarHostState = remember { SnackbarHostState() } + + MifosScaffold( + icon = MifosIcons.arrowBack, + title = stringResource(id = R.string.feature_activate), + onBackPressed = onBackPressed, + snackbarHostState = snackbarHostState + ) { paddingValues -> + Column(modifier = Modifier.padding(paddingValues)) { + when (state) { + is ActivateUiState.ActivatedSuccessfully -> { + Toast.makeText( + LocalContext.current, + stringResource(id = state.message), + Toast.LENGTH_SHORT + ).show() + onBackPressed() + } + + is ActivateUiState.Error -> MifosSweetError(message = stringResource(id = state.message)) {} + + is ActivateUiState.Loading -> MifosCircularProgress() + + is ActivateUiState.Initial -> ActivateContent(onActivate = onActivate) + } + } + } +} + +@Composable +fun ActivateContent( + onActivate: (ActivatePayload) -> Unit +) { + + var showDatePicker by rememberSaveable { mutableStateOf(false) } + var activateDate by rememberSaveable { mutableLongStateOf(System.currentTimeMillis()) } + val datePickerState = rememberDatePickerState( + initialSelectedDateMillis = activateDate, + selectableDates = object : SelectableDates { + override fun isSelectableDate(utcTimeMillis: Long): Boolean { + return utcTimeMillis >= System.currentTimeMillis() + } + } + ) + + if (showDatePicker) { + DatePickerDialog( + onDismissRequest = { + showDatePicker = false + }, + confirmButton = { + TextButton( + onClick = { + showDatePicker = false + datePickerState.selectedDateMillis?.let { + activateDate = it + } + } + ) { Text(stringResource(id = R.string.feature_activate_select)) } + }, + dismissButton = { + TextButton( + onClick = { + showDatePicker = false + } + ) { Text(stringResource(id = R.string.feature_activate_cancel)) } + } + ) + { + DatePicker(state = datePickerState) + } + } + + MifosDatePickerTextField( + value = SimpleDateFormat("dd MMMM yyyy", Locale.getDefault()).format( + activateDate + ), + label = R.string.feature_activate_activation_date, + openDatePicker = { + showDatePicker = true + } + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Button( + onClick = { + onActivate( + ActivatePayload( + activationDate = activateDate.toString() + ) + ) + }, + modifier = Modifier + .fillMaxWidth() + .heightIn(44.dp) + .padding(start = 16.dp, end = 16.dp), + contentPadding = PaddingValues(), + colors = ButtonDefaults.buttonColors( + containerColor = if (isSystemInDarkTheme()) BluePrimaryDark else BluePrimary + ) + ) { + Text(text = stringResource(id = R.string.feature_activate), fontSize = 16.sp) + } +} + + +class ActivateUiStateProvider : PreviewParameterProvider { + + override val values: Sequence + get() = sequenceOf( + ActivateUiState.Loading, + ActivateUiState.Error(R.string.feature_activate_failed_to_activate_client), + ActivateUiState.ActivatedSuccessfully(R.string.feature_activate_client), + ActivateUiState.Initial + ) +} + +@Preview(showBackground = true) +@Composable +private fun ActivateScreenPreview( + @PreviewParameter(ActivateUiStateProvider::class) state: ActivateUiState +) { + ActivateScreen( + state = state, + onActivate = {}, + onBackPressed = {} + ) +} \ No newline at end of file diff --git a/feature/activate/src/main/java/com/mifos/feature/activate/ActivateUiState.kt b/feature/activate/src/main/java/com/mifos/feature/activate/ActivateUiState.kt new file mode 100644 index 00000000000..81618cdd841 --- /dev/null +++ b/feature/activate/src/main/java/com/mifos/feature/activate/ActivateUiState.kt @@ -0,0 +1,15 @@ +package com.mifos.feature.activate + +/** + * Created by Aditya Gupta on 06/08/23. + */ +sealed class ActivateUiState { + + data object Initial : ActivateUiState() + + data object Loading : ActivateUiState() + + data class Error(val message: Int) : ActivateUiState() + + data class ActivatedSuccessfully(val message: Int) : ActivateUiState() +} diff --git a/feature/activate/src/main/java/com/mifos/feature/activate/ActivateViewModel.kt b/feature/activate/src/main/java/com/mifos/feature/activate/ActivateViewModel.kt new file mode 100644 index 00000000000..8e5e802987b --- /dev/null +++ b/feature/activate/src/main/java/com/mifos/feature/activate/ActivateViewModel.kt @@ -0,0 +1,72 @@ +package com.mifos.feature.activate + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.mifos.core.common.utils.Resource +import com.mifos.core.domain.use_cases.ActivateCenterUseCase +import com.mifos.core.domain.use_cases.ActivateClientUseCase +import com.mifos.core.domain.use_cases.ActivateGroupUseCase +import com.mifos.core.objects.client.ActivatePayload +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class ActivateViewModel @Inject constructor( + private val activateClientUseCase: ActivateClientUseCase, + private val activateCenterUseCase: ActivateCenterUseCase, + private val activateGroupUseCase: ActivateGroupUseCase +) : ViewModel() { + + private val _activateUiState = MutableStateFlow(ActivateUiState.Initial) + val activateUiState = _activateUiState.asStateFlow() + + fun activateClient(clientId: Int, clientPayload: ActivatePayload) = + viewModelScope.launch(Dispatchers.IO) { + activateClientUseCase(clientId, clientPayload).collect { result -> + when (result) { + is Resource.Error -> _activateUiState.value = + ActivateUiState.Error(R.string.feature_activate_failed_to_activate_client) + + is Resource.Loading -> _activateUiState.value = ActivateUiState.Loading + + is Resource.Success -> _activateUiState.value = + ActivateUiState.ActivatedSuccessfully(R.string.feature_activate_client) + } + + } + } + + fun activateCenter(centerId: Int, centerPayload: ActivatePayload) = + viewModelScope.launch(Dispatchers.IO) { + activateCenterUseCase(centerId, centerPayload).collect { result -> + when (result) { + is Resource.Error -> _activateUiState.value = + ActivateUiState.Error(R.string.feature_activate_failed_to_activate_center) + + is Resource.Loading -> _activateUiState.value = ActivateUiState.Loading + + is Resource.Success -> _activateUiState.value = + ActivateUiState.ActivatedSuccessfully(R.string.feature_activate_center) + } + } + } + + fun activateGroup(groupId: Int, groupPayload: ActivatePayload) = + viewModelScope.launch(Dispatchers.IO) { + activateGroupUseCase(groupId, groupPayload).collect { result -> + when (result) { + is Resource.Error -> _activateUiState.value = + ActivateUiState.Error(R.string.feature_activate_failed_to_activate_group) + + is Resource.Loading -> _activateUiState.value = ActivateUiState.Loading + + is Resource.Success -> _activateUiState.value = + ActivateUiState.ActivatedSuccessfully(R.string.feature_activate_group) + } + } + } +} \ No newline at end of file diff --git a/feature/activate/src/main/res/values/strings.xml b/feature/activate/src/main/res/values/strings.xml new file mode 100644 index 00000000000..22b671681c8 --- /dev/null +++ b/feature/activate/src/main/res/values/strings.xml @@ -0,0 +1,14 @@ + + + Activate + Activation date + Select + Cancel + Client activated successfully + Center activated successfully + Group activated successfully + Failed to activate client + Failed to activate center + Failed to activate group + + \ No newline at end of file diff --git a/feature/activate/src/test/java/com/mifos/feature/activate/ExampleUnitTest.kt b/feature/activate/src/test/java/com/mifos/feature/activate/ExampleUnitTest.kt new file mode 100644 index 00000000000..1b4dfd521b5 --- /dev/null +++ b/feature/activate/src/test/java/com/mifos/feature/activate/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.mifos.feature.activate + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/mifosng-android/build.gradle.kts b/mifosng-android/build.gradle.kts index f782405949a..5691186de4c 100644 --- a/mifosng-android/build.gradle.kts +++ b/mifosng-android/build.gradle.kts @@ -138,6 +138,7 @@ dependencies { implementation(projects.feature.about) implementation(projects.feature.report) implementation(projects.feature.pathTracking) + implementation(projects.feature.activate) implementation(projects.core.common) implementation(projects.core.ui) diff --git a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/activate/ActivateFragment.kt b/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/activate/ActivateFragment.kt index 22b2a282b14..b2b11e6c0a6 100644 --- a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/activate/ActivateFragment.kt +++ b/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/activate/ActivateFragment.kt @@ -4,46 +4,24 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Toast -import androidx.lifecycle.ViewModelProvider +import androidx.appcompat.app.AppCompatActivity +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs -import com.mifos.core.objects.client.ActivatePayload -import com.mifos.mifosxdroid.R -import com.mifos.mifosxdroid.core.MifosBaseFragment -import com.mifos.mifosxdroid.core.util.Toaster -import com.mifos.mifosxdroid.core.util.Toaster.show -import com.mifos.mifosxdroid.databinding.FragmentActivateClientBinding -import com.mifos.utils.Constants -import com.mifos.utils.DatePickerConstrainType -import com.mifos.utils.FragmentConstants -import com.mifos.utils.getDatePickerDialog -import com.mifos.utils.getTodayFormatted +import com.mifos.feature.activate.ActivateScreen import dagger.hilt.android.AndroidEntryPoint -import java.text.SimpleDateFormat -import java.time.Instant -import java.util.Locale /** * Created by Rajan Maurya on 09/02/17. */ @AndroidEntryPoint -class ActivateFragment : MifosBaseFragment() { +class ActivateFragment : Fragment() { - private lateinit var binding: FragmentActivateClientBinding private val arg: ActivateFragmentArgs by navArgs() - - private lateinit var viewModel: ActivateViewModel - private var id = 0 - private var activateType: String? = null - private var activationDate: Instant = Instant.now() - private val submissionDatePickerDialog by lazy { - getDatePickerDialog(activationDate, DatePickerConstrainType.ONLY_FUTURE_DAYS) { - val formattedDate = SimpleDateFormat("dd MM yyyy", Locale.getDefault()).format(it) - activationDate = Instant.ofEpochMilli(it) - binding.activateDateFieldContainer.editText?.setText(formattedDate) - } - } + private lateinit var activateType: String override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -54,79 +32,29 @@ class ActivateFragment : MifosBaseFragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { - binding = FragmentActivateClientBinding.inflate(inflater, container, false) - viewModel = ViewModelProvider(this)[ActivateViewModel::class.java] - showUserInterface() - - viewModel.activateUiState.observe(viewLifecycleOwner) { - when (it) { - is ActivateUiState.ShowActivatedSuccessfully -> { - showProgressbar(false) - showActivatedSuccessfully(it.message) - } - - is ActivateUiState.ShowError -> { - showProgressbar(false) - showError(it.message) - } - - is ActivateUiState.ShowProgressbar -> showProgressbar(it.state) - } - } - - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - binding.btnActivate.setOnClickListener { - onClickActivationButton() - } - - binding.activateDateFieldContainer.setEndIconOnClickListener { - submissionDatePickerDialog.show( - requireActivity().supportFragmentManager, - FragmentConstants.DFRAG_DATE_PICKER + return ComposeView(requireContext()).apply { + setViewCompositionStrategy( + ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed ) + setContent { + ActivateScreen( + id = id, + activateType = activateType, + onBackPressed = { + findNavController().popBackStack() + } + ) + } } } - private fun showUserInterface() { - setToolbarTitle(getString(R.string.activate)) - binding.activateDateFieldContainer.editText?.setText(getTodayFormatted()) - } - - private fun onClickActivationButton() { - val clientActivate = ActivatePayload(activationDate.toString()) - activate(clientActivate) - } - - fun activate(clientActivate: ActivatePayload?) { - when (activateType) { - Constants.ACTIVATE_CLIENT -> viewModel.activateClient(id, clientActivate) - Constants.ACTIVATE_CENTER -> viewModel.activateCenter(id, clientActivate) - Constants.ACTIVATE_GROUP -> viewModel.activateGroup(id, clientActivate) - else -> {} - } - } - - private fun showActivatedSuccessfully(message: Int) { - Toast.makeText( - activity, getString(message), Toast.LENGTH_SHORT - ).show() - requireActivity().supportFragmentManager.popBackStack() + override fun onResume() { + super.onResume() + (requireActivity() as AppCompatActivity).supportActionBar?.hide() } - private fun showError(errorMessage: String) { - show(binding.root, errorMessage, Toaster.INDEFINITE) - } - - private fun showProgressbar(show: Boolean) { - if (show) { - showMifosProgressDialog() - } else { - hideMifosProgressDialog() - } + override fun onStop() { + super.onStop() + (requireActivity() as AppCompatActivity).supportActionBar?.show() } } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 5fe05f750f7..1a4f617b6b2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -44,3 +44,4 @@ include(":feature:about") include(":feature:report") include(":feature:path-tracking") include(":feature:note") +include(":feature:activate")