Skip to content
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a74cdc6
feat: implement app language selection and onboarding language screen
mena-rizkalla Feb 24, 2026
2e0e217
Merge branch 'development' of https://github.com/mena-rizkalla/mobile…
mena-rizkalla Feb 24, 2026
e4a9cf4
Merge branch 'development' into feat/install-language-selection
mena-rizkalla Feb 24, 2026
252625c
Merge branch 'development' into feat/install-language-selection
mena-rizkalla Feb 25, 2026
254da7b
Merge branch 'development' into feat/install-language-selection
mena-rizkalla Feb 26, 2026
be8ec22
feat: enhance onboarding language selection and persistence
mena-rizkalla Feb 26, 2026
cb369ac
style: cleanup and remove redundant comments
mena-rizkalla Feb 26, 2026
f3a1deb
fix: reorder language update and navigation in LanguageViewModel
mena-rizkalla Feb 26, 2026
754bdde
fix: improve language selection persistence and navigation flow
mena-rizkalla Feb 26, 2026
3e4db00
clean: remove onboarding language strings from settings module
mena-rizkalla Feb 26, 2026
f2fd5da
refactor: use DataState for user preference updates and improve error…
mena-rizkalla Feb 26, 2026
06f2c34
fix: update MainActivity configChanges to handle locale and layoutDir…
mena-rizkalla Feb 26, 2026
d9cb4de
clean: remove language selection feature from settings module
mena-rizkalla Feb 26, 2026
442b726
build: remove datastore and designsystem dependencies from prodReleas…
mena-rizkalla Feb 26, 2026
53c16c9
refactor: update dependencies and refine preference clearing logic
mena-rizkalla Feb 28, 2026
03e33cf
build: remove redundant core:model dependency from prod release class…
mena-rizkalla Feb 28, 2026
f53afbd
Merge branch 'development' into feat/install-language-selection
mena-rizkalla Feb 28, 2026
06c451d
Merge branch 'development' into feat/install-language-selection
mena-rizkalla Mar 2, 2026
4216099
Merge branch 'development' into feat/install-language-selection
mena-rizkalla Mar 4, 2026
1e3b14a
feat: implement MifosRadioButton and enhance language selection UI
mena-rizkalla Mar 5, 2026
56d81a5
Merge development and resolve conflicts
mena-rizkalla Mar 19, 2026
9bfa3ec
build: update and reorganize dependencies for passcode and onboarding…
mena-rizkalla Mar 19, 2026
0f35584
refactor: rename onboarding visibility preference and cleanup design …
mena-rizkalla Mar 21, 2026
d77c000
build: remove redundant passcode feature dependency tree entry
mena-rizkalla Mar 21, 2026
55768da
feat: handle language changes in desktop main
mena-rizkalla Mar 23, 2026
419545a
feat: implement locale persistence and application reload on language…
mena-rizkalla Mar 23, 2026
b5943f8
merge development and solve conflicts
mena-rizkalla Mar 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions cmp-android/dependencies/prodReleaseRuntimeClasspath.tree.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3240,6 +3240,40 @@
| | +--- org.jetbrains.compose.material3:material3:1.9.0-beta03 (*)
| | +--- org.jetbrains.compose.components:components-resources:1.9.3 (*)
| | \--- org.jetbrains.compose.components:components-ui-tooling-preview:1.9.3 (*)
| +--- project :feature:onboarding-language
| | +--- androidx.lifecycle:lifecycle-runtime-compose:2.9.2 -> 2.9.4 (*)
| | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.9.2 -> 2.9.4 (*)
| | +--- androidx.tracing:tracing-ktx:1.3.0 (*)
| | +--- io.insert-koin:koin-bom:4.1.1 (*)
| | +--- io.insert-koin:koin-android:4.1.1 (*)
| | +--- io.insert-koin:koin-androidx-compose:4.1.1 (*)
| | +--- io.insert-koin:koin-androidx-navigation:4.1.1 (*)
| | +--- io.insert-koin:koin-core-viewmodel:4.1.1 (*)
| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.2.21 (*)
| | +--- io.insert-koin:koin-core:4.1.1 (*)
| | +--- io.insert-koin:koin-annotations:2.1.0 (*)
| | +--- project :core:ui (*)
| | +--- project :core:designsystem (*)
| | +--- project :core:data (*)
| | +--- io.insert-koin:koin-compose:4.1.1 (*)
| | +--- io.insert-koin:koin-compose-viewmodel:4.1.1 (*)
| | +--- org.jetbrains.compose.runtime:runtime:1.9.3 (*)
| | +--- org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.6 (*)
| | +--- org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.6 (*)
| | +--- org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.6 (*)
| | +--- org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.6 (*)
| | +--- org.jetbrains.androidx.savedstate:savedstate:1.4.0 (*)
| | +--- org.jetbrains.androidx.core:core-bundle:1.0.1 (*)
| | +--- org.jetbrains.androidx.navigation:navigation-compose:2.9.1 (*)
| | +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.4.0 (*)
| | +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0 (*)
| | +--- org.jetbrains.compose.ui:ui:1.9.3 (*)
| | +--- org.jetbrains.compose.foundation:foundation:1.9.3 (*)
| | +--- org.jetbrains.compose.material3:material3:1.9.0-beta03 (*)
| | +--- org.jetbrains.compose.components:components-resources:1.9.3 (*)
| | +--- org.jetbrains.compose.components:components-ui-tooling-preview:1.9.3 (*)
| | +--- project :core:datastore (*)
| | \--- project :core-base:designsystem (*)
| \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.2.21 (*)
+--- project :core:data (*)
+--- project :core:ui (*)
Expand Down
1 change: 1 addition & 0 deletions cmp-android/dependencies/prodReleaseRuntimeClasspath.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
:feature:mpay-qr
:feature:mpay-qr-scan
:feature:notification
:feature:onboarding-language
:feature:payments
:feature:profile
:feature:receipt
Expand Down
1 change: 1 addition & 0 deletions cmp-android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
<activity
android:name=".MainActivity"
android:exported="true"
android:configChanges="locale|layoutDirection"
android:theme="@style/Theme.MifosSplash"
android:windowSoftInputMode="adjustResize">
<intent-filter>
Expand Down
32 changes: 29 additions & 3 deletions cmp-android/src/main/kotlin/org/mifospay/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
package org.mifospay

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.core.os.LocaleListCompat
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import androidx.lifecycle.Lifecycle
Expand All @@ -35,7 +37,7 @@ import org.mifospay.shared.MainUiState
import org.mifospay.shared.MifosPaySharedApp
import org.mifospay.shared.MifosPayViewModel

class MainActivity : ComponentActivity() {
class MainActivity : AppCompatActivity() {
private val networkMonitor: NetworkMonitor by inject()
private val timeZoneMonitor: TimeZoneMonitor by inject()
private val viewModel: MifosPayViewModel by viewModel()
Expand All @@ -57,7 +59,31 @@ class MainActivity : ComponentActivity() {
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState
.onEach { uiState = it }
.onEach { state ->
uiState = state
if (state is MainUiState.Success) {
val languageTag = state.language.localName
val currentAppLocales = AppCompatDelegate.getApplicationLocales()

val isRequestedDefault = languageTag.isNullOrBlank()
val isCurrentDefault = currentAppLocales.isEmpty

val shouldUpdate = if (isRequestedDefault) {
!isCurrentDefault
} else {
languageTag != currentAppLocales.toLanguageTags()
}

if (shouldUpdate) {
val appLocale: LocaleListCompat = if (isRequestedDefault) {
LocaleListCompat.getEmptyLocaleList()
} else {
LocaleListCompat.forLanguageTags(languageTag)
}
AppCompatDelegate.setApplicationLocales(appLocale)
}
}
}
.collect()
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}
Expand Down
1 change: 1 addition & 0 deletions cmp-shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ kotlin {
implementation(projects.feature.fastMpay)
implementation(projects.feature.merchants)
implementation(projects.feature.upiSetup)
implementation(projects.feature.onboardingLanguage)
}

desktopMain.dependencies {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.mifospay.core.data.util.NetworkMonitor
import org.mifospay.core.data.util.TimeZoneMonitor
import org.mifospay.core.designsystem.component.MifosDialogBox
import org.mifospay.core.designsystem.theme.MifosTheme
import org.mifospay.feature.onboarding.language.navigation.ONBOARDING_LANGUAGE_ROUTE
import org.mifospay.shared.MainUiState.Success
import org.mifospay.shared.navigation.MifosNavGraph.LOGIN_GRAPH
import org.mifospay.shared.navigation.MifosNavGraph.PASSCODE_GRAPH
Expand Down Expand Up @@ -79,7 +80,9 @@ private fun MifosPayApp(

val navDestination = when (uiState) {
is MainUiState.Loading -> LOGIN_GRAPH
is Success -> if ((uiState as Success).userData.authenticated) {
is Success -> if ((uiState as Success).showOnboarding) {
ONBOARDING_LANGUAGE_ROUTE
} else if ((uiState as Success).userData.authenticated) {
PASSCODE_GRAPH
} else {
LOGIN_GRAPH
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,24 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.mifospay.core.datastore.UserPreferencesRepository
import org.mifospay.core.model.LanguageConfig
import org.mifospay.core.model.user.UserInfo
import proto.org.mifos.library.passcode.data.PasscodeManager

class MifosPayViewModel(
private val userDataRepository: UserPreferencesRepository,
private val passcodeManager: PasscodeManager,
) : ViewModel() {
val uiState: StateFlow<MainUiState> = userDataRepository.userInfo.map {
MainUiState.Success(it)
val uiState: StateFlow<MainUiState> = combine(
userDataRepository.userInfo,
userDataRepository.language,
userDataRepository.showOnboarding,
) { userInfo, language, showOnboarding ->
MainUiState.Success(userInfo, language, showOnboarding)
}.stateIn(
scope = viewModelScope,
initialValue = MainUiState.Loading,
Expand All @@ -42,5 +47,9 @@ class MifosPayViewModel(

sealed interface MainUiState {
data object Loading : MainUiState
data class Success(val userData: UserInfo) : MainUiState
data class Success(
val userData: UserInfo,
val language: LanguageConfig,
val showOnboarding: Boolean,
) : MainUiState
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import org.mifospay.feature.merchants.di.MerchantsModule
import org.mifospay.feature.mpay.qr.di.MpayQrModule
import org.mifospay.feature.mpay.qr.scan.di.MpayQrScanModule
import org.mifospay.feature.notification.di.NotificationModule
import org.mifospay.feature.onboarding.language.di.onboardingLanguageModule
import org.mifospay.feature.payments.di.PaymentsModule
import org.mifospay.feature.profile.di.ProfileModule
import org.mifospay.feature.receipt.di.ReceiptModule
Expand Down Expand Up @@ -94,6 +95,7 @@ object KoinModules {
FastMpayModule,
MerchantsModule,
UpiSetupModule,
onboardingLanguageModule,
)
}
private val LibraryModule = module {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import org.mifospay.core.data.util.NetworkMonitor
import org.mifospay.core.data.util.TimeZoneMonitor
import org.mifospay.feature.onboarding.language.navigation.ONBOARDING_LANGUAGE_ROUTE
import org.mifospay.feature.onboarding.language.navigation.onboardingLanguageScreen
import org.mifospay.shared.instance.InstanceSelectorScreen
import org.mifospay.shared.ui.MifosApp

Expand All @@ -45,6 +47,16 @@ internal fun RootNavGraph(
onShowInstanceSelector = { showInstanceSelector = true },
)

onboardingLanguageScreen(
onNavigateToNext = {
navHostController.navigate(MifosNavGraph.LOGIN_GRAPH) {
popUpTo(ONBOARDING_LANGUAGE_ROUTE) {
inclusive = true
}
}
},
)

passcodeNavGraph(navHostController)

composable(MifosNavGraph.MAIN_GRAPH) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ interface KptThemeProvider {
val shapes: KptShapes
val spacing: KptSpacing
val elevation: KptElevation
val strokes: KptStrokes
}

@Stable
Expand Down Expand Up @@ -194,6 +195,14 @@ interface KptElevation {
val level5: Dp
}

@Stable
interface KptStrokes {
val dpPoint5: Dp
val thin: Dp
val dp2: Dp
val dp5: Dp
}

interface ComponentRenderer<T : KptComponent> {
@Composable
fun render(component: T)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import template.core.base.designsystem.core.KptColorScheme
import template.core.base.designsystem.core.KptElevation
import template.core.base.designsystem.core.KptShapes
import template.core.base.designsystem.core.KptSpacing
import template.core.base.designsystem.core.KptStrokes
import template.core.base.designsystem.core.KptThemeProvider
import template.core.base.designsystem.core.KptTypography

Expand Down Expand Up @@ -193,20 +194,30 @@ data class KptElevationImpl(
override val level5: Dp = 12.dp,
) : KptElevation

@Immutable
data class KptStrokesImpl(
Comment thread
mena-rizkalla marked this conversation as resolved.
Outdated
override val dpPoint5: Dp = 0.5.dp,
override val thin: Dp = 1.dp,
override val dp2: Dp = 2.dp,
override val dp5: Dp = 5.dp,
) : KptStrokes

@Immutable
data class KptThemeProviderImpl(
override val colors: KptColorScheme = KptColorSchemeImpl(),
override val typography: KptTypography = KptTypographyImpl(),
override val shapes: KptShapes = KptShapesImpl(),
override val spacing: KptSpacing = KptSpacingImpl(),
override val elevation: KptElevation = KptElevationImpl(),
override val strokes: KptStrokes = KptStrokesImpl(),
) : KptThemeProvider

val LocalKptColors = staticCompositionLocalOf<KptColorScheme> { KptColorSchemeImpl() }
val LocalKptTypography = staticCompositionLocalOf<KptTypography> { KptTypographyImpl() }
val LocalKptShapes = staticCompositionLocalOf<KptShapes> { KptShapesImpl() }
val LocalKptSpacing = staticCompositionLocalOf<KptSpacing> { KptSpacingImpl() }
val LocalKptElevation = staticCompositionLocalOf<KptElevation> { KptElevationImpl() }
val LocalKptStrokes = staticCompositionLocalOf<KptStrokes> { KptStrokesImpl() }

@ComponentDsl
class KptThemeBuilder {
Expand All @@ -215,6 +226,7 @@ class KptThemeBuilder {
private var shapes: KptShapes = KptShapesImpl()
private var spacing: KptSpacing = KptSpacingImpl()
private var elevation: KptElevation = KptElevationImpl()
private var strokes: KptStrokes = KptStrokesImpl()

fun colors(block: KptColorSchemeBuilder.() -> Unit) {
colors = KptColorSchemeBuilder().apply(block).build()
Expand All @@ -236,12 +248,17 @@ class KptThemeBuilder {
elevation = KptElevationBuilder().apply(block).build()
}

fun strokes(block: KptStrokesBuilder.() -> Unit) {
strokes = KptStrokesBuilder().apply(block).build()
}

fun build(): KptThemeProvider = KptThemeProviderImpl(
colors = colors,
typography = typography,
shapes = shapes,
spacing = spacing,
elevation = elevation,
strokes = strokes,
)
}

Expand Down Expand Up @@ -396,6 +413,21 @@ class KptElevationBuilder {
)
}

@ComponentDsl
class KptStrokesBuilder {
var dpPoint5: Dp = 0.5.dp
var thin: Dp = 1.dp
var dp2: Dp = 2.dp
var dp5: Dp = 5.dp

fun build(): KptStrokes = KptStrokesImpl(
dpPoint5 = dpPoint5,
thin = thin,
dp2 = dp2,
dp5 = dp5,
)
}

object KptTheme {
val colorScheme: KptColorScheme
@Composable get() = LocalKptColors.current
Expand All @@ -411,6 +443,9 @@ object KptTheme {

val elevation: KptElevation
@Composable get() = LocalKptElevation.current

val strokes: KptStrokes
@Composable get() = LocalKptStrokes.current
}

fun kptTheme(block: KptThemeBuilder.() -> Unit): KptThemeProvider {
Expand Down
Loading
Loading