Skip to content

Commit c840321

Browse files
feat : Activate migrated to CMP (#2388)
1 parent 072f419 commit c840321

File tree

16 files changed

+144
-146
lines changed

16 files changed

+144
-146
lines changed

cmp-navigation/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ kotlin {
3131
implementation(projects.core.network)
3232

3333
implementation(projects.feature.about)
34-
// implementation(projects.feature.activate)
34+
implementation(projects.feature.activate)
3535
implementation(projects.feature.auth)
3636
// implementation(projects.feature.center)
3737
// implementation(projects.feature.checkerInboxTask)

cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import com.mifos.core.datastore.di.PreferencesModule
1515
import com.mifos.core.domain.di.UseCaseModule
1616
import com.mifos.core.network.di.DataManagerModule
1717
import com.mifos.core.network.di.NetworkModule
18+
import com.mifos.feature.activate.di.ActivateModule
1819
import com.mifos.feature.auth.di.AuthModule
1920
import com.mifos.feature.note.di.NoteModule
2021
import com.mifos.room.di.DaoModule
@@ -46,7 +47,7 @@ object KoinModules {
4647
private val featureModules = module {
4748
includes(
4849
// AboutModule,
49-
// ActivateModule,
50+
ActivateModule,
5051
AuthModule,
5152
// CenterModule,
5253
// CheckerInboxTaskModule,

cmp-navigation/src/commonMain/kotlin/cmp/navigation/navigation/FeatureNavHost.kt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import androidx.compose.foundation.background
1313
import androidx.compose.foundation.layout.Arrangement
1414
import androidx.compose.foundation.layout.Column
1515
import androidx.compose.foundation.layout.fillMaxSize
16+
import androidx.compose.material3.Button
1617
import androidx.compose.material3.Text
1718
import androidx.compose.runtime.Composable
1819
import androidx.compose.ui.Alignment
@@ -23,6 +24,8 @@ import androidx.navigation.compose.NavHost
2324
import androidx.navigation.compose.composable
2425
import cmp.navigation.AppState
2526
import com.mifos.feature.about.navigation.aboutNavGraph
27+
import com.mifos.feature.activate.navigation.activateScreen
28+
import com.mifos.feature.activate.navigation.navigateToActivateScreen
2629
import com.mifos.feature.note.navigation.noteNavGraph
2730

2831
const val WELCOME_ROUTE = "home_screen"
@@ -39,27 +42,32 @@ internal fun FeatureNavHost(
3942
navController = appState.navController,
4043
modifier = modifier,
4144
) {
42-
homeScreen()
45+
homeScreen(onClick = { appState.navController.navigateToActivateScreen(0, "") })
4346

4447
aboutNavGraph(onBackPressed = appState.navController::popBackStack)
4548

4649
noteNavGraph(onBackPressed = appState.navController::popBackStack)
50+
51+
activateScreen(onBackPressed = appState.navController::popBackStack)
4752
}
4853
}
4954

50-
fun NavGraphBuilder.homeScreen() {
55+
fun NavGraphBuilder.homeScreen(onClick: () -> Unit) {
5156
composable(route = HomeDestinationsScreen.SearchScreen.route) {
52-
WelcomeScreen()
57+
WelcomeScreen(onClick)
5358
}
5459
}
5560

5661
@Composable
57-
fun WelcomeScreen() {
62+
fun WelcomeScreen(onClick: () -> Unit) {
5863
Column(
5964
modifier = Modifier.fillMaxSize().background(Color.White),
6065
verticalArrangement = Arrangement.Center,
6166
horizontalAlignment = Alignment.CenterHorizontally,
6267
) {
6368
Text(text = "Welcome to Mifos", color = Color.Black)
69+
Button(onClick = onClick) {
70+
Text("navigate")
71+
}
6472
}
6573
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright 2025 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/android-client/blob/master/LICENSE.md
9+
*/
10+
package com.mifos.core.common.utils
11+
12+
import kotlinx.datetime.Instant
13+
import kotlinx.datetime.TimeZone
14+
import kotlinx.datetime.toLocalDateTime
15+
16+
fun formatDate(millis: Long): String {
17+
val dateTime = Instant.fromEpochMilliseconds(millis).toLocalDateTime(TimeZone.currentSystemDefault())
18+
return "${dateTime.dayOfMonth} ${dateTime.month.name.lowercase().replaceFirstChar { it.uppercase() }} ${dateTime.year}"
19+
}

core/ui/src/commonMain/kotlin/com/mifos/core/ui/components/MifosAlertDialog.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ import com.mifos.core.ui.util.DevicePreview
2323
fun MifosAlertDialog(
2424
dialogTitle: String,
2525
dialogText: String,
26-
dismissText: String,
27-
confirmationText: String,
26+
dismissText: String = "Cancel",
27+
confirmationText: String = "Ok",
2828
onDismissRequest: () -> Unit,
2929
onConfirmation: () -> Unit,
3030
modifier: Modifier = Modifier,

feature/activate/build.gradle.kts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,20 @@
88
* See https://github.com/openMF/android-client/blob/master/LICENSE.md
99
*/
1010
plugins {
11-
alias(libs.plugins.mifos.android.feature)
12-
alias(libs.plugins.mifos.android.library.compose)
13-
alias(libs.plugins.mifos.android.library.jacoco)
11+
alias(libs.plugins.mifos.cmp.feature)
1412
}
1513

1614
android {
1715
namespace = "com.mifos.feature.activate"
1816
}
1917

20-
dependencies {
21-
22-
implementation(projects.core.domain)
23-
24-
testImplementation(libs.hilt.android.testing)
25-
18+
kotlin {
19+
sourceSets {
20+
commonMain.dependencies {
21+
implementation(compose.material3)
22+
implementation(compose.components.resources)
23+
implementation(compose.ui)
24+
implementation(projects.core.domain)
25+
}
26+
}
2627
}

feature/activate/src/androidTest/java/com/mifos/feature/activate/ExampleInstrumentedTest.kt

Lines changed: 0 additions & 31 deletions
This file was deleted.

feature/activate/src/main/java/com/mifos/feature/activate/ActivateScreen.kt renamed to feature/activate/src/commonMain/kotlin/com/mifos/feature/activate/ActivateScreen.kt

Lines changed: 65 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@
1111

1212
package com.mifos.feature.activate
1313

14-
import android.widget.Toast
14+
import androidclient.feature.activate.generated.resources.Res
15+
import androidclient.feature.activate.generated.resources.feature_activate
16+
import androidclient.feature.activate.generated.resources.feature_activate_activation_date
17+
import androidclient.feature.activate.generated.resources.feature_activate_cancel
18+
import androidclient.feature.activate.generated.resources.feature_activate_client
19+
import androidclient.feature.activate.generated.resources.feature_activate_failed_to_activate_client
20+
import androidclient.feature.activate.generated.resources.feature_activate_select
1521
import androidx.compose.foundation.layout.Column
1622
import androidx.compose.foundation.layout.PaddingValues
1723
import androidx.compose.foundation.layout.Spacer
@@ -23,36 +29,38 @@ import androidx.compose.material3.Button
2329
import androidx.compose.material3.DatePicker
2430
import androidx.compose.material3.DatePickerDialog
2531
import androidx.compose.material3.ExperimentalMaterial3Api
32+
import androidx.compose.material3.MaterialTheme
2633
import androidx.compose.material3.SelectableDates
2734
import androidx.compose.material3.SnackbarHostState
2835
import androidx.compose.material3.Text
2936
import androidx.compose.material3.TextButton
3037
import androidx.compose.material3.rememberDatePickerState
3138
import androidx.compose.runtime.Composable
39+
import androidx.compose.runtime.LaunchedEffect
3240
import androidx.compose.runtime.getValue
3341
import androidx.compose.runtime.mutableLongStateOf
3442
import androidx.compose.runtime.mutableStateOf
3543
import androidx.compose.runtime.remember
3644
import androidx.compose.runtime.saveable.rememberSaveable
3745
import androidx.compose.runtime.setValue
3846
import androidx.compose.ui.Modifier
39-
import androidx.compose.ui.platform.LocalContext
40-
import androidx.compose.ui.res.stringResource
41-
import androidx.compose.ui.tooling.preview.Preview
42-
import androidx.compose.ui.tooling.preview.PreviewParameter
43-
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
4447
import androidx.compose.ui.unit.dp
4548
import androidx.compose.ui.unit.sp
4649
import androidx.lifecycle.compose.collectAsStateWithLifecycle
4750
import com.mifos.core.common.utils.Constants
51+
import com.mifos.core.common.utils.formatDate
52+
import com.mifos.core.designsystem.component.MifosButton
4853
import com.mifos.core.designsystem.component.MifosCircularProgress
4954
import com.mifos.core.designsystem.component.MifosDatePickerTextField
5055
import com.mifos.core.designsystem.component.MifosScaffold
5156
import com.mifos.core.designsystem.component.MifosSweetError
5257
import com.mifos.core.model.objects.clients.ActivatePayload
53-
import org.koin.androidx.compose.koinViewModel
54-
import java.text.SimpleDateFormat
55-
import java.util.Locale
58+
import com.mifos.core.ui.components.MifosAlertDialog
59+
import com.mifos.core.ui.util.DevicePreview
60+
import kotlinx.datetime.Clock
61+
import org.jetbrains.compose.resources.getString
62+
import org.jetbrains.compose.resources.stringResource
63+
import org.koin.compose.viewmodel.koinViewModel
5664

5765
@Composable
5866
internal fun ActivateScreen(
@@ -63,6 +71,7 @@ internal fun ActivateScreen(
6371
val id by viewModel.id.collectAsStateWithLifecycle()
6472
val activateType by viewModel.activateType.collectAsStateWithLifecycle()
6573

74+
6675
ActivateScreen(
6776
state = state,
6877
onActivate = {
@@ -101,25 +110,22 @@ internal fun ActivateScreen(
101110
onBackPressed: () -> Unit,
102111
modifier: Modifier = Modifier,
103112
) {
104-
val snackbarHostState = remember { SnackbarHostState() }
105-
106113
MifosScaffold(
107-
title = stringResource(id = R.string.feature_activate),
114+
title = stringResource(Res.string.feature_activate),
108115
onBackPressed = onBackPressed,
109-
snackbarHostState = snackbarHostState,
110116
) { paddingValues ->
111117
Column(modifier = modifier.padding(paddingValues)) {
112118
when (state) {
113119
is ActivateUiState.ActivatedSuccessfully -> {
114-
Toast.makeText(
115-
LocalContext.current,
116-
stringResource(id = state.message),
117-
Toast.LENGTH_SHORT,
118-
).show()
119-
onBackPressed()
120+
MifosAlertDialog(
121+
dialogTitle = "Success",
122+
dialogText = stringResource(state.message),
123+
onConfirmation = onBackPressed,
124+
onDismissRequest = onBackPressed,
125+
)
120126
}
121127

122-
is ActivateUiState.Error -> MifosSweetError(message = stringResource(id = state.message)) {}
128+
is ActivateUiState.Error -> MifosSweetError(message = stringResource(state.message)) {}
123129

124130
is ActivateUiState.Loading -> MifosCircularProgress()
125131

@@ -136,12 +142,12 @@ private fun ActivateContent(
136142
) {
137143
Column(modifier = modifier) {
138144
var showDatePicker by rememberSaveable { mutableStateOf(false) }
139-
var activateDate by rememberSaveable { mutableLongStateOf(System.currentTimeMillis()) }
145+
var activateDate by rememberSaveable { mutableLongStateOf(Clock.System.now().toEpochMilliseconds()) }
140146
val datePickerState = rememberDatePickerState(
141147
initialSelectedDateMillis = activateDate,
142148
selectableDates = object : SelectableDates {
143149
override fun isSelectableDate(utcTimeMillis: Long): Boolean {
144-
return utcTimeMillis >= System.currentTimeMillis()
150+
return utcTimeMillis >= Clock.System.now().toEpochMilliseconds()
145151
}
146152
},
147153
)
@@ -159,33 +165,31 @@ private fun ActivateContent(
159165
activateDate = it
160166
}
161167
},
162-
) { Text(stringResource(id = R.string.feature_activate_select)) }
168+
) { Text(stringResource(Res.string.feature_activate_select)) }
163169
},
164170
dismissButton = {
165171
TextButton(
166172
onClick = {
167173
showDatePicker = false
168174
},
169-
) { Text(stringResource(id = R.string.feature_activate_cancel)) }
175+
) { Text(stringResource(Res.string.feature_activate_cancel)) }
170176
},
171177
) {
172178
DatePicker(state = datePickerState)
173179
}
174180
}
175181

176182
MifosDatePickerTextField(
177-
value = SimpleDateFormat("dd MMMM yyyy", Locale.getDefault()).format(
178-
activateDate,
179-
),
180-
label = stringResource(R.string.feature_activate_activation_date),
183+
value = formatDate(activateDate),
184+
label = stringResource(Res.string.feature_activate_activation_date),
181185
openDatePicker = {
182186
showDatePicker = true
183187
},
184188
)
185189

186190
Spacer(modifier = Modifier.height(16.dp))
187191

188-
Button(
192+
MifosButton(
189193
onClick = {
190194
onActivate(
191195
ActivatePayload(
@@ -199,30 +203,48 @@ private fun ActivateContent(
199203
.padding(start = 16.dp, end = 16.dp),
200204
contentPadding = PaddingValues(),
201205
) {
202-
Text(text = stringResource(id = R.string.feature_activate), fontSize = 16.sp)
206+
Text(text = stringResource(Res.string.feature_activate),
207+
style= MaterialTheme.typography.bodySmall)
203208
}
204209
}
205210
}
206211

207-
private class ActivateUiStateProvider : PreviewParameterProvider<ActivateUiState> {
212+
@DevicePreview
213+
@Composable
214+
private fun ActivateScreenPreviewInitial() {
215+
ActivateScreen(
216+
state = ActivateUiState.Initial,
217+
onActivate = {},
218+
onBackPressed = {},
219+
)
220+
}
208221

209-
override val values: Sequence<ActivateUiState>
210-
get() = sequenceOf(
211-
ActivateUiState.Loading,
212-
ActivateUiState.Error(R.string.feature_activate_failed_to_activate_client),
213-
ActivateUiState.ActivatedSuccessfully(R.string.feature_activate_client),
214-
ActivateUiState.Initial,
215-
)
222+
@DevicePreview
223+
@Composable
224+
private fun ActivateScreenPreviewLoading() {
225+
ActivateScreen(
226+
state = ActivateUiState.Loading,
227+
onActivate = {},
228+
onBackPressed = {},
229+
)
216230
}
217231

218-
@Preview(showBackground = true)
232+
@DevicePreview
219233
@Composable
220-
private fun ActivateScreenPreview(
221-
@PreviewParameter(ActivateUiStateProvider::class) state: ActivateUiState,
222-
) {
234+
private fun ActivateScreenPreviewActivatedSuccessfully() {
223235
ActivateScreen(
224-
state = state,
236+
state = ActivateUiState.ActivatedSuccessfully(Res.string.feature_activate_client),
225237
onActivate = {},
226238
onBackPressed = {},
227239
)
228240
}
241+
242+
@DevicePreview
243+
@Composable
244+
private fun ActivateScreenPreviewError() {
245+
ActivateScreen(
246+
state = ActivateUiState.Error(Res.string.feature_activate_failed_to_activate_client),
247+
onActivate = {},
248+
onBackPressed = {},
249+
)
250+
}

0 commit comments

Comments
 (0)