Skip to content

Firebase remote config instance loader #1945

Open
mobilebytesenseicommunity wants to merge 13 commits intoopenMF:developmentfrom
mobilebytesensei:claude/firebase-remote-config-loader-019GTifqCMdZeH5hUVeB4WKX
Open

Firebase remote config instance loader #1945
mobilebytesenseicommunity wants to merge 13 commits intoopenMF:developmentfrom
mobilebytesensei:claude/firebase-remote-config-loader-019GTifqCMdZeH5hUVeB4WKX

Conversation

@mobilebytesenseicommunity
Copy link

@mobilebytesenseicommunity mobilebytesenseicommunity commented Nov 25, 2025

Issue Fix

Fixes #{Issue Number}
Jira Task: Task_Number

Screenshots

Screen.Recording.2025-11-26.at.9.49.40.AM.mov

Description

  • Apply the AndroidStyle.xml style template to your code in Android Studio.

  • Run the unit tests with ./gradlew check to make sure you didn't break anything

  • If you have multiple commits please combine them into one commit by squashing them.

Summary by CodeRabbit

  • New Features
    • Added instance selector UI to switch between Main and Interbank servers (select, preview, update).
    • Gesture activation: five-tap sequence opens the instance selector.
    • Persistent server selections with cloud-driven configuration and offline fallback.
    • New user-facing labels and messages for instance selection and update actions.

…switching

- Add GitLive Firebase Remote Config SDK dependency
- Create ServerInstance and InstancesConfig data models
- Implement FirebaseInstanceConfigLoader to fetch instance configurations
- Add InstanceConfigManager to manage selected instance
- Update BaseURL to use dynamic configuration instead of hardcoded values
- Update KtorInterceptor to use dynamic tenant ID from selected instance
- Add datastore support for storing selected instance preferences
- Create InstanceSelectorScreen with gesture detection (5 taps)
- Add InstanceSelectorViewModel to manage instance selection
- Integrate gesture detector in MifosApp to show instance selector dialog

This feature allows users to dynamically switch between multiple backend instances
without rebuilding the app. Instance configurations are fetched from Firebase Remote
Config using a single parameter "instances_config_v1" containing JSON with instance details.

Users can trigger the instance selector by tapping 5 times anywhere on the screen.
- Add InstanceType enum (MAIN, INTERBANK) to ServerInstance model
- Update InstancesConfig with helper methods to filter by instance type
- Extend datastore to store both selectedInstance and selectedInterbankInstance
- Update InstanceConfigManager to handle both main and interbank instances
- Add DEFAULT_INTERBANK_INSTANCE with apis.flexcore.mx configuration
- Update BaseURL.interBankUrl to use dynamic configuration from InstanceConfigManager
- Update FirebaseInstanceConfigLoader with default instances for both types
- Redesign InstanceSelectorScreen to display main and interbank instances separately
- Update InstanceSelectorViewModel to handle selection of both instance types
- Users can now independently select main server and interbank server instances

Firebase Remote Config JSON format now supports:
{
  "instances": [
    {
      "endpoint": "mifos-bank-2.mifos.community",
      "protocol": "https://",
      "path": "/fineract-provider/api/v1/",
      "platformTenantId": "mifos-bank-2",
      "label": "Production Main",
      "type": "MAIN",
      "isDefault": true
    },
    {
      "endpoint": "apis.flexcore.mx",
      "protocol": "https://",
      "path": "/v1.0/vnext1/",
      "platformTenantId": "mifos-bank-2",
      "label": "Production Interbank",
      "type": "INTERBANK",
      "isDefault": true
    }
  ]
}
@coderabbitai
Copy link

coderabbitai bot commented Nov 25, 2025

📝 Walkthrough

Walkthrough

Adds a multi-platform server instance selection feature: gesture-triggered bottom sheet UI, ViewModel and persistence for selected main/interbank instances, Firebase remote-config loading of instance lists, runtime network wiring to use chosen instances, and related model, datastore, DI, build, and resource updates.

Changes

Cohort / File(s) Summary
Models
core/model/src/commonMain/kotlin/org/mifospay/core/model/instance/ServerInstance.kt, core/model/src/commonMain/kotlin/org/mifospay/core/model/instance/InstancesConfig.kt
New serializable ServerInstance and InstancesConfig types with helpers to filter defaults and by type.
Datastore / Repository
core/datastore/.../UserPreferencesDataSource.kt, core/datastore/.../UserPreferencesRepository.kt, core/datastore/.../UserPreferencesRepositoryImpl.kt
Adds selectedInstance and selectedInterbankInstance state flows plus update methods and persistence helpers for storing ServerInstance selections.
Remote config loader
core/network/src/commonMain/kotlin/.../InstanceConfigLoader.kt, core/network/build.gradle.kts
New InstanceConfigLoader interface and FirebaseInstanceConfigLoader implementation; adds firebase-config dependency and default/fallback handling.
Instance config manager
core/network/src/commonMain/kotlin/.../InstanceConfigManager.kt
New InstanceConfigManager exposing selected instances, fallbacks, and URL/component helpers (getEndpoint/getUrl/getPlatformTenantId/etc.).
Network wiring / utils
core/network/src/commonMain/kotlin/.../di/NetworkModule.kt, core/network/src/commonMain/kotlin/.../utils/BaseURL.kt, core/network/src/commonMain/kotlin/.../utils/KtorInterceptor.kt
Refactors BaseURL to a class delegating to InstanceConfigManager; injects InstanceConfigLoader and InstanceConfigManager into DI; updates Ktor interceptor to use configManager and header constants.
UI, gesture & viewmodel
cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/GestureDetector.kt, .../InstanceSelectorScreen.kt, .../InstanceSelectorViewModel.kt
Adds Modifier.detectInstanceSelectorGesture (five-tap detector), InstanceSelectorViewModel (combines remote config + prefs into UI state), and InstanceSelectorScreen composable with grouped instance lists and update flow.
Integration & DI
cmp-shared/src/commonMain/kotlin/org/mifospay/shared/di/KoinModules.kt, cmp-shared/src/commonMain/kotlin/org/mifospay/shared/MifosPayApp.kt
Registers InstanceSelectorViewModel in Koin; integrates gesture detector and shows the sheet in MifosPayApp when triggered (login graph).
Resources & deps
gradle/libs.versions.toml, cmp-android/dependencies/prodReleaseRuntimeClasspath.txt, cmp-ios/Podfile, cmp-shared/.../strings.xml
Version catalog and dependency bumps (adds gitlive-firebase-config), Firebase pods for iOS, and new string resources for the instance selector UI.
Build / misc
build-logic/.../KotlinAndroid.kt, cmp-shared/build.gradle.kts, cmp-android/prodRelease-badging.txt
Minor compileSdk bumps and version/manifest metadata updates.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant App as MifosPayApp
    participant Detector as GestureDetector
    participant Screen as InstanceSelectorScreen
    participant VM as InstanceSelectorViewModel
    participant ConfigLoader as FirebaseInstanceConfigLoader
    participant Preferences as UserPreferencesRepository
    participant Network as InstanceConfigManager

    User->>Detector: Five quick taps
    Detector->>App: onGestureDetected
    App->>Screen: show InstanceSelectorScreen
    Screen->>VM: collect state & observe events
    VM->>ConfigLoader: observeInstancesConfig()
    ConfigLoader-->>VM: InstancesConfig (DataState)
    VM->>Preferences: observe selectedInstance / selectedInterbankInstance
    Preferences-->>VM: StateFlow<ServerInstance?>
    VM->>VM: combine -> InstanceSelectorState
    VM-->>Screen: render grouped list
    User->>Screen: select instance + tap Update
    Screen->>VM: handleAction(UpdateInstances)
    VM->>Preferences: updateSelectedInstance(...)
    Preferences-->>VM: DataState.Success
    VM->>VM: emit DismissSheet
    VM-->>Screen: Dismiss event
    Screen->>App: onDismiss
    App->>App: hide sheet
    App->>Network: InstanceConfigManager now reports selected instance for network calls
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Feature interbank Transfer #1941: Overlaps with network configuration and DI changes (BaseURL, KtorInterceptor, NetworkModule) and likely related to centralized instance/config management.

Suggested reviewers

  • HekmatullahAmin

Poem

🐰 I tapped five times beneath the moonlit screen,
Firebase hummed, brought instances unseen.
Preferences tucked choices snug and neat,
Network threads now find the URL complete.
A tiny hop for code — a joyful, nimble scene!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.06% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Firebase remote config instance loader' clearly summarizes the main change: introduction of a Firebase-backed configuration loader for server instances. The title is specific, concise, and directly reflects the primary feature added in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (7)
cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/GestureDetector.kt (1)

22-47: Consider build-type restrictions for this feature.

The 5-tap gesture to access server instance switching is a development/debug feature that could be accidentally triggered by users or misused in production. Consider restricting this gesture to debug/staging builds only.

core/network/src/commonMain/kotlin/org/mifospay/core/network/di/NetworkModule.kt (1)

92-92: Hardcoded host apis.flexcore.mx may conflict with dynamic instance configuration.

Since this PR introduces dynamic server instance selection, having hardcoded hosts in loggableHosts may be inconsistent. Consider deriving this from the config manager or removing it if no longer needed.

cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/InstanceSelectorScreen.kt (2)

60-65: Hardcoded strings should use string resources for i18n support.

The strings "Select Instance", "Close", "Loading instances...", "Main Server Instances", "Interbank Server Instances", and "Tenant:" should be defined as string resources for proper internationalization.


101-114: Consider adding a retry mechanism for the error state.

When an error occurs, users have no way to retry loading instances. Adding a retry button would improve the user experience.

             is InstanceSelectorUiState.Error -> {
                 Column(
                     modifier = Modifier
                         .fillMaxSize()
                         .padding(paddingValues),
                     horizontalAlignment = Alignment.CenterHorizontally,
                     verticalArrangement = Arrangement.Center,
                 ) {
                     Text(
                         text = "Error: ${state.message}",
                         color = MaterialTheme.colorScheme.error,
                     )
+                    Spacer(modifier = Modifier.height(16.dp))
+                    Button(onClick = { viewModel.loadInstances() }) {
+                        Text("Retry")
+                    }
                 }
             }
cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/InstanceSelectorViewModel.kt (1)

68-70: Redundant Loading state handling.

The DataState.Loading branch sets the UI state to Loading, but this is already set at line 50 before the suspend call. If fetchInstancesConfig() is a suspend function that returns a final result (Success/Error), it should never return Loading. If it can return Loading, consider whether this pattern is intended.

If fetchInstancesConfig() always returns a terminal state, this branch can be removed to simplify the code.

core/network/src/commonMain/kotlin/org/mifospay/core/network/config/InstanceConfigLoader.kt (1)

35-61: Surface Remote Config failures and avoid duplicating default configs

Right now any exception, blank JSON, or empty instances list is treated as DataState.Success(DEFAULT_CONFIG), with the original failure completely hidden. This can make diagnosing misconfigured Remote Config or serialization issues difficult, and callers can’t distinguish “remote defaulted” from “locally defaulted”.

Additionally, the default main/interbank instances here duplicate the defaults defined in InstanceConfigManager. If one is updated and the other isn’t, you’ll get divergent behavior depending on the code path.

Consider:

  • Logging or otherwise surfacing the exception (and optionally using a DataState.Error that still carries DEFAULT_CONFIG as a fallback payload).
  • Centralizing the default instance definitions (e.g., via a shared InstancesConfig.DEFAULT/factory or by reusing InstanceConfigManager’s defaults) so there is a single canonical source of truth.

Also applies to: 71-95

core/network/src/commonMain/kotlin/org/mifospay/core/network/config/InstanceConfigManager.kt (1)

20-42: De-duplicate default instances and harden self-service URL composition

InstanceConfigManager provides a nice single point of access for instance-derived URLs and tenant IDs. Two refinement points:

  • The DEFAULT_MAIN_INSTANCE / DEFAULT_INTERBANK_INSTANCE definitions here duplicate the defaults in FirebaseInstanceConfigLoader. Consider extracting a shared default definition (e.g., in the model layer) so both the loader and manager depend on the same constants and can’t drift independently.

  • getSelfServiceUrl() assumes path already includes a trailing /. To be more robust against future Remote Config changes, you could base this on fullUrl (e.g., getCurrentInstance().fullUrl + "self/") or normalise path to ensure exactly one / between segments.

Also applies to: 44-71

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e5f56f and c309eb0.

📒 Files selected for processing (17)
  • cmp-shared/src/commonMain/kotlin/org/mifospay/shared/di/KoinModules.kt (2 hunks)
  • cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/GestureDetector.kt (1 hunks)
  • cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/InstanceSelectorScreen.kt (1 hunks)
  • cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/InstanceSelectorViewModel.kt (1 hunks)
  • cmp-shared/src/commonMain/kotlin/org/mifospay/shared/ui/MifosApp.kt (5 hunks)
  • core/datastore/src/commonMain/kotlin/org/mifospay/core/datastore/UserPreferencesDataSource.kt (5 hunks)
  • core/datastore/src/commonMain/kotlin/org/mifospay/core/datastore/UserPreferencesRepository.kt (3 hunks)
  • core/datastore/src/commonMain/kotlin/org/mifospay/core/datastore/UserPreferencesRepositoryImpl.kt (3 hunks)
  • core/model/src/commonMain/kotlin/org/mifospay/core/model/instance/InstancesConfig.kt (1 hunks)
  • core/model/src/commonMain/kotlin/org/mifospay/core/model/instance/ServerInstance.kt (1 hunks)
  • core/network/build.gradle.kts (1 hunks)
  • core/network/src/commonMain/kotlin/org/mifospay/core/network/config/InstanceConfigLoader.kt (1 hunks)
  • core/network/src/commonMain/kotlin/org/mifospay/core/network/config/InstanceConfigManager.kt (1 hunks)
  • core/network/src/commonMain/kotlin/org/mifospay/core/network/di/NetworkModule.kt (3 hunks)
  • core/network/src/commonMain/kotlin/org/mifospay/core/network/utils/BaseURL.kt (1 hunks)
  • core/network/src/commonMain/kotlin/org/mifospay/core/network/utils/KtorInterceptor.kt (5 hunks)
  • gradle/libs.versions.toml (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/InstanceSelectorScreen.kt (1)
core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/KptTheme.kt (1)
  • KptTheme (55-69)
cmp-shared/src/commonMain/kotlin/org/mifospay/shared/ui/MifosApp.kt (1)
cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/InstanceSelectorScreen.kt (1)
  • InstanceSelectorScreen (48-117)
core/network/src/commonMain/kotlin/org/mifospay/core/network/di/NetworkModule.kt (2)
core-base/network/src/commonMain/kotlin/org/mifos/corebase/network/KtorHttpClient.kt (1)
  • setupDefaultHttpClient (67-162)
core/network/src/commonMain/kotlin/org/mifospay/core/network/utils/KtorInterceptor.kt (2)
  • install (33-52)
  • install (76-97)
🔇 Additional comments (15)
cmp-shared/src/commonMain/kotlin/org/mifospay/shared/di/KoinModules.kt (1)

48-69: LGTM!

The ViewModel registration follows the existing pattern and correctly wires InstanceSelectorViewModel into the shared Koin module.

core/network/build.gradle.kts (1)

41-41: LGTM!

The Firebase Remote Config dependency is correctly added to the commonMain source set, consistent with other gitlive Firebase dependencies in the project.

core/model/src/commonMain/kotlin/org/mifospay/core/model/instance/ServerInstance.kt (1)

19-31: LGTM!

The ServerInstance data class is well-structured with sensible defaults and a useful fullUrl computed property.

cmp-shared/src/commonMain/kotlin/org/mifospay/shared/ui/MifosApp.kt (1)

174-184: LGTM!

The Dialog integration correctly manages the show/dismiss state and renders the InstanceSelectorScreen appropriately.

cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/InstanceSelectorScreen.kt (2)

48-117: LGTM overall!

The screen structure with state handling for Loading, Success, and Error states follows good practices. The UI composition is clean and well-organized.


174-219: LGTM!

The InstanceItem component is well-designed with clear visual hierarchy showing label, URL, and tenant information alongside the selection radio button.

core/network/src/commonMain/kotlin/org/mifospay/core/network/di/NetworkModule.kt (1)

32-46: Unfortunately, I'm unable to access the repository to verify this concern directly. However, based on the code snippet provided and the nature of the concern, I can offer my assessment:

The review comment raises a valid architectural question. The concern states that InstanceConfigLoader is registered as a singleton but InstanceConfigManager only receives userPreferencesRepository as a dependency.

Without being able to inspect the actual InstanceConfigManager class definition, I cannot definitively confirm whether:

  1. The loader is intentionally managed separately (e.g., lazy-loaded or initialized internally)
  2. The loader is truly orphaned and should be injected
  3. The manager derives its config through userPreferencesRepository rather than directly from the loader

Given the constraints, I recommend:

For the developer to verify this locally:

  • Check if InstanceConfigManager internally instantiates or uses InstanceConfigLoader
  • Confirm whether userPreferencesRepository provides the configuration data that would otherwise come from the loader
  • If the loader is unused, consider either removing it or injecting it into InstanceConfigManager

InstanceConfigLoader is registered but its relationship to InstanceConfigManager requires verification. The singleton is declared but InstanceConfigManager receives only userPreferencesRepository. Confirm whether the manager needs InstanceConfigLoader as an explicit dependency or if its configuration needs are satisfied through the preferences repository.

gradle/libs.versions.toml (1)

223-223: Library version verified.

The dev.gitlive:firebase-config:2.1.0 is a valid published version available on Maven Central Repository. The library entry follows the existing pattern for gitlive dependencies and uses the correct version reference.

core/model/src/commonMain/kotlin/org/mifospay/core/model/instance/InstancesConfig.kt (1)

14-29: Clean model implementation with appropriate filtering logic.

The data class is well-designed with immutable properties and functional helper methods. Using firstOrNull gracefully handles the case where no default exists.

One minor consideration: if the remote config contains multiple instances marked as isDefault=true for the same type, only the first one will be returned. This is likely acceptable behavior, but consider adding validation or logging if strict single-default enforcement is needed.

core/datastore/src/commonMain/kotlin/org/mifospay/core/datastore/UserPreferencesRepository.kt (1)

36-53: Interface additions follow existing patterns consistently.

The new properties and methods align well with the existing API surface. Using StateFlow<ServerInstance?> for observable selections and DataState<Unit> return types for updates maintains consistency with other repository methods.

core/datastore/src/commonMain/kotlin/org/mifospay/core/datastore/UserPreferencesRepositoryImpl.kt (2)

76-88: StateFlow properties follow existing conventions.

The new selectedInstance and selectedInterbankInstance properties are implemented consistently with other StateFlow properties in the class, using the same scope, initial value, and sharing strategy.


100-116: Update methods are consistent with existing patterns.

The implementation follows the same try/catch pattern as other update methods in this repository. While catching generic Exception is broad, this maintains consistency with the existing codebase style.

core/network/src/commonMain/kotlin/org/mifospay/core/network/utils/BaseURL.kt (1)

14-34: ---

Breaking change from singleton to DI-injectable class—verify all usages before merging.

The transition from a Kotlin object to a constructor-injected class enables dynamic server configuration. The delegation to InstanceConfigManager centralizes endpoint resolution logic appropriately.

However, this is a breaking change requiring all call sites to be updated. Verification is needed to confirm:

  • All BaseURL usages have been migrated to dependency injection patterns
  • No singleton-style direct accesses (BaseURL.url, BaseURL.selfServiceUrl, etc.) remain in the codebase
  • DI container is properly wired to provide BaseURL instances
core/network/src/commonMain/kotlin/org/mifospay/core/network/config/InstanceConfigLoader.kt (1)

23-26: Interface and loader wiring look coherent

The InstanceConfigLoader abstraction and FirebaseInstanceConfigLoader implementation are straightforward, DI‑friendly, and keep the Remote Config setup localized. The one‑shot observeInstancesConfig() wrapper around fetchInstancesConfig() is also consistent with the current “fetch on demand” behavior.

Also applies to: 28-34, 64-69, 98-100

core/network/src/commonMain/kotlin/org/mifospay/core/network/utils/KtorInterceptor.kt (1)

21-26: Dynamic tenant header integration via InstanceConfigManager looks correct

Wiring InstanceConfigManager into both KtorInterceptor and KtorInterceptorRe and using getPlatformTenantId() for HEADER_TENANT aligns the HTTP layer with the active instance configuration. The new Config/ConfigRe fields continue the existing lateinit pattern, so as long as configManager is always set when installing the plugins, this should behave as intended.

Also applies to: 33-39, 54-57, 61-64, 66-69, 76-83, 99-102, 106-109

therajanmaurya and others added 2 commits November 26, 2025 01:51
- Remove remoteConfigSettings import (not available in GitLive SDK)
- Change settings configuration to use suspend function
- Update getString() to use bracket notation with asString()
- Move settings configuration inside fetchInstancesConfig() method

This fixes compilation errors with the GitLive Firebase SDK.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
core/network/src/commonMain/kotlin/org/mifospay/core/network/config/InstanceConfigLoader.kt (2)

27-32: Consider injecting dependencies for testability.

The remoteConfig is directly accessed from Firebase.remoteConfig singleton, making unit testing difficult. Consider injecting the remote config instance via constructor to allow mocking in tests.

-class FirebaseInstanceConfigLoader : InstanceConfigLoader {
-    private val remoteConfig = Firebase.remoteConfig
+class FirebaseInstanceConfigLoader(
+    private val remoteConfig: FirebaseRemoteConfig = Firebase.remoteConfig,
+) : InstanceConfigLoader {
     private val json = Json {
         ignoreUnknownKeys = true
         isLenient = true
     }

95-97: observeInstancesConfig doesn't actually observe changes.

The method name suggests continuous observation, but it only emits once and completes. Firebase Remote Config supports real-time updates via addOnConfigUpdateListener. If continuous observation isn't needed, consider renaming to fetchInstancesConfigAsFlow() to clarify behavior.

If one-shot flow is intentional, rename for clarity:

-    override fun observeInstancesConfig(): Flow<DataState<InstancesConfig>> = flow {
+    override fun fetchInstancesConfigAsFlow(): Flow<DataState<InstancesConfig>> = flow {
         emit(fetchInstancesConfig())
     }

Or implement true observation using callbackFlow with Remote Config's update listener if real-time updates are desired.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 464256e and 7b5377f.

📒 Files selected for processing (1)
  • core/network/src/commonMain/kotlin/org/mifospay/core/network/config/InstanceConfigLoader.kt (1 hunks)
🔇 Additional comments (3)
core/network/src/commonMain/kotlin/org/mifospay/core/network/config/InstanceConfigLoader.kt (3)

1-21: LGTM!

License header and imports are appropriate for the Firebase Remote Config functionality.


22-25: LGTM!

Clean interface design with appropriate separation between one-shot fetch and reactive observation patterns.


34-61: LGTM!

The default configuration provides sensible fallback values. The fetch interval of 1 hour is reasonable for Remote Config. Having hardcoded defaults ensures the app remains functional even if Remote Config fails.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

♻️ Duplicate comments (4)
core/network/src/commonMain/kotlin/org/mifospay/core/network/di/NetworkModule.kt (1)

84-88: Hardcoded credentials - already flagged in previous review.

core/network/src/commonMain/kotlin/org/mifospay/core/network/config/InstanceConfigLoader.kt (2)

82-112: Error handling masks failures and settings are reconfigured on every fetch.

As previously noted, this method has several issues:

  1. All errors return DataState.Success(DEFAULT_CONFIG), making it impossible for callers to distinguish successful remote fetch from fallback due to errors
  2. Exceptions are silently swallowed without logging
  3. remoteConfig.settings is called on every fetch instead of once at initialization

82-112: Add logging to enable debugging of config fetch failures.

The catch block silently returns DEFAULT_CONFIG with no logging. In production, there would be no visibility into:

  • Whether remote config fetch succeeded or failed
  • Why it failed (network, parsing, Firebase SDK issues)
  • That fallback config is being used

Consider adding error logging in the catch block:

 } catch (e: Exception) {
+    // Log error for production debugging
+    println("Failed to fetch remote config, using defaults: ${e.message}")
     // Return default config on error
     DataState.Success(DEFAULT_CONFIG)
 }
cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/InstanceSelectorViewModel.kt (1)

116-140: Persistence failures are unhandled, leading to data inconsistency.

As previously noted, the results of updateSelectedInstance and updateSelectedInterbankInstance are discarded. If persistence fails:

  1. Temp selections are still reset (lines 135-136)
  2. Sheet is still dismissed (line 138)
  3. User believes their selection was saved, but it wasn't

On app restart, the previous selection would reappear, creating a confusing UX.

🧹 Nitpick comments (8)
core/network/src/commonMain/kotlin/org/mifospay/core/network/utils/KtorInterceptor.kt (1)

64-100: Consider extracting common interceptor logic to reduce duplication.

Both KtorInterceptor and KtorInterceptorRe have nearly identical install() implementations for setting headers and handling unauthorized responses. The only difference is how the token is obtained.

Consider a shared helper or base implementation:

private fun HttpClient.installCommonHeaders(configManager: InstanceConfigManager) {
    requestPipeline.intercept(HttpRequestPipeline.State) {
        context.header(BaseURL.HEADER_CONTENT_TYPE, BaseURL.HEADER_CONTENT_TYPE_VALUE)
        context.header(BaseURL.HEADER_ACCEPT, BaseURL.HEADER_ACCEPT_VALUE)
        context.header(BaseURL.HEADER_TENANT, configManager.getPlatformTenantId())
    }
}

private fun HttpClient.installUnauthorizedHandler() {
    responsePipeline.intercept(HttpResponsePipeline.After) {
        if (context.response.status == HttpStatusCode.Unauthorized) {
            GlobalAuthManager.markUnauthorized()
        }
        proceedWith(subject)
    }
}
cmp-shared/src/commonMain/kotlin/org/mifospay/shared/MifosPayApp.kt (2)

54-55: Inconsistent state declaration patterns.

Line 54 uses var showErrorDialog = remember { mutableStateOf<Boolean>(false) } requiring .value access, while line 55 uses var showInstanceSelector by remember { mutableStateOf(false) } with delegate syntax. For consistency, both should use the same pattern.

-    var showErrorDialog = remember { mutableStateOf<Boolean>(false) }
+    var showErrorDialog by remember { mutableStateOf(false) }

Then update references from showErrorDialog.value to just showErrorDialog.


93-100: Redundant MifosTheme wrapper.

InstanceSelectorScreen is already wrapped in MifosTheme here (lines 95-99), but the screen will be displayed while the main MifosTheme at line 102 is also active. This nested theming is likely unnecessary and could cause subtle styling inconsistencies.

     // Show instance selector dialog
     if (showInstanceSelector) {
-        MifosTheme {
-            InstanceSelectorScreen(
-                onDismiss = { showInstanceSelector = false },
-            )
-        }
+        InstanceSelectorScreen(
+            onDismiss = { showInstanceSelector = false },
+        )
     }
cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/InstanceSelectorScreen.kt (2)

77-82: Modifier applied to both parent and child may cause unintended layout behavior.

The same modifier is passed to both MifosBottomSheet (line 79) and MifosScaffold (line 82). This can lead to doubled padding, sizing constraints, or other layout issues depending on what modifiers are applied externally.

Typically, the outer container should receive the external modifier:

     MifosBottomSheet(
         onDismiss = onDismiss,
         modifier = modifier,
     ) {
         MifosScaffold(
-            modifier = modifier,
+            modifier = Modifier,
             bottomBar = {

337-355: Inconsistent theme usage in previews.

InstanceItemPreview uses KptTheme while InstanceItemUnselectedPreview and InstancesListPreview use MifosTheme. Previews should use consistent theming to accurately represent how components will appear in the app.

 @Preview
 @Composable
 private fun InstanceItemPreview() {
-    KptTheme {
+    MifosTheme {
         InstanceItem(

Also applies to: 357-375

core/network/src/commonMain/kotlin/org/mifospay/core/network/utils/BaseURL.kt (1)

14-16: Add KDoc to document the class purpose and usage.

Since BaseURL is a public class and represents a significant refactor from a singleton to a DI-friendly component, consider adding KDoc to explain its role in providing runtime-configurable base URLs for network requests.

core/network/src/commonMain/kotlin/org/mifospay/core/network/config/InstanceConfigLoader.kt (1)

114-116: Method name suggests continuous observation but implementation fetches once.

The name observeInstancesConfig() implies the Flow will emit updates when remote config changes, but it only fetches once and completes. Consider either:

  • Renaming to fetchInstancesConfigAsFlow() to clarify the one-shot behavior, or
  • Implementing true observation of Remote Config updates if Firebase supports listeners
cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/InstanceSelectorViewModel.kt (1)

165-169: Remove unused ConfigLoaded action.

The ConfigLoaded action is defined but never dispatched. Config loading is handled directly in the init block via combine (lines 41-42), not through the action handling system. The case at lines 110-112 is effectively dead code.

 sealed interface InstanceSelectorAction {
     data class SelectInstance(val instance: ServerInstance) : InstanceSelectorAction
     data object UpdateInstances : InstanceSelectorAction
-    data class ConfigLoaded(val config: DataState<InstancesConfig>) : InstanceSelectorAction
 }

And remove the no-op handler:

         is InstanceSelectorAction.UpdateInstances -> {
             updateInstances()
         }
-
-        is InstanceSelectorAction.ConfigLoaded -> {
-            // Handled by init block
-        }
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b5377f and df2d3a2.

📒 Files selected for processing (10)
  • cmp-android/dependencies/prodReleaseRuntimeClasspath.txt (5 hunks)
  • cmp-android/prodRelease-badging.txt (1 hunks)
  • cmp-shared/src/commonMain/composeResources/values/strings.xml (1 hunks)
  • cmp-shared/src/commonMain/kotlin/org/mifospay/shared/MifosPayApp.kt (4 hunks)
  • cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/InstanceSelectorScreen.kt (1 hunks)
  • cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/InstanceSelectorViewModel.kt (1 hunks)
  • core/network/src/commonMain/kotlin/org/mifospay/core/network/config/InstanceConfigLoader.kt (1 hunks)
  • core/network/src/commonMain/kotlin/org/mifospay/core/network/di/NetworkModule.kt (3 hunks)
  • core/network/src/commonMain/kotlin/org/mifospay/core/network/utils/BaseURL.kt (1 hunks)
  • core/network/src/commonMain/kotlin/org/mifospay/core/network/utils/KtorInterceptor.kt (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
core/network/src/commonMain/kotlin/org/mifospay/core/network/di/NetworkModule.kt (1)
core-base/network/src/commonMain/kotlin/org/mifos/corebase/network/KtorHttpClient.kt (1)
  • setupDefaultHttpClient (67-162)
cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/InstanceSelectorViewModel.kt (1)
core-base/ui/src/commonMain/kotlin/template/core/base/ui/BaseViewModel.kt (1)
  • sendEvent (97-99)
🔇 Additional comments (10)
cmp-android/prodRelease-badging.txt (1)

1-131: I encountered a repository clone failure that prevents me from verifying the concerns raised in the review comment. Without access to the codebase, I cannot:

  1. Confirm whether prodRelease-badging.txt is tracked in git or covered by .gitignore
  2. Search for the mentioned classes (InstanceSelectorViewModel, ServerInstance, InstancesConfig, InstanceConfigManager) to verify if source code files exist
  3. Determine whether the actual source files implementing these features are part of this PR or missing

Since the automated verification is blocked and the review raises substantive claims about:

  • Whether a generated artifact should be in version control
  • Whether critical source code files are missing from the review

I must mark this for manual verification.


Auto-generated artifact may need version control review.

The prodRelease-badging.txt file is generated by Android's aapt tool. Verify whether this artifact should be committed to the repository or added to .gitignore. Generated artifacts can create merge conflicts and make diffs harder to review.

Recommendation: Confirm the repository's policy on committing build artifacts like badging files, and adjust .gitignore if necessary.


Verify completeness of source code files for review.

The PR description mentions feature additions (InstanceSelectorViewModel, ServerInstance, InstancesConfig models, and InstanceConfigManager), but only a build artifact was provided for review. Confirm that all relevant source code files implementing these features are included and accessible for review.

cmp-android/dependencies/prodReleaseRuntimeClasspath.txt (3)

1-462: Verify classpath file generation and build consistency.

This file appears to be auto-generated from a Gradle build process. Before merging:

  1. Confirm whether this file should be committed to version control or if it's regenerated during each build
  2. Run ./gradlew clean build and verify the classpath file regenerates consistently
  3. Ensure the PR contributor has completed the full test suite (./gradlew check) as noted in the incomplete checklist

The PR description indicates the contributor checklist is incomplete (unit tests and style validation have not been checked).


456-462: Verify Kotlin 2.2.0 requirement for kotlinx-serialization 1.9.0 upgrade.

The kotlinx-serialization 1.9.0 is a stable release, but it introduces a breaking change: it requires Kotlin 2.2.0 (the 1.8.1 version used Kotlin 2.1.x). Before merging this PR:

  1. Confirm the Kotlin Gradle plugin and compiler have been upgraded to 2.2.0 in this PR
  2. If kotlinx-datetime is used in the codebase, verify compatibility with v0.7.0+ (which removed kotlinx.datetime.Instant in favor of kotlin.time.Instant)
  3. Verify serialization for ServerInstance and InstancesConfig models works after the Kotlin upgrade
  4. Run ./gradlew check to test the upgrade (the PR checklist indicates tests have not yet been executed)

248-248: Based on my verification through web search, I can now provide an updated review comment:


Verify Compose compiler plugin compatibility with Kotlin 2.2.20 upgrade and confirm tests pass.

The Firebase dependency versions are stable and compatible:

  • dev.gitlive:firebase-config:2.4.0 correctly works with firebase-config:22.1.2 (GitLive wrapper forwards to native Firebase SDKs)
  • firebase-abt:21.1.1 aligns with the Firebase Remote Config ecosystem
  • kotlinx-serialization:1.9.0 is compatible with kotlin-stdlib:2.2.20 (both target Kotlin 2.2.x)
  • No public security advisories found for these versions

However, the Kotlin stdlib bump from 2.1.21 to 2.2.20 requires attention:

  • Jetpack Compose UI 1.8.3 predates Kotlin 2.2.20; compatibility depends on using a matching Compose Compiler Gradle plugin version
  • Ensure composeCompilerVersion or equivalent is properly configured for Kotlin 2.2.20
  • The PR description shows unchecked "Run unit tests with ./gradlew check" — verify the full test suite passes, particularly for Compose-related code

Also applies to: 257-257, 301-306, 439-439, 456-462

core/network/src/commonMain/kotlin/org/mifospay/core/network/utils/KtorInterceptor.kt (1)

23-56: LGTM! Config-driven header construction is a good improvement.

The refactor to use InstanceConfigManager for tenant resolution and BaseURL constants for header names improves configurability and reduces hardcoded values.

cmp-shared/src/commonMain/kotlin/org/mifospay/shared/MifosPayApp.kt (1)

108-115: Gesture detection condition may not update reactively during navigation.

The navDestination is computed once from uiState and won't react to actual navigation changes within the graph. If the user navigates from login to another screen without authentication state changes, the gesture will still be active.

Consider using navController.currentBackStackEntryAsState() for reactive navigation state:

val currentEntry by navController.currentBackStackEntryAsState()
val isOnLoginGraph = currentEntry?.destination?.parent?.route == LOGIN_GRAPH
// ...
modifier = modifier.detectInstanceSelectorGesture(
    onGestureDetected = {
        if (isOnLoginGraph) {
            showInstanceSelector = true
        }
    },
),
core/network/src/commonMain/kotlin/org/mifospay/core/network/di/NetworkModule.kt (2)

51-73: LGTM! Config-driven URL and header construction.

The SelfClient now properly sources its base URL from BaseURL and uses InstanceConfigManager for dynamic tenant configuration.


36-45: Based on my verification attempts, I was unable to access the repository due to network/access issues, which prevents me from confirming the actual InstanceConfigManager constructor signature and verifying whether FirebaseInstanceConfigLoader is used elsewhere in the codebase.

However, the original review comment's logic is sound: if FirebaseInstanceConfigLoader is instantiated as a singleton but never injected or referenced, it would indicate an incomplete wiring issue in the dependency injection setup. The concern that Firebase config loading might not actually be wired into the manager is a legitimate one that requires manual verification by examining the actual class definitions and their usage patterns.

InstanceConfigLoader may be instantiated but unused in the DI container.

The review identifies a potential dependency injection wiring issue where FirebaseInstanceConfigLoader is created but InstanceConfigManager doesn't receive it. Verify that:

  • InstanceConfigManager's constructor actually expects a configLoader parameter
  • The Firebase instance loading is intentionally delegated to userPreferencesRepository

If InstanceConfigManager should use the loader, apply the suggested fix. Otherwise, remove the unused FirebaseInstanceConfigLoader declaration.

cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/InstanceSelectorScreen.kt (1)

229-335: Well-structured instance item component.

Good implementation with clear visual hierarchy: label, type badge, default indicator, endpoint, and tenant ID. The clickable card with radio button provides good UX for selection.

core/network/src/commonMain/kotlin/org/mifospay/core/network/utils/BaseURL.kt (1)

27-37: I'll verify the performance concern about repeated property access in the BaseURL class. Let me start by searching for where these properties are accessed.
<function_calls>

#!/bin/bash

Find all usages of BaseURL properties to assess access frequency

First, find the BaseURL class location and imports

rg -nP 'class BaseURL' --type=kotlin

Search for accesses to BaseURL properties

rg -nP '.(url|selfServiceUrl|interBankUrl|fineractPlatformTenantId)\b'
--type=kotlin
-C2
-g '!/test/'


</function_calls>

Now let me examine the configManager implementation to understand what these methods do:
<function_calls>

#!/bin/bash

Find configManager implementation and interface

rg -nP '(interface|class)\s+.*[Cc]onfigManager' --type=kotlin -A5

Search for getUrl, getSelfServiceUrl, etc. implementations

rg -nP 'fun\s+(getUrl|getSelfServiceUrl|getInterbankUrl|getPlatformTenantId)\s*('
--type=kotlin
-A5


</function_calls>

Let me also check if there are any network interceptors or hot paths using these properties:
<function_calls>

#!/bin/bash

Search for network interceptors and their usage patterns

rg -nP '(Interceptor|intercept|OkHttp)' --type=kotlin -C2 | head -100

Look for BaseURL instantiation and injection patterns

rg -nP 'BaseURL' --type=kotlin -C1 | head -100


</function_calls>

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/InstanceSelectorViewModel.kt (2)

38-88: Simplify combine + stateIn + launchIn pipeline

You create a StateFlow via stateIn and then immediately launchIn(viewModelScope) on it, discarding both the StateFlow and the Job. The side‑effects already happen inside the combine block, so the stateIn layer is unused overhead.

You can simplify to a single cold‑flow collection:

-        combine(
+        combine(
             instanceConfigLoader.observeInstancesConfig(),
             userPreferencesRepository.selectedInstance,
             userPreferencesRepository.selectedInterbankInstance,
             tempSelectedMainInstance,
             tempSelectedInterbankInstance,
         ) { remoteConfig, selectedMainInstance, selectedInterbankInstance, tempMainInstance, tempInterbankInstance ->
             // existing when(remoteConfig) { ... } body
-        }.stateIn(
-            scope = viewModelScope,
-            started = SharingStarted.WhileSubscribed(5_000),
-            initialValue = null,
-        ).launchIn(viewModelScope)
+        }.launchIn(viewModelScope)

This keeps the same behavior with less complexity and no unused StateFlow.


115-138: Handle failures when persisting selected instances

updateInstances always dismisses the sheet and clears temps even if the underlying persistence calls fail (either by throwing or by returning an error result). That can leave the user believing the selection was saved when it wasn’t.

At minimum, wrap the updates in runCatching / trycatch, only dismiss on success, and surface an error on failure:

     private fun updateInstances() {
         viewModelScope.launch {
-            val currentState = state
-            val mainInstanceChanged =
-                tempSelectedMainInstance.value != currentState.selectedMainInstance
-            val interbankInstanceChanged =
-                tempSelectedInterbankInstance.value != currentState.selectedInterbankInstance
-
-            if (mainInstanceChanged && tempSelectedMainInstance.value != null) {
-                userPreferencesRepository.updateSelectedInstance(tempSelectedMainInstance.value!!)
-            }
-
-            if (interbankInstanceChanged && tempSelectedInterbankInstance.value != null) {
-                userPreferencesRepository.updateSelectedInterbankInstance(
-                    tempSelectedInterbankInstance.value!!,
-                )
-            }
-
-            // Reset temp selections after update
-            tempSelectedMainInstance.value = null
-            tempSelectedInterbankInstance.value = null
-
-            sendEvent(InstanceSelectorEvent.DismissSheet)
+            val currentState = state
+            val mainInstance = tempSelectedMainInstance.value
+            val interbankInstance = tempSelectedInterbankInstance.value
+
+            val mainInstanceChanged = mainInstance != currentState.selectedMainInstance
+            val interbankInstanceChanged = interbankInstance != currentState.selectedInterbankInstance
+
+            runCatching {
+                if (mainInstanceChanged && mainInstance != null) {
+                    userPreferencesRepository.updateSelectedInstance(mainInstance)
+                }
+                if (interbankInstanceChanged && interbankInstance != null) {
+                    userPreferencesRepository.updateSelectedInterbankInstance(interbankInstance)
+                }
+            }.onSuccess {
+                tempSelectedMainInstance.value = null
+                tempSelectedInterbankInstance.value = null
+                sendEvent(InstanceSelectorEvent.DismissSheet)
+            }.onFailure {
+                // Optionally log and reflect error in state for UI feedback
+                mutableStateFlow.update { it.copy(error = "Failed to update instances") }
+            }
         }
     }

Adjust the error reporting to match your UX, but some form of failure handling here will make the flow more robust.

🧹 Nitpick comments (1)
cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/InstanceSelectorViewModel.kt (1)

163-167: Consider removing unused ConfigLoaded action

InstanceSelectorAction.ConfigLoaded is defined but effectively a no‑op in handleAction, and the configuration is already fully driven from the init combine block.

If there are no actual call sites sending ConfigLoaded, consider dropping this variant to keep the action surface minimal and avoid confusion:

 sealed interface InstanceSelectorAction {
     data class SelectInstance(val instance: ServerInstance) : InstanceSelectorAction
     data object UpdateInstances : InstanceSelectorAction
-    data class ConfigLoaded(val config: DataState<InstancesConfig>) : InstanceSelectorAction
 }

If you plan to use it later, a short KDoc comment explaining its intended role would also help.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between df2d3a2 and 3293a55.

📒 Files selected for processing (2)
  • cmp-android/prodRelease-badging.txt (1 hunks)
  • cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/InstanceSelectorViewModel.kt (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/InstanceSelectorViewModel.kt (1)
core-base/ui/src/commonMain/kotlin/template/core/base/ui/BaseViewModel.kt (1)
  • sendEvent (97-99)
🔇 Additional comments (1)
cmp-android/prodRelease-badging.txt (1)

1-1: Version bump looks fine

The change here is just the versionName metadata update; no behavioral impact.

@openMF openMF deleted a comment from coderabbitai bot Nov 26, 2025
…r-019GTifqCMdZeH5hUVeB4WKX' into claude/firebase-remote-config-loader-019GTifqCMdZeH5hUVeB4WKX
@therajanmaurya therajanmaurya changed the title Claude/firebase remote config loader Firebase remote config instance loader Nov 26, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/GestureDetector.kt (1)

33-49: LGTM! Gesture detection logic is correct.

The tap detection logic properly handles the timeout window and tap count. The implementation correctly resets the count when taps are too far apart and triggers the callback after 5 consecutive taps within 1 second.

Optional enhancement: Consider resetting lastTapTime when the gesture is detected (line 45) for state consistency:

                if (tapCount >= 5) {
                    onGestureDetected()
                    tapCount = 0
+                   lastTapTime = 0L
                }

This is not strictly necessary since the logic works correctly as-is, but it makes the state reset more complete.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3293a55 and 7ba0e26.

📒 Files selected for processing (1)
  • cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/GestureDetector.kt (1 hunks)
🔇 Additional comments (3)
cmp-shared/src/commonMain/kotlin/org/mifospay/shared/instance/GestureDetector.kt (3)

1-9: LGTM!

Standard copyright header with appropriate Mozilla Public License v2.0.


25-31: LGTM!

Function signature and state initialization are well-structured. The 1-second timeout window for consecutive taps is a reasonable choice for gesture detection.


22-24: Now I have crucial context: the project uses Kotlin 1.9.x, which is correct for the timeline mentioned in the review comment. However, my earlier web search revealed an important nuance:

Even in Kotlin 1.9+, the @OptIn(ExperimentalTime::class) annotation is still needed if the code uses any kotlin.time members that remain experimental/preview. The annotation is not universally unnecessary just because Kotlin 1.9 stabilized parts of the API—it depends on which specific members are being used.

Since I cannot clone the repository to inspect which kotlin.time symbols are actually being imported and used in GestureDetector.kt, I cannot definitively verify whether the annotation can be safely removed.


The @OptIn(ExperimentalTime::class) annotation may still be necessary.

While Kotlin 1.9+ stabilized most of the kotlin.time API, the ExperimentalTime opt-in is still required for any remaining experimental members. Verify which specific kotlin.time symbols are imported and used in this file—if only stable APIs are used, the annotation can be removed; otherwise, it should be retained.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
core/network/src/commonMain/kotlin/org/mifospay/core/network/config/InstanceConfigLoader.kt (2)

43-82: Unresolved: apis.flexcore.mx endpoint issue persists.

This was flagged in a previous review. The endpoint returns HTTP 404 and has no public documentation. Please address the prior feedback before merging.


111-114: Unresolved: Silent error handling returns success state.

This was flagged in a previous review. The catch block returns DataState.Success(DEFAULT_CONFIG), masking failures from callers. Please address the prior feedback.

🧹 Nitpick comments (3)
core/common/src/commonMain/kotlin/org/mifospay/core/common/DeviceInfoProvider.kt (1)

26-31: Consider logging the exception before falling back.

Silently swallowing all exceptions and returning "unknown" can make debugging difficult. Consider logging the error at a debug/warning level to aid troubleshooting when Firebase Installation ID retrieval fails.

core/common/src/nativeMain/kotlin/org/mifospay/core/common/BuildConfig.native.kt (1)

25-29: Track TODO: Retrieve version info from iOS bundle.

The hardcoded version values are noted with TODOs. Consider creating a tracking issue to implement proper version retrieval from the iOS bundle (e.g., CFBundleShortVersionString and CFBundleVersion).

Would you like me to open an issue to track implementing proper iOS version retrieval?

core/network/src/commonMain/kotlin/org/mifospay/core/network/config/InstanceConfigLoader.kt (1)

117-119: Method name implies continuous observation but only emits once.

observeInstancesConfig suggests reactive updates when config changes, but this implementation performs a single fetch and emits. Consider either:

  1. Renaming to fetchInstancesConfigAsFlow() to match behavior
  2. Implementing actual observation using Firebase Remote Config listeners for real-time updates
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ba0e26 and 58cf6e4.

📒 Files selected for processing (8)
  • core/common/src/androidMain/kotlin/org/mifospay/core/common/BuildConfig.android.kt (1 hunks)
  • core/common/src/androidMain/kotlin/org/mifospay/core/common/DeviceInfoProvider.android.kt (1 hunks)
  • core/common/src/commonMain/kotlin/org/mifospay/core/common/BuildConfig.kt (1 hunks)
  • core/common/src/commonMain/kotlin/org/mifospay/core/common/DeviceInfoProvider.kt (1 hunks)
  • core/common/src/nativeMain/kotlin/org/mifospay/core/common/BuildConfig.native.kt (1 hunks)
  • core/model/src/commonMain/kotlin/org/mifospay/core/model/instance/InstanceSelectorAccessControl.kt (1 hunks)
  • core/network/src/commonMain/kotlin/org/mifospay/core/network/config/FeatureAccessManager.kt (1 hunks)
  • core/network/src/commonMain/kotlin/org/mifospay/core/network/config/InstanceConfigLoader.kt (1 hunks)
🔇 Additional comments (9)
core/model/src/commonMain/kotlin/org/mifospay/core/model/instance/InstanceSelectorAccessControl.kt (1)

14-19: LGTM!

Clean and well-structured data class with sensible defaults for access control configuration.

core/common/src/commonMain/kotlin/org/mifospay/core/common/BuildConfig.kt (1)

15-37: LGTM!

Clean interface design and well-organized constants for build configuration abstraction across platforms.

core/common/src/commonMain/kotlin/org/mifospay/core/common/DeviceInfoProvider.kt (1)

48-53: I'm experiencing persistent repository cloning issues that prevent me from executing the verification scripts. However, based on the review comment's concern about verifying actual implementations for expect functions, I can provide guidance:

The original review comment correctly identifies an important requirement for Kotlin Multiplatform code. The expect functions declared in commonMain must have corresponding actual implementations in each platform source set.

Given the technical limitations preventing direct verification, here is the rewritten review comment:


Verify that actual implementations exist for all platform targets.

These expect function declarations require corresponding actual implementations in platform-specific source sets (androidMain, iosMain, nativeMain, etc.). Compilation will fail for any platform target missing these implementations. Ensure implementations are provided for all supported platforms.

core/common/src/nativeMain/kotlin/org/mifospay/core/common/BuildConfig.native.kt (1)

35-37: Ensure actual object Platform implementations exist for all native target platforms in this project.

The expect object Platform requires matching actual implementations in each native target source set (e.g., iosMain, macosMain) that the project targets. Per Kotlin Multiplatform standards, every expect declaration without @OptionalExpectation must have an actual counterpart for each compiled platform. Verify that actual implementations are provided in the appropriate platform-specific source sets to avoid compiler errors.

core/common/src/androidMain/kotlin/org/mifospay/core/common/BuildConfig.android.kt (1)

15-30: LGTM!

Clean implementation delegating to Android's generated BuildConfig. Using getter properties ensures values are read at access time, which is appropriate for these compile-time constants.

core/network/src/commonMain/kotlin/org/mifospay/core/network/config/FeatureAccessManager.kt (3)

36-49: Access control logic is sound.

The precedence (debug → demo → remote config) is correctly implemented. Minor observation: isDebug and buildType == BuildType.DEBUG may overlap depending on how the interface is implemented, but the defensive check is acceptable.


81-108: Allowlist logic is correct.

The access control follows proper allowlist semantics: when both lists are empty, no restrictions apply; when either list has entries, the device or user must match to gain access.


111-119: LGTM!

Well-structured data class for encapsulating device information. The nullable userId appropriately handles the case when users aren't logged in.

core/network/src/commonMain/kotlin/org/mifospay/core/network/config/InstanceConfigLoader.kt (1)

25-29: LGTM!

Clean interface definition with appropriate async return types for remote config operations.

@therajanmaurya therajanmaurya force-pushed the claude/firebase-remote-config-loader-019GTifqCMdZeH5hUVeB4WKX branch from 58cf6e4 to 7ba0e26 Compare November 26, 2025 15:43
therajanmaurya and others added 2 commits February 6, 2026 18:53
- Upgrade Kotlin to 2.2.21 for gitLive 2.4.0 compatibility
- Update Compose Multiplatform to 1.9.3
- Update Ktor to 3.3.3 and Ktorfit to 2.7.1
- Update Firebase BOM to 34.7.0
- Update Android Gradle Plugin to 8.12.3
- Update Room to 2.8.4
- Add Firebase pods to iOS Podfile for gitlive support

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@cmp-ios/Podfile`:
- Around line 12-14: Remove the unused FirebasePerformance pod entry from the
Podfile: delete the line "pod 'FirebasePerformance'" and keep "pod
'FirebaseAnalytics'" and "pod 'FirebaseCrashlytics'"; after removing, run pod
install (or bundle exec pod install) to update the lockfile and ensure there are
no residual references to FirebasePerformance in the iOS build artifacts.
- Around line 9-14: The Podfile currently declares five Firebase pods without
version constraints and likely includes unnecessary pods for this PR; update the
Podfile to (1) remove pod 'FirebaseAnalytics', pod 'FirebaseCrashlytics', and
pod 'FirebasePerformance' unless gitlive/documentation or your app features
require them, keeping only pod 'FirebaseCore' and pod 'FirebaseRemoteConfig' for
Remote Config support, and (2) pin the remaining Firebase pods with a
pessimistic constraint (use ~> to the current major version used in the project)
so both pod 'FirebaseCore' and pod 'FirebaseRemoteConfig' include the same ~>
major.minor constraint to ensure reproducible installs and consistent versions.

In `@gradle/libs.versions.toml`:
- Around line 6-7: The listed versions in libs.versions.toml are invalid and
will cause Gradle sync failures; update androidGradlePlugin and androidTools to
actual published AGP/tool versions (replace androidGradlePlugin = "8.12.3" and
androidTools = "31.12.3" with a real release such as a published 8.1.x / 8.2.x /
9.0.x for androidGradlePlugin and the matching published androidTools
coordinate), verify the chosen versions exist on Maven Central, and run a Gradle
sync to confirm compatibility (look for androidGradlePlugin and androidTools in
the file to make the changes).
- Around line 349-353: Two aliases in gradle/libs.versions.toml point to the
same module: filekit-compose and filekit-dialogs-compose both map to
io.github.vinceglb:filekit-dialogs-compose; update code to use the canonical
alias and remove the duplicate. Replace all usages of libs.filekit.compose with
libs.filekit.dialogs.compose in the build.gradle.kts that currently imports
filekit (where libs.filekit.compose appears), then delete the redundant
filekit-compose entry from gradle/libs.versions.toml so only
filekit-dialogs-compose remains.
🧹 Nitpick comments (2)
gradle/libs.versions.toml (2)

87-93: Kotlin 2.3.0 is now available — consider upgrading.

For the language release 2.2.0, there was only one tooling release 2.2.20 and one bugfix release 2.2.21. This confirms 2.2.21 is valid. However, Kotlin 2.3.0 has been released (Feb 5, 2026). If merging soon, you may want to bump kotlin to 2.3.0 and ksp to a compatible version to stay current. Note that Kotlin 2.3.0 brings Gradle 9.0 compatibility, which may be relevant if you plan to migrate to AGP 9.0.


1-134: Consider splitting the mass dependency upgrade from the feature PR.

This version catalog update touches ~50+ dependency versions (AGP, Kotlin, Ktor, Compose, Firebase, Koin, Room, testing libraries, etc.), but the PR is titled "Firebase remote config instance loader." Bundling sweeping upgrades with a feature change makes it difficult to bisect regressions and complicates the review.

Ideally, the broad version bumps should be a separate, dedicated PR, and this PR should only add the dependencies directly required for the Firebase Remote Config feature (gitlive-firebase-config, gitLive version bump, and Room entries if needed by the feature).

Comment on lines +9 to +14
# Firebase dependencies required by gitlive
pod 'FirebaseCore'
pod 'FirebaseRemoteConfig'
pod 'FirebaseAnalytics'
pod 'FirebaseCrashlytics'
pod 'FirebasePerformance'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Firebase iOS SDK latest version 2025 2026

💡 Result:

As of February 6, 2026, the latest Firebase iOS (Firebase Apple) SDK version is 12.9.0, released February 3, 2026. [1][2]


🌐 Web query:

gitlive firebase iOS CocoaPods dependencies requirements

💡 Result:

For GitLiveApp/firebase-kotlin-sdk on iOS, the key CocoaPods “dependency requirement” is:

  • GitLive does not pull in the Firebase Apple SDK transitively on iOS — you must explicitly add the Firebase iOS pods your app uses (via CocoaPods or SPM). (github.com)

What Pods to add

Add the product-specific Firebase pods that match the GitLive modules you use, for example:

  • FirebaseCore (baseline)
  • FirebaseAuth (if using auth)
  • FirebaseFirestore (if using firestore)
  • FirebaseFunctions, etc. (github.com)

Also note: the umbrella Firebase pod is deprecated (Firebase Apple SDK v9+); use product pods directly. (firebase.google.com)

Minimum toolchain requirements (CocoaPods install path)

Firebase’s CocoaPods distribution requires:

  • Xcode 16.2+
  • CocoaPods 1.12.0+
  • Podfile must include use_frameworks! (dynamic or static linkage variant) (firebase.google.com)

Kotlin Multiplatform / cocoapods-gradle specifics (if applicable)

  • Set ios.deploymentTarget high enough; otherwise CocoaPods can fail if any pod requires a higher iOS minimum. (kotlinlang.org)

Example (Kotlin cocoapods block)

cocoapods {
  pod("FirebaseCore")
  pod("FirebaseFirestore") // add what you actually use
  pod("FirebaseAuth")
}

GitLive explicitly recommends this approach for iOS linking (and similarly for test targets). (github.com)

Citations:


🌐 Web query:

CocoaPods Firebase version pinning best practices

💡 Result:

Best practices for pinning Firebase iOS SDK versions with CocoaPods

  1. Pin Firebase with a pessimistic constraint (~>) to stay within a major (or minor) series

    • CocoaPods’ ~> lets you accept patch updates while blocking the next breaking series (depending on how many version components you specify). (guides.cocoapods.org)
      Example (stay on Firebase 10.x):
    pod 'FirebaseAnalytics', '~> 10.27'
    pod 'FirebaseCrashlytics', '~> 10.27'
    pod 'FirebaseFirestore', '~> 10.27'

    (Using the same 10.27 family across Firebase pods reduces the chance of resolution conflicts.)

  2. Avoid mixing mismatched Firebase major/minor versions across pods

    • Firebase pods depend on each other with version ranges; mixing constraints can produce CocoaPods solver conflicts. (github.com)
      Practical rule: choose one Firebase SDK version family (e.g., 10.27.x) and keep all Firebase pods aligned to it.
  3. Commit Podfile.lock and use pod install for reproducible builds

    • CocoaPods explicitly recommends committing Podfile.lock, and using pod update only when you intend to change resolved versions. (guides.cocoapods.org)
      Workflow:
    • Day-to-day / CI: pod install
    • Intentional upgrades: pod update FirebaseAnalytics (or specific pods), then test and commit updated Podfile.lock.
  4. Only hard-pin (= X.Y.Z) when you have a known regression and a short-term need

    • CocoaPods supports exact version pinning, but it increases maintenance burden (you’ll miss even patch fixes). (guides.cocoapods.org)
      If you must do it:
    pod 'FirebaseAnalytics', '= 10.27.0'
  5. Keep CocoaPods itself up to date enough for Firebase requirements

    • Firebase noted a CocoaPods minimum (e.g., CocoaPods 1.12.0+ for privacy manifest support). (firebase.google.com)
  6. Prefer the dedicated pod names (e.g., FirebaseAnalytics) over the older subspec style when pinning

    • The Firebase/Analytics subspec form is historically supported, but using the direct pod name is clearer for pinning and is commonly recommended. (stackoverflow.com)

Citations:


Pin Firebase pod versions and verify pod necessity.

All five Firebase pods lack version constraints, risking inconsistent builds across environments. Additionally, according to GitLive documentation, only add the product-specific Firebase pods that match the GitLive modules you use. Since this PR targets Remote Config, only FirebaseCore and FirebaseRemoteConfig are required; verify whether FirebaseAnalytics, FirebaseCrashlytics, and FirebasePerformance are dependencies of the gitlive feature or unnecessary.

Use a pessimistic constraint (~>) aligned across all Firebase pods to the current major version:

Suggested fix
-  pod 'FirebaseCore'
-  pod 'FirebaseRemoteConfig'
-  pod 'FirebaseAnalytics'
-  pod 'FirebaseCrashlytics'
-  pod 'FirebasePerformance'
+  pod 'FirebaseCore', '~> 12'
+  pod 'FirebaseRemoteConfig', '~> 12'
🤖 Prompt for AI Agents
In `@cmp-ios/Podfile` around lines 9 - 14, The Podfile currently declares five
Firebase pods without version constraints and likely includes unnecessary pods
for this PR; update the Podfile to (1) remove pod 'FirebaseAnalytics', pod
'FirebaseCrashlytics', and pod 'FirebasePerformance' unless
gitlive/documentation or your app features require them, keeping only pod
'FirebaseCore' and pod 'FirebaseRemoteConfig' for Remote Config support, and (2)
pin the remaining Firebase pods with a pessimistic constraint (use ~> to the
current major version used in the project) so both pod 'FirebaseCore' and pod
'FirebaseRemoteConfig' include the same ~> major.minor constraint to ensure
reproducible installs and consistent versions.

Comment on lines +12 to +14
pod 'FirebaseAnalytics'
pod 'FirebaseCrashlytics'
pod 'FirebasePerformance'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if gitlive firebase-analytics, firebase-crashlytics, or firebase-perf
# are actually declared as dependencies in any Gradle build file.
echo "=== Searching for gitlive firebase dependencies in Gradle files ==="
rg -n 'dev.gitlive' --type=gradle || rg -n 'dev.gitlive' -g '*.gradle*' -g '*.kts'

echo ""
echo "=== Searching for Firebase usage in shared/common Kotlin source ==="
rg -n 'import dev\.gitlive\.firebase' -g '*.kt' | head -40

Repository: openMF/mobile-wallet

Length of output: 1844


🏁 Script executed:

#!/bin/bash
echo "=== Finding gradle build files ==="
find . -name "build.gradle*" -o -name "*.kts" | grep -E "(build|gradle)" | head -20

echo ""
echo "=== Searching for all gitlive dependencies ==="
rg -i 'gitlive' -g '*.gradle' -g '*.kts' -g '*.gradle.kts'

echo ""
echo "=== Searching for Crashlytics imports ==="
rg -n 'firebase.*crashlytics|crashlytics.*firebase' -g '*.kt' -i

echo ""
echo "=== Searching for Performance imports ==="
rg -n 'firebase.*performance|performance.*firebase' -g '*.kt' -i

echo ""
echo "=== Examining cmp-ios/Podfile ==="
cat cmp-ios/Podfile | head -40

Repository: openMF/mobile-wallet

Length of output: 2755


Remove FirebasePerformance pod — it's not declared as a dependency.

The dev.gitlive:firebase-analytics and dev.gitlive:firebase-crashlytics dependencies are legitimately used in the core-base/analytics module, so keeping FirebaseAnalytics and FirebaseCrashlytics is justified.

However, FirebasePerformance is not declared anywhere in the Gradle build files (core-base/analytics/build.gradle.kts does not declare libs.gitlive.firebase.performance), and no imports of it appear in the Kotlin codebase. Remove it to avoid unnecessary binary size and attack surface.

🤖 Prompt for AI Agents
In `@cmp-ios/Podfile` around lines 12 - 14, Remove the unused FirebasePerformance
pod entry from the Podfile: delete the line "pod 'FirebasePerformance'" and keep
"pod 'FirebaseAnalytics'" and "pod 'FirebaseCrashlytics'"; after removing, run
pod install (or bundle exec pod install) to update the lockfile and ensure there
are no residual references to FirebasePerformance in the iOS build artifacts.

Comment on lines +6 to +7
androidGradlePlugin = "8.12.3"
androidTools = "31.12.3"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify AGP 8.12.3 exists on Maven Central
echo "=== Checking AGP 8.12.3 ==="
curl -s -o /dev/null -w "Status: %{http_code}\n" "https://repo1.maven.org/maven2/com/android/tools/build/gradle/8.12.3/gradle-8.12.3.pom"

# Also check android tools 31.12.3
echo "=== Checking Android Tools 31.12.3 ==="
curl -s -o /dev/null -w "Status: %{http_code}\n" "https://repo1.maven.org/maven2/com/android/tools/common/31.12.3/common-31.12.3.pom"

# Check what 8.x versions exist (list the most recent)
echo "=== Available 8.x AGP versions ==="
curl -s "https://repo1.maven.org/maven2/com/android/tools/build/gradle/maven-metadata.xml" 2>/dev/null | grep -oP '<version>8\.\d+\.\d+</version>' | tail -30

# Also check 9.x for reference
echo "=== Available 9.x AGP versions ==="
curl -s "https://repo1.maven.org/maven2/com/android/tools/build/gradle/maven-metadata.xml" 2>/dev/null | grep -oP '<version>9\.\d+\.\d+</version>' | tail -10

Repository: openMF/mobile-wallet

Length of output: 224


🏁 Script executed:

#!/bin/bash
# Try fetching the raw metadata directly without parsing
echo "=== AGP Metadata (raw) ==="
curl -s "https://repo1.maven.org/maven2/com/android/tools/build/gradle/maven-metadata.xml" 2>/dev/null | head -100

echo ""
echo "=== Available AGP versions via alternative check ==="
curl -s "https://repo1.maven.org/maven2/com/android/tools/build/gradle/" 2>/dev/null | grep -oP 'href="[0-9]+\.[0-9]+\.[0-9]+' | grep -oP '[0-9]+\.[0-9]+\.[0-9]+' | tail -30

Repository: openMF/mobile-wallet

Length of output: 3036


Fix non-existent Gradle plugin versions: androidGradlePlugin = "8.12.3" and androidTools = "31.12.3" do not exist on Maven Central.

Both versions return HTTP 404 errors. Direct Maven Central repository checks confirm neither com.android.tools.build:gradle:8.12.3 nor com.android.tools.common:31.12.3 exist as published releases. Gradle sync will fail with these versions. Use actual published AGP versions (e.g., 8.1.x, 8.2.x, or 9.0.x based on your target API level and compatibility requirements).

🤖 Prompt for AI Agents
In `@gradle/libs.versions.toml` around lines 6 - 7, The listed versions in
libs.versions.toml are invalid and will cause Gradle sync failures; update
androidGradlePlugin and androidTools to actual published AGP/tool versions
(replace androidGradlePlugin = "8.12.3" and androidTools = "31.12.3" with a real
release such as a published 8.1.x / 8.2.x / 9.0.x for androidGradlePlugin and
the matching published androidTools coordinate), verify the chosen versions
exist on Maven Central, and run a Gradle sync to confirm compatibility (look for
androidGradlePlugin and androidTools in the file to make the changes).

Comment on lines 349 to 353
filekit-core = { group = "io.github.vinceglb", name = "filekit-core", version.ref = "fileKit" }
filekit-compose = { module = "io.github.vinceglb:filekit-dialogs-compose", version.ref = "fileKit" }
filekit-dialogs = { module = "io.github.vinceglb:filekit-dialogs", version.ref = "fileKit" }
filekit-dialogs-compose = { module = "io.github.vinceglb:filekit-dialogs-compose", version.ref = "fileKit" }
filekit-coil = { module = "io.github.vinceglb:filekit-coil", version.ref = "fileKit" }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 1. First, locate and examine gradle/libs.versions.toml
find . -name "libs.versions.toml" -type f

Repository: openMF/mobile-wallet

Length of output: 91


🏁 Script executed:

# 2. Once we find the file, let's examine the specific lines mentioned (349-353)
# and surrounding context to understand the state
head -360 gradle/libs.versions.toml | tail -20

Repository: openMF/mobile-wallet

Length of output: 1986


🏁 Script executed:

# 3. Search for all usages of filekit-compose and filekit-dialogs-compose in build files
rg -n 'filekit-compose|filekit-dialogs-compose' --glob '*.kts' --glob '*.gradle'

Repository: openMF/mobile-wallet

Length of output: 46


🏁 Script executed:

# 4. Also check if there are any version references or other filekit entries
rg -n 'fileKit' gradle/libs.versions.toml -A 2 -B 2

Repository: openMF/mobile-wallet

Length of output: 1144


🏁 Script executed:

# Search for any usage of filekit-compose or filekit-dialogs-compose in all files
rg -n 'filekit-compose|filekit-dialogs-compose' --type-add 'gradle:*.{gradle,kts,toml}'

Repository: openMF/mobile-wallet

Length of output: 1154


🏁 Script executed:

# Also search for any gradle dependency declarations that might use these
rg -n 'libs\.filekit'

Repository: openMF/mobile-wallet

Length of output: 1365


🏁 Script executed:

# Check if these aliases appear in any build configuration or documentation
rg -i 'filekit-compose|filekit-dialogs-compose' --glob '*.md' --glob '*.txt' --glob '*.properties'

Repository: openMF/mobile-wallet

Length of output: 856


Consolidate duplicate filekit library aliases: filekit-compose and filekit-dialogs-compose resolve to the same Maven module.

Both aliases (lines 350 and 352) point to io.github.vinceglb:filekit-dialogs-compose. The filekit-compose alias is actively used in the codebase (core-base/ui/build.gradle.kts lines 58, 72), while filekit-dialogs-compose is used elsewhere. This duplication creates unnecessary ambiguity.

Migrate the 2 usages of libs.filekit.compose to libs.filekit.dialogs.compose in core-base/ui/build.gradle.kts, then remove the duplicate filekit-compose entry from gradle/libs.versions.toml.

🤖 Prompt for AI Agents
In `@gradle/libs.versions.toml` around lines 349 - 353, Two aliases in
gradle/libs.versions.toml point to the same module: filekit-compose and
filekit-dialogs-compose both map to io.github.vinceglb:filekit-dialogs-compose;
update code to use the canonical alias and remove the duplicate. Replace all
usages of libs.filekit.compose with libs.filekit.dialogs.compose in the
build.gradle.kts that currently imports filekit (where libs.filekit.compose
appears), then delete the redundant filekit-compose entry from
gradle/libs.versions.toml so only filekit-dialogs-compose remains.

- Update compileSdk from 35 to 36 (required by newer AndroidX libraries)
- Replace deprecated Firebase KTX imports with new Firebase API
- Update Firebase library names from -ktx to non-ktx variants
- Regenerate dependency guard baseline

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@cmp-shared/build.gradle.kts`:
- Line 84: Remove the redundant local compileSdk declaration: delete the line
setting compileSdk = 36 from this module so the project uses the centralized
value defined by the convention plugin (KotlinAndroid). Ensure no other local
compileSdk assignments remain in the file so the convention (KotlinAndroid)
controls the SDK version.

android {
namespace = "org.mifospay.shared"
compileSdk = 35
compileSdk = 36
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 1. Check what compileSdk the convention plugin sets
echo "=== Convention plugin compileSdk ==="
fd -e kt -e kts --full-path 'build-logic|convention' | xargs rg -n 'compileSdk' 2>/dev/null || echo "No convention plugin compileSdk found"

echo ""
echo "=== All compileSdk declarations across modules ==="
fd -e kts --glob 'build.gradle*' | xargs rg -n 'compileSdk' 2>/dev/null || echo "No compileSdk declarations found"

echo ""
echo "=== libs.versions.toml SDK version catalog entries ==="
fd 'libs.versions.toml' | xargs rg -n -i 'compileSdk\|android.*sdk\|androidCompileSdk' 2>/dev/null || echo "No version catalog entries found"

echo ""
echo "=== Check cmp-shared/build.gradle.kts context around line 84 ==="
cat -n cmp-shared/build.gradle.kts | head -90 | tail -20

Repository: openMF/mobile-wallet

Length of output: 945


Remove the redundant compileSdk = 36 declaration; it's already configured by the convention plugin.

The convention plugin (build-logic/convention/src/main/kotlin/org/mifospay/KotlinAndroid.kt:22) already sets compileSdk = 36 for all modules. The hardcoded declaration here is redundant and should be removed to keep configuration centralized.

🤖 Prompt for AI Agents
In `@cmp-shared/build.gradle.kts` at line 84, Remove the redundant local
compileSdk declaration: delete the line setting compileSdk = 36 from this module
so the project uses the centralized value defined by the convention plugin
(KotlinAndroid). Ensure no other local compileSdk assignments remain in the file
so the convention (KotlinAndroid) controls the SDK version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants