Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9454de4
Login fixed
Aditya002500 Jan 16, 2026
e03bbdd
Login fixed
Aditya002500 Jan 16, 2026
6e75c4b
Merge branch 'openMF:development' into devx
Aditya002500 Jan 20, 2026
ff242d9
Revert "Merge branch 'openMF:development' into devx"
Aditya002500 Jan 20, 2026
830808a
Removed string
Aditya002500 Jan 20, 2026
927c989
Removed string
Aditya002500 Jan 20, 2026
4e9f7fd
Added user choices services in home screen
Aditya002500 Jan 22, 2026
a3cc592
Merge branch 'development' into devy
Aditya002500 Jan 29, 2026
3aca61f
Fixed icon
Aditya002500 Jan 29, 2026
d3242f6
Merge branch 'devy' of github.com:Aditya002500/mifos-mobile into devy
Aditya002500 Jan 29, 2026
df22afb
Fixed issues
Aditya002500 Jan 29, 2026
73b291d
Fixed icons
Aditya002500 Jan 29, 2026
338ba3d
Fixed icons
Aditya002500 Jan 29, 2026
fff5753
Removed unnecessary comments
Aditya002500 Jan 30, 2026
93ab3a7
Added coroutine
Aditya002500 Jan 30, 2026
c62785d
Merge branch 'openMF:development' into devy
Aditya002500 Feb 3, 2026
c6c480e
Merge branch 'development' into devy
Aditya002500 Feb 4, 2026
77ff835
Removed comments
Aditya002500 Feb 4, 2026
c545c79
Merge branch 'development' into devy
Aditya002500 Feb 4, 2026
ee2e802
Added nullable values acceptance
Aditya002500 Feb 4, 2026
7a25e44
Changed DesignToken
Aditya002500 Feb 6, 2026
8510393
Spotless
Aditya002500 Feb 6, 2026
1b542f8
Added recommendations
Aditya002500 Feb 6, 2026
3aaef4c
Fixed issues
Aditya002500 Feb 6, 2026
44b03b2
Spotless
Aditya002500 Feb 6, 2026
54e7b84
Fixed minor changes
Aditya002500 Feb 6, 2026
5281c03
Reverted to factory
Aditya002500 Feb 6, 2026
3f04306
Merge branch 'openMF:development' into devy
Aditya002500 Feb 10, 2026
8d9ec64
Reverted
Aditya002500 Feb 10, 2026
2acaa96
Bugs fixed
Aditya002500 Feb 10, 2026
01f370b
Viewmodel implementation
Aditya002500 Feb 12, 2026
07fe311
minor fixes
Aditya002500 Feb 12, 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
2 changes: 1 addition & 1 deletion cmp-android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

<application
android:name=".AndroidApp"
android:allowBackup="true"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import org.mifos.mobile.core.model.MifosThemeConfig
private const val USER_DATA = "userData"
private const val APP_SETTINGS = "appSettings"

@Suppress("TooManyFunctions")
class UserPreferencesDataSource(
private val settings: Settings,
private val dispatcher: CoroutineDispatcher,
Expand Down Expand Up @@ -160,6 +161,7 @@ class UserPreferencesDataSource(
suspend fun clearInfo() {
withContext(dispatcher) {
settings.putUserPreference(UserData.DEFAULT)
_userInfo.value = UserData.DEFAULT
val cleared = settings.getSettingsPreference().copy(
isAuthenticated = false,
)
Expand Down Expand Up @@ -234,8 +236,25 @@ class UserPreferencesDataSource(
_settingsInfo.value = newPreference
}

suspend fun setSelectedServices(selectedServices: Set<String>) =
withContext(dispatcher) {
val newPreference = settings.getSettingsPreference().copy(selectedServices = selectedServices)
settings.putSettingsPreference(newPreference)
_settingsInfo.value = newPreference
}

fun saveSelectedServicesDirectly(services: Set<String>) {
settings.putString(SELECTED_SERVICES_KEY, services.joinToString(","))
}

fun getSelectedServicesDirectly(): Set<String> {
val stored = settings.getString(SELECTED_SERVICES_KEY, "")
return if (stored.isEmpty()) emptySet() else stored.split(",").toSet()
}

companion object {
private const val PROFILE_IMAGE = "preferences_profile_image"
private const val SELECTED_SERVICES_KEY = "selected_services"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,10 @@ interface UserPreferencesRepository {

suspend fun setLanguage(language: LanguageConfig)

suspend fun setSelectedServices(selectedServices: Set<String>)

val selectedServices: Set<String>
fun saveSelectedServices(services: Set<String>)

suspend fun logOut(): Unit
}
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,17 @@ class UserPreferencesRepositoryImpl(
preferenceManager.setPasscode(passcode)
}

override suspend fun setSelectedServices(selectedServices: Set<String>) {
preferenceManager.setSelectedServices(selectedServices)
}

override val selectedServices: Set<String>
get() = preferenceManager.getSelectedServicesDirectly()

override fun saveSelectedServices(services: Set<String>) {
preferenceManager.saveSelectedServicesDirectly(services)
}

override suspend fun logOut() {
preferenceManager.clearInfo()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ data class AppSettings(
val showOnboarding: Boolean,
val firstTimeState: Boolean,
val timeBasedTheme: TimeBasedTheme,
val selectedServices: Set<String> = emptySet(),
) {
companion object {
val DEFAULT = AppSettings(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import fluent.ui.system.icons.filled.CaretUp
import fluent.ui.system.icons.filled.ChatBubblesQuestion
import fluent.ui.system.icons.filled.ChatHistory
import fluent.ui.system.icons.filled.ChatMultiple
import fluent.ui.system.icons.filled.CheckmarkCircle
import fluent.ui.system.icons.filled.ChevronRight
import fluent.ui.system.icons.filled.CoinMultiple
import fluent.ui.system.icons.filled.ContactCardRibbon
Expand Down Expand Up @@ -265,4 +266,8 @@ object MifosIcons {
val Attach = FluentIcons.Regular.Attach

val AddColor = FluentIcons.Colored.AddCircle

val GridApps = FluentIcons.Filled.Grid
val CheckCircle1 = FluentIcons.Filled.CheckmarkCircle
val Pencil = Icons.Filled.Edit
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,28 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.BottomSheetDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.collections.immutable.ImmutableList
import mifos_mobile.core.ui.generated.resources.ic_icon_logo_1
Expand Down Expand Up @@ -124,6 +129,28 @@ internal fun HomeContent(
onAction: (HomeAction) -> Unit,
modifier: Modifier = Modifier,
) {
// Repository for persistence
val preferencesRepository: org.mifos.mobile.core.datastore.UserPreferencesRepository =
org.koin.compose.koinInject()

// All available service routes
val allRoutes = remember { serviceCards.map { it.route }.toSet() }

// Load saved services
val savedServices = remember { preferencesRepository.selectedServices }
var selectedServices by remember {
mutableStateOf(if (savedServices.isEmpty()) allRoutes else savedServices)
}
var isEditMode by remember { mutableStateOf(false) }

fun toggleEditMode() {
if (isEditMode) {
// Save
preferencesRepository.saveSelectedServices(selectedServices)
}
isEditMode = !isEditMode
}

MifosElevatedScaffold(
modifier = modifier,
brandIcon = mifos_mobile.core.ui.generated.resources.Res.drawable.ic_icon_logo_1,
Expand Down Expand Up @@ -208,17 +235,43 @@ internal fun HomeContent(

Spacer(modifier = Modifier.height(DesignToken.spacing.extraLarge))

Text(
text = stringResource(Res.string.feature_home_services),
style = MifosTypography.titleMediumEmphasized,
color = KptTheme.colorScheme.onSurface,
)
// Services header with edit icon
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(KptTheme.spacing.sm),
) {
Text(
text = stringResource(Res.string.feature_home_services),
style = MifosTypography.titleMediumEmphasized,
color = KptTheme.colorScheme.onSurface,
)
Icon(
imageVector = if (isEditMode) MifosIcons.Edit else MifosIcons.GridApps,
contentDescription = "Edit services",
tint = KptTheme.colorScheme.primary,
modifier = Modifier
.size(16.dp)
.clickable { toggleEditMode() },
)
}

Spacer(modifier = Modifier.height(KptTheme.spacing.md))

ServiceBox(
items = state.items,
onAction = onAction,
isEditMode = isEditMode,
selectedServices = selectedServices,
onServiceClick = { route ->
if (isEditMode) {
selectedServices = if (selectedServices.contains(route)) {
selectedServices - route
} else {
selectedServices + route
}
} else {
onAction(HomeAction.OnNavigate(route))
}
},
)
}
}
Expand All @@ -231,12 +284,15 @@ internal fun HomeContent(
@Composable
internal fun ServiceBox(
items: ImmutableList<ServiceItem>,
onAction: (HomeAction) -> Unit,
isEditMode: Boolean,
selectedServices: Set<String>,
onServiceClick: (String) -> Unit,
modifier: Modifier = Modifier,
) {
val columnCount = 4
val spacing = DesignToken.spacing.medium
val rows = items.chunked(columnCount)
val displayItems = if (isEditMode) items else items.filter { selectedServices.contains(it.route) }
val rows = displayItems.chunked(columnCount)

Column(
modifier = modifier.fillMaxWidth(),
Expand All @@ -248,14 +304,17 @@ internal fun ServiceBox(
horizontalArrangement = Arrangement.spacedBy(spacing),
) {
rowItems.forEach { item ->
val isSelected = selectedServices.contains(item.route)
Box(
modifier = Modifier.weight(1f),
contentAlignment = Alignment.TopCenter,
) {
ServiceItemCard(
title = item.title,
icon = item.icon,
onClick = { onAction(HomeAction.OnNavigate(item.route)) },
isSelected = isSelected,
isEditMode = isEditMode,
onClick = { onServiceClick(item.route) },
)
}
}
Expand All @@ -273,30 +332,51 @@ internal fun ServiceItemCard(
icon: ImageVector,
onClick: () -> Unit,
modifier: Modifier = Modifier,
isSelected: Boolean = true,
isEditMode: Boolean = false,
) {
Column(
modifier = modifier
.padding(vertical = KptTheme.spacing.sm)
.clippedClickable(
onClick = onClick,
),
modifier = modifier.padding(vertical = KptTheme.spacing.sm),
verticalArrangement = Arrangement.spacedBy(KptTheme.spacing.sm),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Box(
modifier = Modifier
.border(
DesignToken.strokes.thin,
KptTheme.colorScheme.secondaryContainer,
KptTheme.shapes.medium,
)
.padding(DesignToken.padding.dp14),
modifier = Modifier.clickable { onClick() },
) {
Image(
modifier = Modifier
.border(
DesignToken.strokes.thin,
if (isEditMode && isSelected) {
KptTheme.colorScheme.primary
} else {
KptTheme.colorScheme.outlineVariant
},
KptTheme.shapes.medium,
)
.padding(DesignToken.padding.dp14),
imageVector = icon,
contentDescription = null,
colorFilter = ColorFilter.tint(KptTheme.colorScheme.tertiary),
colorFilter = ColorFilter.tint(
if (isEditMode && isSelected) {
KptTheme.colorScheme.primary
} else {
KptTheme.colorScheme.tertiary
},
),
)
// Checkmark icon for selected items in edit mode
if (isEditMode && isSelected) {
Icon(
imageVector = MifosIcons.CheckCircle1,
contentDescription = "Selected",
tint = KptTheme.colorScheme.primary,
modifier = Modifier
.align(Alignment.TopEnd)
.padding(4.dp)
.size(KptTheme.spacing.md),
)
}
}

Text(
Expand Down