-
Notifications
You must be signed in to change notification settings - Fork 671
remove redundant scaffold, back icon and screen name #2587
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: development
Are you sure you want to change the base?
remove redundant scaffold, back icon and screen name #2587
Conversation
📝 WalkthroughWalkthroughReplaces many Scaffold-based screen wrappers with content-centric composables (renaming *Scaffold → *Content), adds breadcrumb bars, moves padding to DesignToken values, flattens UI composition and dialog/bottom-sheet placement, and adjusts modifier propagation and button layout while preserving state-driven branches and actions. Changes
Sequence Diagram(s)(omitted) Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 20
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/savingsAccounts/SavingsAccountsScreen.kt (1)
241-253: Remove dead code statement.Line 243 (
DesignToken.padding) is a no-op statement that has no effect. This appears to be leftover code or an incomplete addition. Remove it or replace it with the intended spacing component (e.g.,Spacer).🧹 Proposed fix
IconButton( onClick = { onAction.invoke(SavingsAccountAction.ToggleSearch) }, ) { // add a cross icon when its active, talk with design team Icon( painter = painterResource(Res.drawable.search), contentDescription = null, ) } - DesignToken.padding + Spacer(modifier = Modifier.width(DesignToken.padding.small)) IconButton( onClick = { onAction.invoke(SavingsAccountAction.ToggleFilter) },Or simply remove the line if no spacing is needed between the buttons.
feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientEditDetails/ClientEditDetailsScreen.kt (1)
663-674: Fix staff selection mismatch after sorting options.
You sortstaffInOfficesfor display, but still index into the unsorted list when selecting, so the savedselectedStaffIdcan be wrong. Use the same sorted list for both options and selection mapping.🐛 Proposed fix
- MifosTextFieldDropdown( + val sortedStaff = staffInOffices.sortedBy { it.displayName } + MifosTextFieldDropdown( enabled = false, value = staff, onValueChanged = { staff = it }, onOptionSelected = { index, value -> staff = value - selectedStaffId = staffInOffices[index].id + selectedStaffId = sortedStaff.getOrNull(index)?.id }, label = stringResource(Res.string.feature_client_staff), - options = staffInOffices.sortedBy { it.displayName } - .map { it.displayName.toString() }, + options = sortedStaff.map { it.displayName.orEmpty() }, readOnly = true, )feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientDocuments/ClientDocumentsScreen.kt (1)
289-305: Add content descriptions for accessibility.The search and add icons are interactive but have
contentDescription = null. Screen reader users won't be able to identify these buttons. Consider adding meaningful descriptions.♿ Suggested fix
Icon( imageVector = MifosIcons.Search, - contentDescription = null, + contentDescription = stringResource(Res.string.search_content_description), // or a suitable string modifier = Modifier.clickable { onToggleSearch.invoke() }, ) Spacer(modifier = Modifier.width(DesignToken.spacing.largeIncreased)) Icon( imageVector = MifosIcons.Add, - contentDescription = null, + contentDescription = stringResource(Res.string.add_document_content_description), // or a suitable string modifier = Modifier.clickable { onAddDocument.invoke() }, )Note: You may need to add the appropriate string resources if they don't exist.
🤖 Fix all issues with AI agents
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationsScreen.kt`:
- Around line 113-152: The MifosSweetError currently takes its default
Modifier.fillMaxSize(), causing it to occupy the whole screen under the Column;
update the call to MifosSweetError to pass a Modifier.weight(1f).fillMaxWidth()
so it only fills the remaining vertical space after MifosBreadcrumbNavBar and
matches the column width; locate the MifosSweetError invocation in
ClientApplyNewApplicationsScreen (inside the else branch) and replace or augment
its modifier argument accordingly.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientClosure/ClientClosureScreen.kt`:
- Around line 166-178: The code reads
state.reasons[state.currentSelectedIndex].name which can crash if
currentSelectedIndex is out-of-bounds when reasons change; update the
MifosTextFieldDropdown usages to safely resolve the selected value and options
by first clamping or safely accessing the index (e.g., compute a safeIndex =
state.currentSelectedIndex.coerceIn(0, state.reasons.lastIndex) or use
state.reasons.getOrNull(state.currentSelectedIndex) and fallback to an empty
string) and use state.reasons.map { it.name } for options as before; ensure the
value parameter uses the safe lookup and that onOptionSelected still dispatches
onAction(ClientClosureAction.OptionChanged(index)).
- Around line 103-110: The DatePicker state created by rememberDatePickerState
(datePickerState) is only initialized with state.date and won't react to later
changes; add a LaunchedEffect keyed to state.date that assigns
datePickerState.selectedDateMillis = state.date (or null-handled) whenever
state.date changes so the UI stays in sync; use the existing datePickerState and
state.date references and ensure the LaunchedEffect runs on composition to
update the selectedDateMillis property.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientCollateralDetails/ClientCollateralDetailScreen.kt`:
- Around line 70-72: The empty-state padding is ignored because MifosEmptyCard
does not apply the passed Modifier; either wrap the MifosEmptyCard call in a
padded container (e.g., Box or Column with
Modifier.padding(DesignToken.padding.large)) where
ClientCollateralDetailsState.State.Empty is handled, or modify MifosEmptyCard
itself to accept and apply the incoming modifier to its root composable so the
horizontal padding passed from ClientCollateralDetailScreen takes effect.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientIdentifiersAddUpdate/ClientIdentitiesAddUpdateScreen.kt`:
- Around line 189-209: The header title logic in ClientIdentitiesAddUpdateScreen
(look for state.feature and the Text block) is wrong because it checks
documentKey outside the feature switch and has an unreachable VIEW_DOCUMENT
branch; replace the current conditional with a when(state.feature) to choose
titles per feature (e.g., VIEW_DOCUMENT, ADD_IDENTIFIER, ADD_UPDATE_DOCUMENT)
and only inspect state.documentKey inside the ADD_UPDATE_DOCUMENT branch to
decide between "add" vs "update" titles so the ADD_IDENTIFIER title remains
stable while typing.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientLoanAccounts/ClientLoanAccountsScreen.kt`:
- Around line 132-147: The loanBalance prop is incorrectly populated from
loan.amountPaid (duplicating amountPaid); change the value passed to
MifosActionsLoanListingComponent for loanBalance to use the model's actual
balance field (e.g., loan.balanceOutstanding or loan.outstandingBalance —
whichever your data model defines) instead of loan.amountPaid, keeping the same
null-safe formatting and prefixing with symbol (same pattern as
originalLoan/amountPaid).
- Around line 171-178: The MakeRepayment path currently uses a parameterless
Actions.MakeRepayment / ClientLoanAccountsAction.MakeRepayment which causes the
ViewModel to emit ClientLoanAccountsEvent.MakeRepayment using only the client
id; change ClientLoanAccountsAction.MakeRepayment from a parameterless data
object to carry the loan id (e.g., MakeRepayment(loanId: Int)), update the call
site in onActionClicked to pass loan.id ?: 0, update the ViewModel handling that
action to forward the loanId in ClientLoanAccountsEvent.MakeRepayment(loanId)
instead of the client id, and adjust the navigation handler to consume the
provided loanId; reference: Actions.MakeRepayment,
ClientLoanAccountsAction.MakeRepayment, onActionClicked, onAction,
ClientLoanAccountsEvent.MakeRepayment.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientPinpoint/PinpointClientScreen.kt`:
- Around line 237-244: In PinpointClientScreen, avoid launching coroutines or
invoking onAddressesChanged directly in the composition when handling
PinPointClientUiState.SuccessMessage; wrap the side-effect in a LaunchedEffect
keyed by state (e.g., LaunchedEffect(state)) and move the
snackbarHostState.showSnackbar call and onAddressesChanged() into that
LaunchedEffect so they run only once per state change. Also ensure the UI
actually displays the snackbar by adding a SnackbarHost wired to
snackbarHostState (for example via Scaffold(snackbarHost = {
SnackbarHost(snackbarHostState) })) in the composable tree.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientStaff/ClientStaffScreen.kt`:
- Around line 103-117: The code is indexing state.staffOptions with
state.currentSelectedIndex which can be -1 or stale; fix by performing a safe
lookup when setting MifosTextFieldDropdown.value (e.g., use
state.staffOptions.getOrNull(state.currentSelectedIndex)?.displayName ?:
fallbackLabel) and ensure the onOptionSelected still dispatches
ClientStaffAction.OptionChanged(index); also guard any other usages of
state.currentSelectedIndex to clamp or use getOrNull before accessing
staffOptions to avoid out-of-bounds crashes.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientUpdateDefaultAccount/ClientUpdateDefaultAccountScreen.kt`:
- Around line 161-163: Replace the hardcoded English message in the Text call
inside ClientUpdateDefaultAccountScreen (the else branch that currently shows
"No Savings Accounts found for this client") with a call to stringResource(...)
and create a matching string resource (e.g., update_default_account_no_accounts)
in your strings.xml/multiplatform resources; ensure you reference it via
stringResource(R.string.update_default_account_no_accounts) in the Text
composable so the UI uses localized text.
- Around line 109-118: The UI accesses
state.accounts[state.currentSelectedIndex] in MifosTextFieldDropdown and the
ViewModel uses state.currentSelectedIndex in updateDefaultAccount() and
getClientSavingsAccountId(); add bounds checks and index normalization: when
accounts change or are loaded, clamp currentSelectedIndex to
0..(accounts.size-1) or set to 0 if empty, use safe access (getOrNull) instead
of direct indexing in MifosTextFieldDropdown and in updateDefaultAccount(), and
update getClientSavingsAccountId() to reset the index (or return null) when no
matching account is found so a stale index cannot be used.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/createNewClient/CreateNewClientScreen.kt`:
- Around line 202-285: Snackbars are never shown because snackbarHostState is
not attached to any SnackbarHost in the UI tree; attach a SnackbarHost using the
existing snackbarHostState (e.g., add SnackbarHost(hostState =
snackbarHostState) as a child of the top-level Column in CreateNewClientScreen)
so that calls to snackbarHostState.showSnackbar(...) actually render; ensure the
SnackbarHost is placed where it can display (typically near the top-level
layout) and that necessary compose imports are present.
- Around line 233-268: The composition currently triggers side effects directly:
in CreateNewClientUiState.SetClientId it calls uploadImage(uiState.id) and
navigateBack.invoke(), and in CreateNewClientUiState.OnImageUploadSuccess and
ShowWaitingForCheckerApproval it calls navigateBack.invoke() outside the
coroutine; move these into LaunchedEffect blocks keyed on the specific ui state
(e.g., LaunchedEffect(uiState) or LaunchedEffect(uiState.id) for SetClientId) so
uploadImage(uiState.id) and snackbarHostState.showSnackbar(...) are invoked
inside LaunchedEffect and navigation (navigateBack.invoke()) happens after the
snackbar within the same LaunchedEffect coroutine, and remove any direct calls
to uploadImage or navigateBack from the composable body to ensure each side
effect runs only once per state change.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/FixedDepositAccountScreen.kt`:
- Around line 137-151: The root Column in FixedDepositAccountScreen currently
uses a hardcoded Modifier.fillMaxSize() so the passed-in modifier parameter is
only applied to the inner content Column; move or combine the incoming modifier
into the outer Column (the one that wraps MifosBreadcrumbNavBar and the
when(state.isLoading) branch) so that the caller's modifier (padding, insets,
test tags, etc.) applies to both the loading branch (MifosProgressIndicator) and
the content branch; update the outer Column to use
modifier.then(Modifier.fillMaxSize()) or modifier.fillMaxSize() while removing
the inner Column’s standalone modifier usage to avoid duplicate/contradictory
modifiers.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/recurringDepositAccount/RecurringDepositAccountScreen.kt`:
- Around line 137-140: The root Column in RecurringDepositAccountScreen ignores
the passed modifier parameter and uses Modifier.fillMaxSize() directly; update
the root Column to apply the incoming modifier combined with the size constraint
(e.g., modifier.fillMaxSize() or modifier.then(Modifier.fillMaxSize())) so
callers can style or position the composable using the modifier parameter.
- Around line 228-232: The ApproveAccount branch constructs a
RecurringDepositAccountAction.ApproveAccount but never dispatches it; update the
Actions.ApproveAccount case to call onAction(...) with the created action (i.e.,
create RecurringDepositAccountAction.ApproveAccount(recurringDeposit.accountNo
?: "") and immediately pass that instance into onAction) so the approve flow is
dispatched the same way as the ViewAccount handling; ensure you reference the
existing onAction callback and RecurringDepositAccountAction.ApproveAccount
symbol when making the change.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/savingsAccounts/SavingsAccountsScreen.kt`:
- Around line 97-109: The root composable in SavingsAccountsContent is not using
the passed-in modifier; apply the function parameter modifier to the outer
Column (the one currently using Modifier.fillMaxSize()) so callers can control
the component, and change the inner Column to use a local modifier (e.g.,
Modifier.fillMaxSize() or a composed localModifier) instead of the parameter;
update references to the outer Column and the inner Column in
SavingsAccountsContent accordingly.
In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/NewLoanAccountScreen.kt`:
- Around line 163-169: The Column is not consuming scaffold insets because the
MifosScaffold content lambda dropped the paddingValues; restore consuming
scaffold padding by accepting the padding parameter from MifosScaffold's content
lambda (e.g., paddingValues/innerPadding) and apply it to the root container
(the Column modifier) so the breadcrumb/stepper and transient UI (snackbar)
won't be overlapped; locate the MifosScaffold invocation and the Column in
NewLoanAccountScreen and add .padding(paddingValues) (or combine with
fillMaxSize via .then) to the Column's modifier.
In
`@feature/note/src/commonMain/kotlin/com/mifos/feature/note/notes/NoteScreen.kt`:
- Around line 190-194: The root Column in the NoteContent composable is ignoring
its caller-supplied modifier by using the companion Modifier; update the Column
to apply the function parameter instead (use the modifier parameter and chain
the paddings onto it, e.g. modifier = modifier.padding(...)) so any external
modifiers are preserved; locate the NoteContent function and replace Modifier
with the modifier parameter on the Column, chaining paddings via
modifier.padding(...) or modifier.then(...) as appropriate.
In
`@feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountv2/SavingsAccountScreen.kt`:
- Around line 146-148: The Column in SavingsAccountScreen is not applying the
scaffold's paddingValues from MifosScaffold, causing content to ignore scaffold
insets; update the composable lambda where MifosScaffold provides paddingValues
(the content lambda in SavingsAccountScreen) to accept a parameter (e.g.,
paddingValues: PaddingValues) and apply it to the root Column via
Modifier.padding(paddingValues) or alternatively add systemBarsPadding() to the
Column's modifier so the root respects scaffold/system insets; locate the Column
declaration in SavingsAccountScreen.kt and modify its surrounding MifosScaffold
content lambda signature and Column modifier accordingly.
🧹 Nitpick comments (16)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientUpcomingCharges/ClientUpcomingChargesScreen.kt (1)
86-119: Consider addingfillMaxSize()to the root Column for explicit layout behavior.The root
Columncurrently has no modifier, relying on the innerColumnat line 93 to propagate the fill behavior. While this works, addingfillMaxSize()to the root makes the layout intent explicit and guards against unexpected sizing if the composable is used in different contexts.♻️ Suggested change
- Column { + Column(modifier = Modifier.fillMaxSize()) { MifosBreadcrumbNavBar(navController)feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientProfile/ClientProfileScreen.kt (1)
136-140: Consider UX implications of conditional content rendering.When
dialogStateis not null (loading or error), the entire content is hidden. This causes the screen to flash empty before showing the loading indicator or error component. A smoother approach could be to show the content with an overlay or keep a skeleton/placeholder visible.However, if this pattern aligns with how other screens in this refactoring behave, it may be intentional for consistency.
feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientPinpoint/PinpointClientScreen.kt (1)
86-89: Unused parameteronBackPressedafter scaffold removal.The
onBackPressedcallback is declared in both the ViewModel-connected and statelessPinpointClientScreencomposables but is never invoked after removing the scaffold. TheMifosBreadcrumbNavBarhandles back navigation internally vianavController.popBackStack().Consider removing
onBackPressedfrom the function signatures if it's no longer needed, or wire it up if custom back handling is required.Also applies to: 131-143
feature/client/src/commonMain/kotlin/com/mifos/feature/client/recurringDepositAccount/RecurringDepositAccountScreen.kt (1)
163-177: TODO: Search bar functionality is incomplete.The comment indicates that search functionality needs implementation. The search bar UI is wired to dispatch actions (
UpdateSearch,Search,ToggleSearch), but the actual filtering/search logic may not be implemented in the ViewModel.Would you like me to help implement the search filtering logic, or should I open an issue to track this task?
feature/client/src/commonMain/kotlin/com/mifos/feature/client/savingsAccounts/SavingsAccountsScreen.kt (1)
256-281: Extract hardcoded strings to resources for i18n consistency.Lines 269 and 276 use hardcoded strings (
"No Item Found"and"Click Here To View Filled State. ") while the rest of the file usesstringResource(). For consistency and internationalization support, these should be moved to string resources.Additionally, the text
"Click Here To View Filled State."appears to be placeholder/debug text that may not be appropriate for production.feature/client/src/commonMain/kotlin/com/mifos/feature/client/documentPreviewScreen/DocumentPreviewScreen.kt (1)
129-129: Consider using a design token for consistency.Line 129 uses a hardcoded
8.dpwhile other spacing in this file usesDesignToken.spacing.*. For consistency and maintainability, consider using an appropriate design token.♻️ Suggested change
- Spacer(modifier = Modifier.width(8.dp)) + Spacer(modifier = Modifier.width(DesignToken.spacing.small))feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientIdentifiersAddUpdate/ClientIdentitiesAddUpdateScreen.kt (1)
186-195: Applymodifierto the outer Column for flexibility.
Right now the passed-inmodifieronly affects the inner Column. If callers need test tags, padding, or semantics on the root, they can’t set it.♻️ Optional tweak
- Column( - modifier = Modifier.fillMaxSize(), - ) { + Column( + modifier = modifier.fillMaxSize(), + ) { if (state.feature != Feature.VIEW_DOCUMENT) { MifosBreadcrumbNavBar(navController) } Column( - modifier = modifier.fillMaxSize().padding( + modifier = Modifier.fillMaxSize().padding( horizontal = DesignToken.padding.large, ), ) {feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientEditProfile/ClientProfileEditScreen.kt (1)
95-105: Extract a singleonActionlambda to avoid duplication.
This reduces repetition and keeps the same stable reference shared by both composables.♻️ Suggested refactor
val state by viewModel.stateFlow.collectAsStateWithLifecycle() + val onAction = remember(viewModel) { { viewModel.trySendAction(it) } } EventsEffect(viewModel.eventFlow) { event -> when (event) { ClientProfileEditEvent.NavigateBack -> onNavigateBack() ClientProfileEditEvent.OnSaveSuccess -> { @@ ClientProfileEditContent( modifier = modifier, state = state, - onAction = remember(viewModel) { { viewModel.trySendAction(it) } }, + onAction = onAction, navController = navController, ) ClientProfileEditDialogs( state = state, - onAction = remember(viewModel) { { viewModel.trySendAction(it) } }, + onAction = onAction, ) }feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientUpdateDefaultAccount/ClientUpdateDefaultAccountScreen.kt (1)
142-142: Usewidthinstead ofpaddingfor horizontal spacing between buttons.Using
Modifier.padding()on aSpaceris semantically unusual. For horizontal spacing between row elements,Modifier.width()is more appropriate and explicit.♻️ Proposed fix
- Spacer(Modifier.padding(DesignToken.padding.small)) + Spacer(Modifier.width(DesignToken.padding.small))feature/client/src/commonMain/kotlin/com/mifos/feature/client/shareAccounts/ShareAccountsScreen.kt (2)
81-95: Use the passedmodifierat the root to follow Jetpack Compose best practices.The function signature exposes a
modifier: Modifier = Modifierparameter (line 78), but the rootColumn(lines 81–84) usesModifier.fillMaxSize()instead of the passed-in modifier. This prevents callers from applying padding, test tags, or other layout modifications to the whole screen. The innerColumn(lines 93–95) uses the modifier correctly, but it should be at the root level.Apply the passed modifier to the root
Column:Suggested fix
- Column( - modifier = Modifier - .fillMaxSize(), - ) { + Column( + modifier = modifier + .fillMaxSize(), + ) {
104-141: UseitemsIndexedinstead of wrapping the entire list in a singleitemblock.Using
item { forEachIndexed(...) }defeats the lazy evaluation mechanism and forces Compose to measure and layout all rows whenever that single item enters the viewport, causing unnecessary memory overhead and potential jank on large lists.itemsIndexedenables proper lazy composition of individual items.Suggested fix
- LazyColumn { - item { - state.accounts.forEachIndexed { index, account -> - MifosActionsShareListingComponent( - accountNo = account.accountNo ?: emptyText, - shareProductName = account.shortProductName - ?: emptyText, - pendingForApprovalShares = account.totalPendingForApprovalShares, - approvedShares = account.totalApprovedShares, - isExpanded = state.currentlyActiveIndex == index && state.isCardActive, - menuList = (listOf(Actions.ViewAccount())), - onActionClicked = { actions -> - when (actions) { - is Actions.ViewAccount -> { - onAction( - ShareAccountsAction.ViewAccount( - account.id ?: -1, - ), - ) - } - else -> {} - } - }, - onClick = { - onAction( - ShareAccountsAction.CardClicked( - index, - ), - ) - }, - ) - Spacer(Modifier.height(DesignToken.padding.small)) - } - } - } + LazyColumn { + itemsIndexed( + items = state.accounts, + key = { index, account -> account.id ?: index }, + ) { index, account -> + MifosActionsShareListingComponent( + accountNo = account.accountNo ?: emptyText, + shareProductName = account.shortProductName + ?: emptyText, + pendingForApprovalShares = account.totalPendingForApprovalShares, + approvedShares = account.totalApprovedShares, + isExpanded = state.currentlyActiveIndex == index && state.isCardActive, + menuList = (listOf(Actions.ViewAccount())), + onActionClicked = { actions -> + when (actions) { + is Actions.ViewAccount -> { + onAction( + ShareAccountsAction.ViewAccount( + account.id ?: -1, + ), + ) + } + else -> {} + } + }, + onClick = { + onAction( + ShareAccountsAction.CardClicked( + index, + ), + ) + }, + ) + Spacer(Modifier.height(DesignToken.padding.small)) + } + }feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/FixedDepositAccountScreen.kt (1)
135-208: Prefer stable keys + account-keyed expansion state to avoid mismatches after list updates.Index-based expansion can highlight the wrong row after filtering or reordering (e.g., search). Consider keying expansion by account and providing a stable key to the list.
♻️ Suggested change
- var expandedIndex by rememberSaveable { mutableStateOf(-1) } + var expandedKey by rememberSaveable { mutableStateOf<String?>(null) } ... - LazyColumn { - itemsIndexed(state.fixedDepositAccount) { index, fixedDepositAccount -> + LazyColumn { + itemsIndexed( + state.fixedDepositAccount, + key = { index, item -> item.accountNo ?: "index-$index" }, + ) { index, fixedDepositAccount -> + val itemKey = fixedDepositAccount.accountNo ?: "index-$index" + val isExpanded = expandedKey == itemKey MifosActionsSavingsListingComponent( ... - isExpanded = expandedIndex == index, - onExpandToggle = { - expandedIndex = if (expandedIndex == index) -1 else index - }, + isExpanded = isExpanded, + onExpandToggle = { + expandedKey = if (isExpanded) null else itemKey + },feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientTransfer/ClientTransferScreen.kt (1)
196-221: Use a width spacer in the Row.Line 220 uses
Spacer(Modifier.padding(...)), which adds vertical padding inside aRow. A width spacer is clearer for horizontal gaps.♻️ Suggested spacing tweak
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width @@ - Spacer(Modifier.padding(DesignToken.padding.small)) + Spacer(Modifier.width(DesignToken.padding.small))feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientAddress/ClientAddressScreen.kt (1)
138-143: Use a stable, O(1) key for address items.
state.address.indexOf(it)is O(n) per item and can produce unstable keys when duplicates exist, which may cause incorrect item reuse during recomposition. Prefer a stable id (oritemsIndexed) instead.♻️ Suggested refactor
- items(state.address, key = ({ state.address.indexOf(it) })) { address -> + itemsIndexed( + items = state.address, + key = { index, address -> address.id ?: index } // use a stable id if available + ) { _, address ->feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientDocuments/ClientDocumentsScreen.kt (2)
151-154: Applymodifierparameter to the root composable.The
modifierparameter (line 149) is not applied to the outerColumn. This breaks the Compose convention where the modifier should be applied to the root element, preventing parent composables from controlling sizing, padding, or other layout behavior.♻️ Suggested fix
Column( - modifier = Modifier + modifier = modifier .fillMaxSize(), ) {Then update the inner
Columnat line 169 to useModifierinstead:Column( - modifier.fillMaxSize() + Modifier.fillMaxSize() .padding(
250-251: Preferelse -> Unitor an empty block overelse -> null.Returning
nullfrom awhenbranch used as a statement is unconventional. An empty block orUnitis clearer.♻️ Suggested fix
- else -> null + else -> Unit
...tlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationsScreen.kt
Show resolved
Hide resolved
...e/client/src/commonMain/kotlin/com/mifos/feature/client/clientClosure/ClientClosureScreen.kt
Show resolved
Hide resolved
...e/client/src/commonMain/kotlin/com/mifos/feature/client/clientClosure/ClientClosureScreen.kt
Show resolved
Hide resolved
...Main/kotlin/com/mifos/feature/client/clientCollateralDetails/ClientCollateralDetailScreen.kt
Show resolved
Hide resolved
...otlin/com/mifos/feature/client/clientIdentifiersAddUpdate/ClientIdentitiesAddUpdateScreen.kt
Show resolved
Hide resolved
...ain/kotlin/com/mifos/feature/client/recurringDepositAccount/RecurringDepositAccountScreen.kt
Show resolved
Hide resolved
...ient/src/commonMain/kotlin/com/mifos/feature/client/savingsAccounts/SavingsAccountsScreen.kt
Show resolved
Hide resolved
...ure/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/NewLoanAccountScreen.kt
Show resolved
Hide resolved
feature/note/src/commonMain/kotlin/com/mifos/feature/note/notes/NoteScreen.kt
Show resolved
Hide resolved
...ngs/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountv2/SavingsAccountScreen.kt
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/savingsAccounts/SavingsAccountsScreen.kt (1)
241-245: Dead statement:DesignToken.paddingdoes nothing.Line 243 evaluates
DesignToken.paddingbut doesn't use the result. This appears to be incomplete code, possibly intended to add spacing between the icons.🔧 Suggested fix: Add explicit spacing or remove
IconButton( onClick = { onAction.invoke(SavingsAccountAction.ToggleSearch) }, ) { // add a cross icon when its active, talk with design team Icon( painter = painterResource(Res.drawable.search), contentDescription = null, ) } - DesignToken.padding + Spacer(modifier = Modifier.width(DesignToken.padding.small)) IconButton( onClick = { onAction.invoke(SavingsAccountAction.ToggleFilter) }, ) {feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientClosure/ClientClosureScreen.kt (1)
112-227: AddErrorstate to the content visibility condition.The condition currently hides content only for
LoadingandShowStatusDialog, butErrorstate still renders the form in the composition tree. WhileMifosErrorComponentis a full-screen component, the form should be conditionally excluded from rendering for better UX and to avoid unnecessary recomposition. Update the condition at line 112 to:if (state.dialogState != ClientClosureState.DialogState.Loading && state.dialogState !is ClientClosureState.DialogState.ShowStatusDialog && state.dialogState !is ClientClosureState.DialogState.Error )feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientDetailsProfile/ClientProfileDetailsScreen.kt (1)
97-133: Guard against null client before firing navigation actions.
Several branches fall back to-1whenstate.clientis null, which risks routing to invalid screens if actions can be triggered during incomplete/error states. Consider short-circuiting when the client isn’t available.🛠️ Suggested guard
- is ClientProfileDetailsEvent.OnActionClick -> { + is ClientProfileDetailsEvent.OnActionClick -> { + val client = state.client ?: return@EventsEffect when (event.action) { ClientProfileDetailsActionItem.AddCharge -> { - navigateToAddCharge(state.client?.id ?: -1) + navigateToAddCharge(client.id) } ClientProfileDetailsActionItem.ApplyNewApplication -> { navigateToApplyNewApplication( - state.client?.id ?: -1, - state.client?.status?.value ?: "", + client.id, + client.status?.value.orEmpty(), ) } ClientProfileDetailsActionItem.AssignStaff -> { - navigateToAssignStaff(state.client?.id ?: -1) + navigateToAssignStaff(client.id) } ClientProfileDetailsActionItem.ClosureApplication -> { - navigateToClientClosure(state.client?.id ?: -1) + navigateToClientClosure(client.id) } ClientProfileDetailsActionItem.CreateCollateral -> { - navigateToCollateral(state.client?.id ?: -1) + navigateToCollateral(client.id) } ClientProfileDetailsActionItem.TransferClient -> { - navigateToClientTransfer(state.client?.id ?: -1) + navigateToClientTransfer(client.id) } ClientProfileDetailsActionItem.UpdateDefaultAccount -> { - navigateToUpdateDefaultAccount(state.client?.id ?: -1) + navigateToUpdateDefaultAccount(client.id) }
🤖 Fix all issues with AI agents
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientAddress/ClientAddressScreen.kt`:
- Around line 139-145: The LazyColumn is using a non‑stable key
(state.address.indexOf(it)) which is O(n) and can produce duplicate/unstable
keys; update the items call to use the stable unique identifier from the model
(use address.addressId from ClientAddressEntity) as the key (e.g.,
items(state.address, key = { it.addressId }) ), ensuring addressId is
non-null/converted to a stable String if needed so MifosAddressCard composition
remains stable.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientCollateralDetails/ClientCollateralDetailScreen.kt`:
- Around line 74-83: The Error branch in ClientCollateralDetailScreen renders
MifosErrorComponent without the horizontal padding used by the Empty and Success
branches, causing inconsistent layout; update the
ClientCollateralDetailsState.State.Error branch so the MifosErrorComponent call
includes padding(horizontal = DesignToken.padding.large) (same as other
branches) to align visuals, keeping the existing isNetworkConnected,
isRetryEnabled, onRetry and message parameters unchanged.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientEditDetails/ClientEditDetailsScreen.kt`:
- Around line 180-223: The content under the Column (after
MifosBreadcrumbNavBar) can overflow because children like
MifosProgressIndicator, MifosSweetError and MifosStatusDialog use fillMaxSize();
wrap the when (state.dialogState) { ... } block in a container with weight to
constrain it to remaining space — e.g., replace the direct when block with a Box
(or Column) that uses Modifier.weight(1f, fill = true) and place the when inside
it so those full-size children no longer push content below the breadcrumb;
update references around MifosBreadcrumbNavBar, the when on state.dialogState,
and the listed composables (MifosProgressIndicator, MifosSweetError,
MifosStatusDialog) when making the change.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientIdentifiersList/ClientIdentifiersListScreen.kt`:
- Around line 125-200: The LazyColumn currently wraps a single item { ...
forEachIndexed } which forces all rows to compose eagerly; replace that pattern
with LazyColumn { itemsIndexed(...) { index, item -> ... } } using itemsIndexed
to lazily compose each MifosActionsIdentifierListingComponent (preserving props
like status, uniqueKeyForHandleDocument, onAction, onClick, isExpanded which
uses state.currentExpandedItem and state.expandClientIdentity). Also move the
reversed() call out of the composable (e.g., val displayList = remember {
state.clientIdentitiesList.reversed() } or compute it in the ViewModel) so it
isn’t recomputed on every recomposition before passing displayList into
itemsIndexed.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientStaff/ClientStaffScreen.kt`:
- Line 144: The Spacer used in ClientStaffScreen currently uses
Modifier.padding(DesignToken.padding.small) which has no effect because Spacer
has zero intrinsic size; replace that with a horizontal size modifier (e.g.,
Modifier.width(DesignToken.padding.small)) so the Spacer actually creates
visible gap between the buttons in the Row where it's used.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientTransfer/ClientTransferScreen.kt`:
- Around line 135-144: The code directly indexes state.offices with
state.currentSelectedIndex in the MifosTextFieldDropdown leading to possible
IndexOutOfBoundsException; update the value and options computation to
defensively access the list (e.g., use
state.offices.getOrNull(state.currentSelectedIndex)?.name ?: "" for the
displayed value and ensure options = state.offices.map { it.name ?: "" } remains
safe), or clamp/validate currentSelectedIndex before use, and keep the existing
onOptionSelected calling onAction(ClientTransferAction.OptionChanged(index))
unchanged so selected index handling remains consistent.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/documentPreviewScreen/DocumentPreviewScreen.kt`:
- Around line 78-179: The left button in ViewDocumentContent currently shows
"Back" but invokes DocumentPreviewScreenAction.RejectDocument when state.step ==
EntityDocumentState.Step.PREVIEW — change behavior so label and action align:
either swap the action to DocumentPreviewScreenAction.NavigateBack for PREVIEW
or change the displayed text to "Reject" when state.step == PREVIEW (update the
Text stringResource logic accordingly); additionally protect the bottom action
Row from system insets by adding systemBarsPadding() (or appropriate
WindowInsets.padding(WindowInsets.systemBars)) to the Column or the Row
containing the MifosOutlinedButton controls so the buttons are not obscured by
navigation bars.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/FixedDepositAccountScreen.kt`:
- Around line 137-148: The layout overflows because both the loading branch
(MifosProgressIndicator) and the content branch Column use fillMaxSize() and
thus ignore the space taken by MifosBreadcrumbNavBar; update the branches where
state.isLoading is checked so the MifosProgressIndicator and the inner Column
use Modifier.weight(1f).fillMaxWidth() instead of fillMaxSize(), ensuring they
only occupy the remaining space after MifosBreadcrumbNavBar and prevent
overflow.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/CreateFixedDepositAccountScreen.kt`:
- Around line 178-180: The Column is reusing the outer `modifier` (already
passed into `MifosScaffold`) which can duplicate padding/semantics; update the
content inside CreateFixedDepositAccountScreen so the Column uses a fresh
Modifier (e.g., `Modifier.padding(paddingValues)` or another local Modifier
chain) instead of `modifier.padding(paddingValues)` to avoid reapplying the
caller's modifier/semantics.
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/shareAccounts/ShareAccountsScreen.kt`:
- Around line 104-142: The LazyColumn currently uses item {
state.accounts.forEachIndexed { ... } } which forces all rows to compose
eagerly; replace that block with LazyColumn { itemsIndexed(state.accounts) {
index, account -> ... } } so each account is produced lazily and keep the same
inner usage of MifosActionsShareListingComponent and the existing
onClick/onAction handling (ShareAccountsAction.ViewAccount /
ShareAccountsAction.CardClicked); also add the import
androidx.compose.foundation.lazy.itemsIndexed.
In
`@feature/note/src/commonMain/kotlin/com/mifos/feature/note/notes/NoteScreen.kt`:
- Around line 156-163: The MifosScaffold content lambda currently ignores the
provided PaddingValues causing content (Column) to render under system bars;
update the lambda to accept the padding parameter (e.g., "{ paddingValues -> ...
}") and apply it to the Column modifier via .padding(paddingValues) so the
content respects scaffold insets (change the MifosScaffold lambda and the Column
modifier accordingly).
🧹 Nitpick comments (20)
feature/client/src/commonMain/composeResources/values/strings.xml (1)
32-32: Duplicate string resource detected.This string has the exact same value as
update_default_no_accounts_foundon line 438:<string name="update_default_no_accounts_found">No Savings Accounts found for this client</string>Consider reusing the existing string resource instead of creating a duplicate, or consolidate them into a single entry to reduce localization overhead and maintain consistency.
♻️ Suggested fix: Remove the duplicate and use the existing string
<string name="feature_client_accounts">Accounts</string> - <string name="feature_client_no_savings_found">No Savings Accounts found for this client</string>Then update the usage in
ClientUpdateDefaultAccountScreen.ktto referenceupdate_default_no_accounts_foundinstead.Alternatively, if
feature_client_*naming is preferred, remove line 438 and keep this new entry, updating any references toupdate_default_no_accounts_found.feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientUpdateDefaultAccount/ClientUpdateDefaultAccountScreen.kt (1)
90-92: Consider the UX when hiding content during dialog states.The content is completely hidden when
dialogStateisLoadingorShowStatusDialog. This means the user sees a blank screen during loading, rather than the form with a loading overlay. This may be intentional for this flow, but worth confirming it matches the expected UX pattern.feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientUpcomingCharges/ClientUpcomingChargesScreen.kt (1)
87-89: Consider addingfillMaxSize()to the outer Column.The outer Column only applies the passed modifier but doesn't explicitly fill the available space. If callers don't pass a size modifier, this could result in the content not expanding as expected. The inner Column (line 96) uses
fillMaxSize(), but it's only rendered when not loading.♻️ Suggested improvement
Column( - modifier = modifier, + modifier = modifier.fillMaxSize(), ) {feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientLoanAccounts/ClientLoanAccountsScreen.kt (2)
181-182: UseUnitor empty block instead ofnullfor no-op branch.The
else -> nullreturns a nullable value that's discarded. For clarity and to avoid lint warnings, useelse -> Unitorelse -> {}.♻️ Suggested fix
- else -> null + else -> Unit
186-186: Use DesignToken for consistent spacing.The rest of the file uses
DesignToken.padding.*values, but this line uses a hardcoded8.dp. Consider usingDesignToken.padding.medium(or the appropriate token) for consistency.♻️ Suggested fix
- Spacer(modifier = Modifier.height(8.dp)) + Spacer(modifier = Modifier.height(DesignToken.padding.medium))feature/client/src/commonMain/kotlin/com/mifos/feature/client/recurringDepositAccount/RecurringDepositAccountScreen.kt (3)
124-124: Consider usingUnitinstead ofnullin exhaustivewhen.The
else -> nullbranch returns a value that isn't used. For clarity in awhenused as a statement, preferelse -> Unitor remove the explicit return if thewhendoesn't need to be exhaustive.
163-177: TODO: Search bar functionality is not implemented.The comment indicates search functionality needs implementation. The UI components are wired but the actual search logic may be incomplete.
Would you like me to help implement the search functionality or open an issue to track this task?
234-235: Consider usingUnitinstead ofnullfor the else branch.Similar to the dialog,
else -> nullreturns an unused value. For awhenused as a statement, preferelse -> Unitor simplyelse -> {}.feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientSignature/ClientSignatureScreen.kt (2)
93-94: Consider makingClientSignatureDialoginternal for consistency.
ClientSignatureContentis markedinternal(line 167), butClientSignatureDialoglacks a visibility modifier and defaults to public. For screen-specific composables, internal visibility is typically preferred.Suggested change
`@Composable` -fun ClientSignatureDialog( +internal fun ClientSignatureDialog( state: ClientSignatureState, onAction: (ClientSignatureAction) -> Unit, ) {
287-293: Incomplete implementation: "More" option has empty onClick handler.The comment
// it implement furtherindicates this is a placeholder. The "More" option currently does nothing when clicked, which may confuse users.Consider either:
- Hiding this option until implemented
- Adding a TODO comment with a tracking reference
- Showing a "coming soon" toast/snackbar when clicked
Do you want me to open a new issue to track this incomplete functionality?
feature/client/src/commonMain/kotlin/com/mifos/feature/client/savingsAccounts/SavingsAccountsScreen.kt (2)
168-186: Non-idiomatic:else -> nullin a Unit-returning lambda.The
onActionClickedcallback expectsUnit, but line 184 returnsnull. While this compiles (the value is discarded), it's clearer to useelse -> {}orelse -> Unitto express "do nothing" intent.💡 Suggested fix
onActionClicked = { actions -> when (actions) { is Actions.ViewAccount -> onAction.invoke(...) is Actions.ApproveAccount -> onAction.invoke(...) - else -> null + else -> {} } },
303-306: Non-idiomatic:else -> nullin composable when statement.The
else -> nullbranch is confusing in this context. For a statement-levelwheninside a@Composablefunction, useelse -> {}orelse -> Unitto clearly express "render nothing."💡 Suggested fix
- else -> null + else -> Unit } }feature/note/src/commonMain/kotlin/com/mifos/feature/note/addEditNotes/AddEditNoteScreen.kt (1)
125-139: Empty container when error state is active.When
dialogStateisError, theColumnrenders empty since bothMifosBreadcrumbNavBarandAddEditNoteare conditionally hidden. This could cause a brief layout flicker or empty screen whileMifosErrorComponentdisplays fromAddEditNoteScreenDialog.Consider whether the error component should be rendered inline here instead of separately, or ensure the error overlay covers the entire screen to avoid visual artifacts.
feature/client/src/commonMain/kotlin/com/mifos/feature/client/shareAccounts/ShareAccountsScreen.kt (1)
117-128: Consider handling unknown actions explicitly or logging them.The empty
else -> {}branch silently ignores any unhandledActionstypes. While currently safe since onlyViewAccountis in the menu list, this could mask issues if the menu list is extended in the future.feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientTransfer/ClientTransferScreen.kt (1)
155-180: Consider moving DatePickerDialog to ClientTransferDialogs for consistency.The file already has a dedicated
ClientTransferDialogscomposable handling all dialog states (Loading, Error, ShowStatusDialog). Moving theDatePickerDialogthere would centralize dialog management and align with the established pattern in this file.feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientClosure/ClientClosureScreen.kt (1)
202-202: UseModifier.width()for horizontal spacing between buttons.
Modifier.padding()applies padding on all sides; for a horizontalRow, prefer an explicit width to create horizontal space between the two buttons.♻️ Suggested fix
- Spacer(Modifier.padding(DesignToken.padding.small)) + Spacer(Modifier.width(DesignToken.padding.small))feature/note/src/commonMain/kotlin/com/mifos/feature/note/notes/NoteScreen.kt (1)
214-220: Minor: Missingmodifier =named argument for clarity.The
Modifieris passed as a positional argument toIcon. While valid, using the named argument improves readability and consistency.Suggested improvement
Icon( imageVector = MifosIcons.Add, contentDescription = null, - Modifier.clickable { + modifier = Modifier.clickable { onAction(NoteAction.OnClickAddScreen) }.size(DesignToken.sizes.iconAverage), )feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientIdentifiersList/ClientIdentifiersListScreen.kt (1)
142-143: Consider a more robust unique key generation.The current concatenation can produce collisions when nullable fields are null (e.g.,
"nullnullActive"). Consider using a delimiter or the item'sidfor uniqueness:💡 Suggested improvement
- val uniqueKeyForHandleDocument = - item.documentType?.name + item.documentKey + status + val uniqueKeyForHandleDocument = + "${item.documentType?.name.orEmpty()}_${item.documentKey.orEmpty()}_${status?.name.orEmpty()}"Or, if
item.idis always unique when present:val uniqueKeyForHandleDocument = item.id?.toString() ?: "${item.documentType?.name}_${item.documentKey}_${status?.name}"feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientGeneral/ClientProfileGeneralScreen.kt (1)
191-218: Consider centralizing click handling for icon row cards.
Click behavior is now attached viaModifier.clickable(Line 212-217) because the icon-basedMifosRowCardoverload has noonClick. If consistent styling/ripple or reuse is desired, consider adding anonClickparam to that overload and handling it internally.feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/FixedDepositAccountScreen.kt (1)
164-178: Track or remove the TODO for search.The TODO indicates incomplete work. If search is already wired through
FixedDepositAccountAction, consider removing the comment; otherwise, please link it to a ticket so it doesn’t get lost.
| LazyColumn( | ||
| modifier = Modifier | ||
| .fillMaxSize(), | ||
| ) { | ||
| items(state.address, key = ({ state.address.indexOf(it) })) { address -> | ||
| MifosAddressCard( | ||
| title = address.addressType, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, find the file
find . -name "ClientAddressScreen.kt" -type fRepository: openMF/android-client
Length of output: 165
🏁 Script executed:
# Look at the specific code section
cat -n feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientAddress/ClientAddressScreen.kt | sed -n '130,150p'Repository: openMF/android-client
Length of output: 1514
🏁 Script executed:
# Find the address data class definition
rg -A 10 "data class.*Address" --type kotlin | head -50Repository: openMF/android-client
Length of output: 6485
🌐 Web query:
Jetpack Compose LazyColumn stable key best practices unique identifier
💡 Result:
LazyColumn stable key: best practices (unique identifier)
- Always provide a
keyfor dynamic lists (insert/remove/reorder). A stable, unique key lets Compose keep item identity, avoid unnecessary recomposition, and preserve per-item state correctly. (developer.android.com) - Use a real, stable ID from your data model (e.g., database primary key, server ID, UUID). Example:
key = { it.id }. (developer.android.com) - Do not use list position/index as the key for changing lists. If
keyisnull, Compose effectively keys by position, which breaks identity when items move. (developer.android.com) - Keys must be unique and stable across updates, and duplicate keys are not allowed. (developer.android.com)
- Key type should be Bundle-saveable on Android (prefer
Long,Int,String; avoid complex objects). (developer.android.com) - Stable keys help maintain scroll position by item identity when content changes before the visible range. (developer.android.com)
Recommended pattern
LazyColumn {
items(
items = notes,
key = { it.id } // stable, unique
) { note ->
NoteRow(note)
}
}If you use Paging (LazyPagingItems)
Use the provided helpers to generate stable keys:
items(
count = lazyPagingItems.itemCount,
key = lazyPagingItems.itemKey { it.id }
) { index -> ... }Citations:
- 1: https://developer.android.com/develop/ui/compose/performance/bestpractices?utm_source=openai
- 2: https://developer.android.com/develop/ui/compose/performance/bestpractices?utm_source=openai
- 3: https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListScope?utm_source=openai
- 4: https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListScope?utm_source=openai
- 5: https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListScope?utm_source=openai
- 6: https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListScope?utm_source=openai
- 7: https://developer.android.com/develop/ui/compose/performance/bestpractices?utm_source=openai
- 8: https://developer.android.com/reference/kotlin/androidx/paging/compose/LazyPagingItems?utm_source=openai
🏁 Script executed:
# Find ClientAddressEntity definition
rg -B 2 -A 15 "data class ClientAddressEntity" --type kotlinRepository: openMF/android-client
Length of output: 2287
🏁 Script executed:
# Also check for any related imports or type definitions in ClientAddressScreen
head -30 feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientAddress/ClientAddressScreen.ktRepository: openMF/android-client
Length of output: 1783
Use a stable ID key instead of indexOf() for LazyColumn items.
indexOf(it) is O(n) per item and returns the first match for duplicates, which causes unstable keys and composition issues. Using positional indices (via itemsIndexed) has the same problem. Instead, use the addressId field available in ClientAddressEntity as a stable, unique key.
🔧 Suggested fix
LazyColumn(
modifier = Modifier
.fillMaxSize(),
) {
- items(state.address, key = ({ state.address.indexOf(it) })) { address ->
+ items(state.address, key = { it.addressId }) { address ->
MifosAddressCard(📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| LazyColumn( | |
| modifier = Modifier | |
| .fillMaxSize(), | |
| ) { | |
| items(state.address, key = ({ state.address.indexOf(it) })) { address -> | |
| MifosAddressCard( | |
| title = address.addressType, | |
| LazyColumn( | |
| modifier = Modifier | |
| .fillMaxSize(), | |
| ) { | |
| items(state.address, key = { it.addressId }) { address -> | |
| MifosAddressCard( | |
| title = address.addressType, |
🤖 Prompt for AI Agents
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientAddress/ClientAddressScreen.kt`
around lines 139 - 145, The LazyColumn is using a non‑stable key
(state.address.indexOf(it)) which is O(n) and can produce duplicate/unstable
keys; update the items call to use the stable unique identifier from the model
(use address.addressId from ClientAddressEntity) as the key (e.g.,
items(state.address, key = { it.addressId }) ), ensuring addressId is
non-null/converted to a stable String if needed so MifosAddressCard composition
remains stable.
| is ClientCollateralDetailsState.State.Error -> { | ||
| MifosErrorComponent( | ||
| isNetworkConnected = state.state.isNetworkAvailable, | ||
| isRetryEnabled = true, | ||
| onRetry = { | ||
| onAction(ClientCollateralDetailsAction.OnRetry) | ||
| }, | ||
| message = state.state.message, | ||
| ) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent horizontal padding in Error state.
The Empty and Success states apply padding(horizontal = DesignToken.padding.large), but the Error state has no horizontal padding. This may cause visual inconsistency.
Suggested fix
is ClientCollateralDetailsState.State.Error -> {
MifosErrorComponent(
+ modifier = Modifier.padding(horizontal = DesignToken.padding.large),
isNetworkConnected = state.state.isNetworkAvailable,
isRetryEnabled = true,
onRetry = {
onAction(ClientCollateralDetailsAction.OnRetry)
},
message = state.state.message,
)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| is ClientCollateralDetailsState.State.Error -> { | |
| MifosErrorComponent( | |
| isNetworkConnected = state.state.isNetworkAvailable, | |
| isRetryEnabled = true, | |
| onRetry = { | |
| onAction(ClientCollateralDetailsAction.OnRetry) | |
| }, | |
| message = state.state.message, | |
| ) | |
| } | |
| is ClientCollateralDetailsState.State.Error -> { | |
| MifosErrorComponent( | |
| modifier = Modifier.padding(horizontal = DesignToken.padding.large), | |
| isNetworkConnected = state.state.isNetworkAvailable, | |
| isRetryEnabled = true, | |
| onRetry = { | |
| onAction(ClientCollateralDetailsAction.OnRetry) | |
| }, | |
| message = state.state.message, | |
| ) | |
| } |
🤖 Prompt for AI Agents
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientCollateralDetails/ClientCollateralDetailScreen.kt`
around lines 74 - 83, The Error branch in ClientCollateralDetailScreen renders
MifosErrorComponent without the horizontal padding used by the Empty and Success
branches, causing inconsistent layout; update the
ClientCollateralDetailsState.State.Error branch so the MifosErrorComponent call
includes padding(horizontal = DesignToken.padding.large) (same as other
branches) to align visuals, keeping the existing isNetworkConnected,
isRetryEnabled, onRetry and message parameters unchanged.
| Column( | ||
| modifier = modifier | ||
| .fillMaxSize(), | ||
| ) { | ||
| MifosBreadcrumbNavBar(navController) | ||
| when (state.dialogState) { | ||
| is ClientEditDetailsState.DialogState.Loading -> { | ||
| MifosProgressIndicator() | ||
| } | ||
|
|
||
| else -> {} | ||
| is ClientEditDetailsState.DialogState.ShowUpdateDetailsContent -> { | ||
| UpdateClientDetailsFields( | ||
| state = state, | ||
| scope = scope, | ||
| officeList = officeList, | ||
| staffInOffices = staffInOffices, | ||
| clientTemplate = state.clientsTemplate, | ||
| loadStaffInOffice = loadStaffInOffice, | ||
| updateClient = updateClient, | ||
| navigateBack = navigateBack, | ||
| ) | ||
| } | ||
|
|
||
| is ClientEditDetailsState.DialogState.Error -> { | ||
| MifosSweetError( | ||
| message = state.dialogState.message, | ||
| ) | ||
| } | ||
|
|
||
| is ClientEditDetailsState.DialogState.ShowStatusDialog -> { | ||
| MifosStatusDialog( | ||
| status = state.dialogState.status, | ||
| btnText = stringResource(Res.string.dialog_continue), | ||
| onConfirm = { onAction(ClientEditDetailsAction.OnNext) }, | ||
| successTitle = stringResource(Res.string.client_details_updated), | ||
| successMessage = stringResource(Res.string.client_details_updated_success_message), | ||
| failureTitle = stringResource(Res.string.client_details_update_failure_title), | ||
| failureMessage = state.dialogState.msg, | ||
| modifier = Modifier.fillMaxSize(), | ||
| ) | ||
| } | ||
|
|
||
| else -> {} | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
find . -name "ClientEditDetailsScreen.kt" -type fRepository: openMF/android-client
Length of output: 173
🏁 Script executed:
cat -n "./feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientEditDetails/ClientEditDetailsScreen.kt" | sed -n '170,235p'Repository: openMF/android-client
Length of output: 3000
🏁 Script executed:
cat -n "./feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientEditDetails/ClientEditDetailsScreen.kt" | sed -n '155,177p'Repository: openMF/android-client
Length of output: 1069
🏁 Script executed:
cat -n "./feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientEditDetails/ClientEditDetailsScreen.kt" | sed -n '130,165p'Repository: openMF/android-client
Length of output: 1534
🏁 Script executed:
wc -l "./feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientEditDetails/ClientEditDetailsScreen.kt"Repository: openMF/android-client
Length of output: 178
🏁 Script executed:
rg "Scaffold" "./feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientEditDetails/ClientEditDetailsScreen.kt"Repository: openMF/android-client
Length of output: 142
🏁 Script executed:
rg -B 5 -A 10 "MifosScaffold\(" "./feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientEditDetails/ClientEditDetailsScreen.kt"Repository: openMF/android-client
Length of output: 601
🏁 Script executed:
rg -n "MifosScaffold\(" "./feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientEditDetails/ClientEditDetailsScreen.kt"Repository: openMF/android-client
Length of output: 87
🏁 Script executed:
cat -n "./feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientEditDetails/ClientEditDetailsScreen.kt" | sed -n '400,440p'Repository: openMF/android-client
Length of output: 2014
🏁 Script executed:
cat -n "./feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientEditDetails/ClientEditDetailsScreen.kt" | sed -n '227,250p'Repository: openMF/android-client
Length of output: 1360
🏁 Script executed:
find . -name "MifosProgressIndicator.kt" -o -name "*ProgressIndicator*" | grep -v node_modules | head -5Repository: openMF/android-client
Length of output: 151
🏁 Script executed:
cat "./core/ui/src/commonMain/kotlin/com/mifos/core/ui/components/MifosProgressIndicator.kt"Repository: openMF/android-client
Length of output: 5268
🏁 Script executed:
find . -name "*SweetError*" -o -name "*StatusDialog*" | grep -v node_modules | grep kotlinRepository: openMF/android-client
Length of output: 245
🏁 Script executed:
cat "./core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosSweetError.kt"Repository: openMF/android-client
Length of output: 4956
🏁 Script executed:
cat "./core/ui/src/commonMain/kotlin/com/mifos/core/ui/components/MifosStatusDialog.kt"Repository: openMF/android-client
Length of output: 4269
Wrap when statement in Box with weight(1f) to prevent children from overflowing below the breadcrumb.
The Column at line 180 stacks a breadcrumb above content that uses fillMaxSize() (MifosProgressIndicator, MifosSweetError, and MifosStatusDialog in the diff). This can cause child content to exceed the viewport, clipping content or pushing layout below the screen. Constrain the content area to remaining space using Modifier.weight(1f, fill = true) as shown in the diff.
🔧 Suggested fix
Column(
modifier = modifier
.fillMaxSize(),
) {
MifosBreadcrumbNavBar(navController)
- when (state.dialogState) {
- is ClientEditDetailsState.DialogState.Loading -> {
- MifosProgressIndicator()
- }
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f, fill = true),
+ ) {
+ when (state.dialogState) {
+ is ClientEditDetailsState.DialogState.Loading -> {
+ MifosProgressIndicator()
+ }
- is ClientEditDetailsState.DialogState.ShowUpdateDetailsContent -> {
- UpdateClientDetailsFields(
- state = state,
- scope = scope,
- officeList = officeList,
- staffInOffices = staffInOffices,
- clientTemplate = state.clientsTemplate,
- loadStaffInOffice = loadStaffInOffice,
- updateClient = updateClient,
- navigateBack = navigateBack,
- )
- }
+ is ClientEditDetailsState.DialogState.ShowUpdateDetailsContent -> {
+ UpdateClientDetailsFields(
+ state = state,
+ scope = scope,
+ officeList = officeList,
+ staffInOffices = staffInOffices,
+ clientTemplate = state.clientsTemplate,
+ loadStaffInOffice = loadStaffInOffice,
+ updateClient = updateClient,
+ navigateBack = navigateBack,
+ )
+ }
- is ClientEditDetailsState.DialogState.Error -> {
- MifosSweetError(
- message = state.dialogState.message,
- )
- }
+ is ClientEditDetailsState.DialogState.Error -> {
+ MifosSweetError(
+ message = state.dialogState.message,
+ )
+ }
- is ClientEditDetailsState.DialogState.ShowStatusDialog -> {
- MifosStatusDialog(
- status = state.dialogState.status,
- btnText = stringResource(Res.string.dialog_continue),
- onConfirm = { onAction(ClientEditDetailsAction.OnNext) },
- successTitle = stringResource(Res.string.client_details_updated),
- successMessage = stringResource(Res.string.client_details_updated_success_message),
- failureTitle = stringResource(Res.string.client_details_update_failure_title),
- failureMessage = state.dialogState.msg,
- modifier = Modifier.fillMaxSize(),
- )
- }
+ is ClientEditDetailsState.DialogState.ShowStatusDialog -> {
+ MifosStatusDialog(
+ status = state.dialogState.status,
+ btnText = stringResource(Res.string.dialog_continue),
+ onConfirm = { onAction(ClientEditDetailsAction.OnNext) },
+ successTitle = stringResource(Res.string.client_details_updated),
+ successMessage = stringResource(Res.string.client_details_updated_success_message),
+ failureTitle = stringResource(Res.string.client_details_update_failure_title),
+ failureMessage = state.dialogState.msg,
+ modifier = Modifier.fillMaxSize(),
+ )
+ }
- else -> {}
+ else -> {}
+ }
}
}🤖 Prompt for AI Agents
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientEditDetails/ClientEditDetailsScreen.kt`
around lines 180 - 223, The content under the Column (after
MifosBreadcrumbNavBar) can overflow because children like
MifosProgressIndicator, MifosSweetError and MifosStatusDialog use fillMaxSize();
wrap the when (state.dialogState) { ... } block in a container with weight to
constrain it to remaining space — e.g., replace the direct when block with a Box
(or Column) that uses Modifier.weight(1f, fill = true) and place the when inside
it so those full-size children no longer push content below the breadcrumb;
update references around MifosBreadcrumbNavBar, the when on state.dialogState,
and the listed composables (MifosProgressIndicator, MifosSweetError,
MifosStatusDialog) when making the change.
| LazyColumn { | ||
| item { | ||
| state.clientIdentitiesList.reversed().forEachIndexed { index, item -> | ||
|
|
||
| val status = getClientIdentifierStatus(item.status) | ||
| val status = getClientIdentifierStatus(item.status) | ||
|
|
||
| /** | ||
| * A unique key generated for document operations (update/delete). | ||
| * | ||
| * This key is built by concatenating: | ||
| * - the document type name | ||
| * - the document key | ||
| * - the current status | ||
| * | ||
| * It ensures each document can be uniquely identified and handled | ||
| * even if multiple documents share the same type or key. | ||
| */ | ||
| val uniqueKeyForHandleDocument = item.documentType?.name + item.documentKey + status | ||
| /** | ||
| * A unique key generated for document operations (update/delete). | ||
| * | ||
| * This key is built by concatenating: | ||
| * - the document type name | ||
| * - the document key | ||
| * - the current status | ||
| * | ||
| * It ensures each document can be uniquely identified and handled | ||
| * even if multiple documents share the same type or key. | ||
| */ | ||
| val uniqueKeyForHandleDocument = | ||
| item.documentType?.name + item.documentKey + status | ||
|
|
||
| MifosActionsIdentifierListingComponent( | ||
| type = item.documentType?.name ?: emptyMessage, | ||
| id = if (item.id != null) item.id.toString() else emptyMessage, | ||
| key = item.documentKey ?: emptyMessage, | ||
| status = status, | ||
| description = item.description ?: emptyMessage, | ||
| // TODO check what is identifyDocuments, couldnot find in the api | ||
| identifyDocuments = item.documentType?.name ?: emptyMessage, | ||
| menuList = listOf( | ||
| Actions.ViewDocument(), | ||
| Actions.UploadAgain(), | ||
| Actions.DeleteDocument(), | ||
| ), | ||
| onActionClicked = { actions -> | ||
| when (actions) { | ||
| is Actions.ViewDocument -> onAction.invoke( | ||
| ClientIdentifiersListAction.ViewDocument(uniqueKeyForHandleDocument), | ||
| ) | ||
| MifosActionsIdentifierListingComponent( | ||
| type = item.documentType?.name ?: emptyMessage, | ||
| id = if (item.id != null) item.id.toString() else emptyMessage, | ||
| key = item.documentKey ?: emptyMessage, | ||
| status = status, | ||
| description = item.description ?: emptyMessage, | ||
| // TODO check what is identifyDocuments, couldnot find in the api | ||
| identifyDocuments = item.documentType?.name ?: emptyMessage, | ||
| menuList = listOf( | ||
| Actions.ViewDocument(), | ||
| Actions.UploadAgain(), | ||
| Actions.DeleteDocument(), | ||
| ), | ||
| onActionClicked = { actions -> | ||
| when (actions) { | ||
| is Actions.ViewDocument -> onAction.invoke( | ||
| ClientIdentifiersListAction.ViewDocument( | ||
| uniqueKeyForHandleDocument, | ||
| ), | ||
| ) | ||
|
|
||
| is Actions.UploadAgain -> onAction.invoke( | ||
| ClientIdentifiersListAction.UploadAgain(uniqueKeyForHandleDocument), | ||
| ) | ||
| is Actions.UploadAgain -> onAction.invoke( | ||
| ClientIdentifiersListAction.UploadAgain( | ||
| uniqueKeyForHandleDocument, | ||
| ), | ||
| ) | ||
|
|
||
| is Actions.DeleteDocument -> { | ||
| val id = item.id | ||
| if (id != null) { | ||
| onAction.invoke( | ||
| ClientIdentifiersListAction.ShowDeleteConfirmation(id, uniqueKeyForHandleDocument), | ||
| ) | ||
| } | ||
| is Actions.DeleteDocument -> { | ||
| val id = item.id | ||
| if (id != null) { | ||
| onAction.invoke( | ||
| ClientIdentifiersListAction.ShowDeleteConfirmation( | ||
| id, | ||
| uniqueKeyForHandleDocument, | ||
| ), | ||
| ) | ||
| } | ||
| else -> {} | ||
| } | ||
| }, | ||
| onClick = { | ||
| onAction.invoke( | ||
| ClientIdentifiersListAction.ToggleShowMenu( | ||
| index, | ||
| ), | ||
| ) | ||
| }, | ||
| isExpanded = (index == state.currentExpandedItem) && state.expandClientIdentity, | ||
| ) | ||
|
|
||
| Spacer(Modifier.height(DesignToken.spacing.small)) | ||
| } | ||
| else -> {} | ||
| } | ||
| }, | ||
| onClick = { | ||
| onAction.invoke( | ||
| ClientIdentifiersListAction.ToggleShowMenu( | ||
| index, | ||
| ), | ||
| ) | ||
| }, | ||
| isExpanded = (index == state.currentExpandedItem) && state.expandClientIdentity, | ||
| ) | ||
|
|
||
| Spacer(Modifier.height(DesignToken.spacing.small)) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LazyColumn anti-pattern defeats lazy loading.
Using a single item { } block with forEachIndexed inside composes all items at once, negating the benefits of LazyColumn. For proper lazy composition, use itemsIndexed().
🔧 Proposed fix using itemsIndexed
- LazyColumn {
- item {
- state.clientIdentitiesList.reversed().forEachIndexed { index, item ->
+ val reversedList = state.clientIdentitiesList.reversed()
+ LazyColumn {
+ itemsIndexed(
+ items = reversedList,
+ key = { _, item -> item.id ?: item.hashCode() }
+ ) { index, item ->
val status = getClientIdentifierStatus(item.status)
// ... rest of the item content ...
Spacer(Modifier.height(DesignToken.spacing.small))
- }
}
}Note: The reversed() call should be moved outside the LazyColumn to avoid recomputing on each recomposition, ideally wrapped in remember or derived in the ViewModel.
🤖 Prompt for AI Agents
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientIdentifiersList/ClientIdentifiersListScreen.kt`
around lines 125 - 200, The LazyColumn currently wraps a single item { ...
forEachIndexed } which forces all rows to compose eagerly; replace that pattern
with LazyColumn { itemsIndexed(...) { index, item -> ... } } using itemsIndexed
to lazily compose each MifosActionsIdentifierListingComponent (preserving props
like status, uniqueKeyForHandleDocument, onAction, onClick, isExpanded which
uses state.currentExpandedItem and state.expandClientIdentity). Also move the
reversed() call out of the composable (e.g., val displayList = remember {
state.clientIdentitiesList.reversed() } or compute it in the ViewModel) so it
isn’t recomputed on every recomposition before passing displayList into
itemsIndexed.
| onValueChanged = {}, | ||
| onOptionSelected = { index, value -> | ||
| onAction(ClientStaffAction.OptionChanged(index)) | ||
| Spacer(Modifier.padding(DesignToken.padding.small)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spacer with padding modifier has no effect.
Spacer has zero intrinsic size, so applying Modifier.padding() adds space around nothing, resulting in no visible gap between buttons. Use Modifier.width() instead for horizontal spacing in a Row.
🐛 Proposed fix
- Spacer(Modifier.padding(DesignToken.padding.small))
+ Spacer(Modifier.width(DesignToken.padding.small))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Spacer(Modifier.padding(DesignToken.padding.small)) | |
| Spacer(Modifier.width(DesignToken.padding.small)) |
🤖 Prompt for AI Agents
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientStaff/ClientStaffScreen.kt`
at line 144, The Spacer used in ClientStaffScreen currently uses
Modifier.padding(DesignToken.padding.small) which has no effect because Spacer
has zero intrinsic size; replace that with a horizontal size modifier (e.g.,
Modifier.width(DesignToken.padding.small)) so the Spacer actually creates
visible gap between the buttons in the Row where it's used.
| private fun ViewDocumentContent( | ||
| state: DocumentPreviewState, | ||
| modifier: Modifier = Modifier, | ||
| onAction: (DocumentPreviewScreenAction) -> Unit, | ||
| ) { | ||
| MifosScaffold( | ||
| modifier = Modifier | ||
| Column( | ||
| modifier = modifier | ||
| .padding(DesignToken.padding.large) | ||
| .fillMaxSize(), | ||
| title = "", | ||
| onBackPressed = { | ||
| onAction(DocumentPreviewScreenAction.NavigateBack) | ||
| }, | ||
| ) { paddingValues -> | ||
| Column( | ||
| modifier = modifier | ||
| .padding(paddingValues) | ||
| .padding(DesignToken.padding.large) | ||
| .fillMaxSize(), | ||
| verticalArrangement = Arrangement.Center, | ||
| horizontalAlignment = Alignment.CenterHorizontally, | ||
| verticalArrangement = Arrangement.Center, | ||
| horizontalAlignment = Alignment.CenterHorizontally, | ||
| ) { | ||
| ViewDocumentsScreenContent( | ||
| state = state, | ||
| modifier = Modifier.weight(1f), | ||
| onAction = onAction, | ||
| ) | ||
| Spacer(modifier = Modifier.height(DesignToken.spacing.largeIncreased)) | ||
| Row( | ||
| modifier = Modifier | ||
| .fillMaxWidth() | ||
| .wrapContentHeight(), | ||
| horizontalArrangement = Arrangement.SpaceBetween, | ||
| ) { | ||
| ViewDocumentsScreenContent( | ||
| state = state, | ||
| modifier = Modifier.weight(1f), | ||
| onAction = onAction, | ||
| ) | ||
| Spacer(modifier = Modifier.height(DesignToken.spacing.largeIncreased)) | ||
| Row( | ||
| MifosOutlinedButton( | ||
| onClick = { | ||
| if (state.step == EntityDocumentState.Step.PREVIEW) { | ||
| onAction(DocumentPreviewScreenAction.RejectDocument) | ||
| } else { | ||
| onAction(DocumentPreviewScreenAction.NavigateBack) | ||
| } | ||
| }, | ||
| colors = ButtonDefaults.outlinedButtonColors( | ||
| containerColor = MaterialTheme.colorScheme.onPrimary, | ||
| contentColor = MaterialTheme.colorScheme.primary, | ||
| ), | ||
| border = BorderStroke( | ||
| 1.dp, | ||
| MaterialTheme.colorScheme.secondaryContainer, | ||
| ), | ||
| shape = DesignToken.shapes.small, | ||
| modifier = Modifier | ||
| .fillMaxWidth() | ||
| .wrapContentHeight(), | ||
| horizontalArrangement = Arrangement.SpaceBetween, | ||
| .height(DesignToken.sizes.iconExtraLarge) | ||
| .weight(1f), | ||
| ) { | ||
| MifosOutlinedButton( | ||
| onClick = { | ||
| if (state.step == EntityDocumentState.Step.PREVIEW) { | ||
| onAction(DocumentPreviewScreenAction.RejectDocument) | ||
| } else { | ||
| onAction(DocumentPreviewScreenAction.NavigateBack) | ||
| } | ||
| }, | ||
| colors = ButtonDefaults.outlinedButtonColors( | ||
| containerColor = MaterialTheme.colorScheme.onPrimary, | ||
| contentColor = MaterialTheme.colorScheme.primary, | ||
| ), | ||
| border = BorderStroke( | ||
| 1.dp, | ||
| MaterialTheme.colorScheme.secondaryContainer, | ||
| ), | ||
| shape = DesignToken.shapes.small, | ||
| modifier = Modifier | ||
| .height(DesignToken.sizes.iconExtraLarge) | ||
| .weight(1f), | ||
| ) { | ||
| Text( | ||
| stringResource(Res.string.btn_back), | ||
| fontFamily = FontFamily.SansSerif, | ||
| style = MaterialTheme.typography.labelLarge, | ||
| ) | ||
| } | ||
| Spacer(modifier = Modifier.width(8.dp)) | ||
| MifosOutlinedButton( | ||
| onClick = { | ||
| if (state.step == EntityDocumentState.Step.PREVIEW) { | ||
| onAction(DocumentPreviewScreenAction.SubmitClicked) | ||
| } else { | ||
| onAction(DocumentPreviewScreenAction.UpdateNew) | ||
| } | ||
| }, | ||
| colors = ButtonDefaults.outlinedButtonColors( | ||
| containerColor = MaterialTheme.colorScheme.primary, | ||
| contentColor = MaterialTheme.colorScheme.onPrimary, | ||
| ), | ||
| border = BorderStroke( | ||
| 1.dp, | ||
| MaterialTheme.colorScheme.secondaryContainer, | ||
| ), | ||
| shape = DesignToken.shapes.small, | ||
| enabled = state.step == EntityDocumentState.Step.PREVIEW || | ||
| state.step == EntityDocumentState.Step.UPDATE_PREVIEW, | ||
| modifier = Modifier | ||
| .height(DesignToken.sizes.iconExtraLarge) | ||
| .weight(1f), | ||
| ) { | ||
| Text( | ||
| if (state.step == EntityDocumentState.Step.UPDATE_PREVIEW) { | ||
| stringResource(Res.string.btn_update_new) | ||
| } else { | ||
| stringResource(Res.string.btn_submit) | ||
| }, | ||
| fontFamily = FontFamily.SansSerif, | ||
| style = MaterialTheme.typography.labelLarge, | ||
| ) | ||
| } | ||
| Text( | ||
| stringResource(Res.string.btn_back), | ||
| fontFamily = FontFamily.SansSerif, | ||
| style = MaterialTheme.typography.labelLarge, | ||
| ) | ||
| } | ||
|
|
||
| if (state.showBottomSheet) { | ||
| MifosFilePickerBottomSheet( | ||
| onDismiss = { | ||
| onAction(DocumentPreviewScreenAction.DismissBottomSheet) | ||
| }, | ||
| onGalleryClick = { | ||
| onAction(DocumentPreviewScreenAction.PickFromGallery) | ||
| }, | ||
| onFilesClick = { | ||
| onAction(DocumentPreviewScreenAction.PickFromFile) | ||
| }, | ||
| onMoreClick = { | ||
| onAction(DocumentPreviewScreenAction.UseMoreOptions) | ||
| Spacer(modifier = Modifier.width(8.dp)) | ||
| MifosOutlinedButton( | ||
| onClick = { | ||
| if (state.step == EntityDocumentState.Step.PREVIEW) { | ||
| onAction(DocumentPreviewScreenAction.SubmitClicked) | ||
| } else { | ||
| onAction(DocumentPreviewScreenAction.UpdateNew) | ||
| } | ||
| }, | ||
| colors = ButtonDefaults.outlinedButtonColors( | ||
| containerColor = MaterialTheme.colorScheme.primary, | ||
| contentColor = MaterialTheme.colorScheme.onPrimary, | ||
| ), | ||
| border = BorderStroke( | ||
| 1.dp, | ||
| MaterialTheme.colorScheme.secondaryContainer, | ||
| ), | ||
| shape = DesignToken.shapes.small, | ||
| enabled = state.step == EntityDocumentState.Step.PREVIEW || | ||
| state.step == EntityDocumentState.Step.UPDATE_PREVIEW, | ||
| modifier = Modifier | ||
| .height(DesignToken.sizes.iconExtraLarge) | ||
| .weight(1f), | ||
| ) { | ||
| Text( | ||
| if (state.step == EntityDocumentState.Step.UPDATE_PREVIEW) { | ||
| stringResource(Res.string.btn_update_new) | ||
| } else { | ||
| stringResource(Res.string.btn_submit) | ||
| }, | ||
| fontFamily = FontFamily.SansSerif, | ||
| style = MaterialTheme.typography.labelLarge, | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| if (state.showBottomSheet) { | ||
| MifosFilePickerBottomSheet( | ||
| onDismiss = { | ||
| onAction(DocumentPreviewScreenAction.DismissBottomSheet) | ||
| }, | ||
| onGalleryClick = { | ||
| onAction(DocumentPreviewScreenAction.PickFromGallery) | ||
| }, | ||
| onFilesClick = { | ||
| onAction(DocumentPreviewScreenAction.PickFromFile) | ||
| }, | ||
| onMoreClick = { | ||
| onAction(DocumentPreviewScreenAction.UseMoreOptions) | ||
| }, | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, find where DocumentPreviewScreen is defined
find . -name "*.kt" | xargs rg -l "fun DocumentPreviewScreen" | head -5Repository: openMF/android-client
Length of output: 175
🏁 Script executed:
#!/bin/bash
# Look for DocumentPreviewScreen usage and hosting context
rg -n -C5 "DocumentPreviewScreen\s*\(" --type kotlin | head -40Repository: openMF/android-client
Length of output: 4910
🏁 Script executed:
#!/bin/bash
# Check the string resources to understand button labels
rg "btn_back|btn_reject|btn_submit|btn_update_new" --type kotlin -C2 | head -30Repository: openMF/android-client
Length of output: 4211
🏁 Script executed:
#!/bin/bash
# Read the full DocumentPreviewScreen.kt file to understand the root layout structure
cat -n feature/client/src/commonMain/kotlin/com/mifos/feature/client/documentPreviewScreen/DocumentPreviewScreen.kt | head -90Repository: openMF/android-client
Length of output: 4305
🏁 Script executed:
#!/bin/bash
# Check if Scaffold is used anywhere in the file
rg -n "Scaffold" feature/client/src/commonMain/kotlin/com/mifos/feature/client/documentPreviewScreen/DocumentPreviewScreen.ktRepository: openMF/android-client
Length of output: 47
🏁 Script executed:
#!/bin/bash
# Look for system insets handling (systemBarsPadding, safeContentPadding, windowInsets)
rg -n -i "inset|systembar|safeContent|windowInset" feature/client/src/commonMain/kotlin/com/mifos/feature/client/documentPreviewScreen/DocumentPreviewScreen.ktRepository: openMF/android-client
Length of output: 47
🏁 Script executed:
#!/bin/bash
# Read more of the file to see the complete ViewDocumentContent and action handling
cat -n feature/client/src/commonMain/kotlin/com/mifos/feature/client/documentPreviewScreen/DocumentPreviewScreen.kt | sed -n '70,200p'Repository: openMF/android-client
Length of output: 5870
🏁 Script executed:
#!/bin/bash
# Check the DocumentPreviewScreenViewModel to understand action handling
rg -n "RejectDocument|SubmitClicked|NavigateBack|UpdateNew" feature/client/src/commonMain/kotlin/com/mifos/feature/client/documentPreviewScreen/ -A2 -B2Repository: openMF/android-client
Length of output: 10678
🏁 Script executed:
#!/bin/bash
# Check if there's a parent composition (Scaffold or Box with insets) wrapping DocumentPreviewScreen
rg -n "DocumentPreviewScreen" feature/client/src/commonMain/kotlin/com/mifos/feature/client/documentPreviewScreen/DocumentPreviewScreenRoute.kt -C10Repository: openMF/android-client
Length of output: 903
Align left button label with its action in PREVIEW state, and add system insets protection to action buttons.
-
The left button displays "Back" but triggers
RejectDocumentwhen in PREVIEW state (which resets and rejects the document). This semantic mismatch is confusing; either change the label to "Reject" for PREVIEW state or adjust the action to match the "Back" intent. -
The root
Columnapplies onlyDesignToken.padding.largewithout system insets handling. The action buttonRowat the bottom has no protection against system navigation bars (gesture nav), and could be obscured. AddsystemBarsPadding()or useWindowInsetsto ensure buttons remain accessible.
🤖 Prompt for AI Agents
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/documentPreviewScreen/DocumentPreviewScreen.kt`
around lines 78 - 179, The left button in ViewDocumentContent currently shows
"Back" but invokes DocumentPreviewScreenAction.RejectDocument when state.step ==
EntityDocumentState.Step.PREVIEW — change behavior so label and action align:
either swap the action to DocumentPreviewScreenAction.NavigateBack for PREVIEW
or change the displayed text to "Reject" when state.step == PREVIEW (update the
Text stringResource logic accordingly); additionally protect the bottom action
Row from system insets by adding systemBarsPadding() (or appropriate
WindowInsets.padding(WindowInsets.systemBars)) to the Column or the Row
containing the MifosOutlinedButton controls so the buttons are not obscured by
navigation bars.
| Column( | ||
| modifier = modifier | ||
| .fillMaxSize(), | ||
| ) { | ||
| MifosBreadcrumbNavBar(navController) | ||
|
|
||
| when (state.isLoading) { | ||
| true -> MifosProgressIndicator() | ||
| false -> { | ||
| Column( | ||
| Modifier.fillMaxSize() | ||
| .padding( | ||
| horizontal = DesignToken.padding.large, | ||
| ), | ||
| ) { | ||
| val notAvailableText = stringResource(Res.string.client_savings_not_avilable) | ||
| when (state.isLoading) { | ||
| true -> MifosProgressIndicator() | ||
| false -> { | ||
| Column( | ||
| Modifier.fillMaxSize() | ||
| .padding( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, let's look at the specific file and lines mentioned
if [ -f "feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/FixedDepositAccountScreen.kt" ]; then
echo "=== Lines 130-160 of the file ==="
sed -n '130,160p' "feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/FixedDepositAccountScreen.kt" | cat -n
else
echo "File not found at expected path, searching for it..."
find . -name "FixedDepositAccountScreen.kt" -type f
fiRepository: openMF/android-client
Length of output: 1461
🏁 Script executed:
# Let's also search for the MifosProgressIndicator composable to understand its signature
rg "fun MifosProgressIndicator" -A 5Repository: openMF/android-client
Length of output: 4285
🏁 Script executed:
# Check the imports in the file to understand current state
head -50 "feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/FixedDepositAccountScreen.kt" | cat -nRepository: openMF/android-client
Length of output: 2983
Use Modifier.weight(1f) with fillMaxWidth() to prevent overflow from breadcrumb.
Both MifosProgressIndicator() and the inner Column default to fillMaxSize(), which fills the entire screen height without accounting for the breadcrumb. This causes layout overflow in both loading and content branches. Apply Modifier.weight(1f).fillMaxWidth() to each branch so they only consume the remaining space after the breadcrumb.
♻️ Proposed fix
-import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.layout.weight- when (state.isLoading) {
- true -> MifosProgressIndicator()
- false -> {
- Column(
- Modifier.fillMaxSize()
- .padding(
- horizontal = DesignToken.padding.large,
- ),
- ) {
+ when (state.isLoading) {
+ true -> MifosProgressIndicator(
+ modifier = Modifier
+ .weight(1f)
+ .fillMaxWidth(),
+ )
+ false -> {
+ Column(
+ modifier = Modifier
+ .weight(1f)
+ .fillMaxWidth()
+ .padding(
+ horizontal = DesignToken.padding.large,
+ ),
+ ) {🤖 Prompt for AI Agents
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/FixedDepositAccountScreen.kt`
around lines 137 - 148, The layout overflows because both the loading branch
(MifosProgressIndicator) and the content branch Column use fillMaxSize() and
thus ignore the space taken by MifosBreadcrumbNavBar; update the branches where
state.isLoading is checked so the MifosProgressIndicator and the inner Column
use Modifier.weight(1f).fillMaxWidth() instead of fillMaxSize(), ensuring they
only occupy the remaining space after MifosBreadcrumbNavBar and prevent
overflow.
...in/kotlin/com/mifos/feature/client/newFixedDepositAccount/CreateFixedDepositAccountScreen.kt
Show resolved
Hide resolved
| if (state.accounts.isNotEmpty()) { | ||
| val emptyText = stringResource(Res.string.string_not_available) | ||
| LazyColumn { | ||
| item { | ||
| state.accounts.forEachIndexed { index, account -> | ||
| MifosActionsShareListingComponent( | ||
| accountNo = account.accountNo ?: emptyText, | ||
| shareProductName = account.shortProductName | ||
| ?: emptyText, | ||
| pendingForApprovalShares = account.totalPendingForApprovalShares, | ||
| approvedShares = account.totalApprovedShares, | ||
| isExpanded = state.currentlyActiveIndex == index && state.isCardActive, | ||
| menuList = (listOf(Actions.ViewAccount())), | ||
| onActionClicked = { actions -> | ||
| when (actions) { | ||
| is Actions.ViewAccount -> { | ||
| onAction( | ||
| ShareAccountsAction.ViewAccount( | ||
| account.id ?: -1, | ||
| ), | ||
| ) | ||
| } | ||
| }, | ||
| onClick = { | ||
| onAction( | ||
| ShareAccountsAction.CardClicked( | ||
| index, | ||
| ), | ||
| ) | ||
| }, | ||
| ) | ||
|
|
||
| Spacer(Modifier.height(DesignToken.padding.small)) | ||
| } | ||
|
|
||
| else -> {} | ||
| } | ||
| }, | ||
| onClick = { | ||
| onAction( | ||
| ShareAccountsAction.CardClicked( | ||
| index, | ||
| ), | ||
| ) | ||
| }, | ||
| ) | ||
|
|
||
| Spacer(Modifier.height(DesignToken.padding.small)) | ||
| } | ||
| } | ||
| } else { | ||
| MifosEmptyCard() | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use itemsIndexed instead of item with forEachIndexed for proper lazy loading.
The current implementation wraps all items inside a single item {} block, which defeats the purpose of LazyColumn. All items will be composed immediately regardless of visibility, negating lazy loading benefits and potentially causing performance issues with large account lists.
🔧 Proposed fix
if (state.accounts.isNotEmpty()) {
val emptyText = stringResource(Res.string.string_not_available)
- LazyColumn {
- item {
- state.accounts.forEachIndexed { index, account ->
- MifosActionsShareListingComponent(
- accountNo = account.accountNo ?: emptyText,
- shareProductName = account.shortProductName
- ?: emptyText,
- pendingForApprovalShares = account.totalPendingForApprovalShares,
- approvedShares = account.totalApprovedShares,
- isExpanded = state.currentlyActiveIndex == index && state.isCardActive,
- menuList = (listOf(Actions.ViewAccount())),
- onActionClicked = { actions ->
- when (actions) {
- is Actions.ViewAccount -> {
- onAction(
- ShareAccountsAction.ViewAccount(
- account.id ?: -1,
- ),
- )
- }
-
- else -> {}
- }
- },
- onClick = {
- onAction(
- ShareAccountsAction.CardClicked(
- index,
- ),
- )
- },
- )
-
- Spacer(Modifier.height(DesignToken.padding.small))
- }
- }
- }
+ LazyColumn {
+ itemsIndexed(state.accounts) { index, account ->
+ MifosActionsShareListingComponent(
+ accountNo = account.accountNo ?: emptyText,
+ shareProductName = account.shortProductName
+ ?: emptyText,
+ pendingForApprovalShares = account.totalPendingForApprovalShares,
+ approvedShares = account.totalApprovedShares,
+ isExpanded = state.currentlyActiveIndex == index && state.isCardActive,
+ menuList = (listOf(Actions.ViewAccount())),
+ onActionClicked = { actions ->
+ when (actions) {
+ is Actions.ViewAccount -> {
+ onAction(
+ ShareAccountsAction.ViewAccount(
+ account.id ?: -1,
+ ),
+ )
+ }
+
+ else -> {}
+ }
+ },
+ onClick = {
+ onAction(
+ ShareAccountsAction.CardClicked(
+ index,
+ ),
+ )
+ },
+ )
+
+ Spacer(Modifier.height(DesignToken.padding.small))
+ }
+ }
} else {Note: You'll need to add the import:
import androidx.compose.foundation.lazy.itemsIndexed📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (state.accounts.isNotEmpty()) { | |
| val emptyText = stringResource(Res.string.string_not_available) | |
| LazyColumn { | |
| item { | |
| state.accounts.forEachIndexed { index, account -> | |
| MifosActionsShareListingComponent( | |
| accountNo = account.accountNo ?: emptyText, | |
| shareProductName = account.shortProductName | |
| ?: emptyText, | |
| pendingForApprovalShares = account.totalPendingForApprovalShares, | |
| approvedShares = account.totalApprovedShares, | |
| isExpanded = state.currentlyActiveIndex == index && state.isCardActive, | |
| menuList = (listOf(Actions.ViewAccount())), | |
| onActionClicked = { actions -> | |
| when (actions) { | |
| is Actions.ViewAccount -> { | |
| onAction( | |
| ShareAccountsAction.ViewAccount( | |
| account.id ?: -1, | |
| ), | |
| ) | |
| } | |
| }, | |
| onClick = { | |
| onAction( | |
| ShareAccountsAction.CardClicked( | |
| index, | |
| ), | |
| ) | |
| }, | |
| ) | |
| Spacer(Modifier.height(DesignToken.padding.small)) | |
| } | |
| else -> {} | |
| } | |
| }, | |
| onClick = { | |
| onAction( | |
| ShareAccountsAction.CardClicked( | |
| index, | |
| ), | |
| ) | |
| }, | |
| ) | |
| Spacer(Modifier.height(DesignToken.padding.small)) | |
| } | |
| } | |
| } else { | |
| MifosEmptyCard() | |
| } | |
| if (state.accounts.isNotEmpty()) { | |
| val emptyText = stringResource(Res.string.string_not_available) | |
| LazyColumn { | |
| itemsIndexed(state.accounts) { index, account -> | |
| MifosActionsShareListingComponent( | |
| accountNo = account.accountNo ?: emptyText, | |
| shareProductName = account.shortProductName | |
| ?: emptyText, | |
| pendingForApprovalShares = account.totalPendingForApprovalShares, | |
| approvedShares = account.totalApprovedShares, | |
| isExpanded = state.currentlyActiveIndex == index && state.isCardActive, | |
| menuList = (listOf(Actions.ViewAccount())), | |
| onActionClicked = { actions -> | |
| when (actions) { | |
| is Actions.ViewAccount -> { | |
| onAction( | |
| ShareAccountsAction.ViewAccount( | |
| account.id ?: -1, | |
| ), | |
| ) | |
| } | |
| else -> {} | |
| } | |
| }, | |
| onClick = { | |
| onAction( | |
| ShareAccountsAction.CardClicked( | |
| index, | |
| ), | |
| ) | |
| }, | |
| ) | |
| Spacer(Modifier.height(DesignToken.padding.small)) | |
| } | |
| } |
🤖 Prompt for AI Agents
In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/shareAccounts/ShareAccountsScreen.kt`
around lines 104 - 142, The LazyColumn currently uses item {
state.accounts.forEachIndexed { ... } } which forces all rows to compose
eagerly; replace that block with LazyColumn { itemsIndexed(state.accounts) {
index, account -> ... } } so each account is produced lazily and keep the same
inner usage of MifosActionsShareListingComponent and the existing
onClick/onAction handling (ShareAccountsAction.ViewAccount /
ShareAccountsAction.CardClicked); also add the import
androidx.compose.foundation.lazy.itemsIndexed.
| MifosScaffold( | ||
| title = stringResource(Res.string.feature_note_notes), | ||
| onBackPressed = { | ||
| onAction(NoteAction.NavigateBack) | ||
| }, | ||
| snackbarHostState = snackBarHostState, | ||
| modifier = modifier, | ||
| ) { paddingValues -> | ||
| ) { | ||
| Column( | ||
| modifier = Modifier | ||
| .padding(paddingValues) | ||
| .fillMaxSize(), | ||
| ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find MifosScaffold definition
find . -type f -name "*.kt" | xargs grep -l "fun MifosScaffold" | head -5Repository: openMF/android-client
Length of output: 243
🏁 Script executed:
#!/bin/bash
# Check MifosScaffold implementation with context
rg -n "fun MifosScaffold" --type=kt -A 15 | head -100Repository: openMF/android-client
Length of output: 91
🏁 Script executed:
#!/bin/bash
# Check how MifosScaffold is used in the NoteScreen.kt file specifically
rg -n -B 2 -A 10 "MifosScaffold" feature/note/src/commonMain/kotlin/com/mifos/feature/note/notes/NoteScreen.ktRepository: openMF/android-client
Length of output: 1232
🏁 Script executed:
#!/bin/bash
# Find other uses of MifosScaffold in similar screen files
rg -n "MifosScaffold\(" --type=kt -A 5 | grep -E "(MifosScaffold|modifier|paddingValues)" | head -50Repository: openMF/android-client
Length of output: 91
🏁 Script executed:
#!/bin/bash
# Read the designsystem MifosScaffold (the one imported in NoteScreen)
cat -n ./core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosScaffold.ktRepository: openMF/android-client
Length of output: 9786
🏁 Script executed:
#!/bin/bash
# Also check the cmp-navigation version
cat -n ./cmp-navigation/src/commonMain/kotlin/cmp/navigation/components/MifosScaffold.ktRepository: openMF/android-client
Length of output: 9895
🏁 Script executed:
#!/bin/bash
# Find other screens and how they use MifosScaffold
find . -path "*/src/commonMain/kotlin/*" -name "*Screen.kt" -type f | head -10 | while read file; do
echo "=== $file ==="
grep -A 10 "MifosScaffold(" "$file" | head -15
doneRepository: openMF/android-client
Length of output: 3114
Capture and apply scaffold padding values to content.
The MifosScaffold content lambda provides PaddingValues to handle system bar insets. The current implementation ignores this parameter—the lambda should be { paddingValues -> ... } and the Column should apply .padding(paddingValues). Without this, content renders under status/navigation bars.
Example fix
MifosScaffold(
snackbarHostState = snackBarHostState,
modifier = modifier,
) { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
) {🤖 Prompt for AI Agents
In
`@feature/note/src/commonMain/kotlin/com/mifos/feature/note/notes/NoteScreen.kt`
around lines 156 - 163, The MifosScaffold content lambda currently ignores the
provided PaddingValues causing content (Column) to render under system bars;
update the lambda to accept the padding parameter (e.g., "{ paddingValues -> ...
}") and apply it to the Column modifier via .padding(paddingValues) so the
content respects scaffold insets (change the MifosScaffold lambda and the Column
modifier accordingly).
Fixes - Jira-#633
WhatsApp.Video.2026-01-28.at.8.16.32.PM.mp4
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes & Improvements
✏️ Tip: You can customize this high-level summary in your review settings.