Skip to content

fix: prevent setup auto-dismiss, improve settings UX#9

Merged
GeiserX merged 2 commits intomainfrom
fix/setup-settings-ux
Apr 4, 2026
Merged

fix: prevent setup auto-dismiss, improve settings UX#9
GeiserX merged 2 commits intomainfrom
fix/setup-settings-ux

Conversation

@GeiserX
Copy link
Copy Markdown
Owner

@GeiserX GeiserX commented Apr 4, 2026

Summary

  • Setup screen no longer auto-dismisses when granting the last required permission — stays until user explicitly clicks Continue/Skip. Uses a persistent setupCompleted flag in DataStore (with migration for existing installs).
  • Settings page: removed redundant notification/usage access buttons (already handled in setup wizard + dashboard banner). Kept only battery optimization with state-aware text (shows checkmark when already disabled vs actionable button when not).
  • Battery intent: fixed to use direct per-app ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS prompt instead of global settings list.
  • About section: GitHub buttons now show a GitHub icon instead of "GitHub —" text prefix.

Test plan

  • Fresh install: setup screen shows, granting all permissions does NOT auto-redirect to dashboard
  • Click "Continue to Dashboard" → goes to dashboard, setup never shows again on restart
  • Click "Skip for now" → goes to dashboard, setup never shows again on restart
  • Existing install (server URL set): setup screen does NOT re-appear after update
  • Settings: battery shows "Battery optimization disabled" with checkmark when unrestricted
  • Settings: battery shows "Disable Battery Optimization" button when optimized, clicking opens direct per-app prompt
  • Settings: About section shows GitHub icon on repo buttons

Summary by CodeRabbit

  • New Features

    • Battery optimization status and controls added to Settings
    • GitHub icons added to About links
  • Improvements

    • Setup flow now records and respects a persisted "setup completed" flag
    • Setup completion is consolidated and reliably persisted
    • Settings persistence enhanced with migration for existing users

- Add setupCompleted flag to DataStore so setup screen stays until
  user explicitly clicks Continue/Skip (no more auto-redirect after
  granting the last permission)
- Migration: existing installs with server URL set are auto-marked
  as setup completed
- Settings: remove notification/usage access buttons (handled in setup),
  keep only battery optimization with state-aware text
- Settings: fix battery intent to use direct per-app prompt
- Settings: add GitHub icon to About section buttons
- Update string resources for battery state and about labels
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 4, 2026

📝 Walkthrough

Walkthrough

Added a persisted setupCompleted flag to Settings and migrated flow/control to rely on it; replaced permission checks in Settings UI with battery optimization controls and added a GitHub icon and related resources.

Changes

Cohort / File(s) Summary
Settings model & persistence
app/src/main/java/com/cashpilot/android/model/Settings.kt, app/src/main/java/com/cashpilot/android/util/SettingsStore.kt
Added setupCompleted: Boolean = false to Settings. Persisted setup_completed in DataStore, reading it with a migration fallback from existing server_url/api_key and writing it on updates.
Setup/navigation flow
app/src/main/java/com/cashpilot/android/ui/MainActivity.kt, app/src/main/java/com/cashpilot/android/ui/screen/SetupScreen.kt
Removed permission/dismissal-derived setup state; navigation now depends on settings.setupCompleted. Consolidated SetupScreen completion logic to set setupCompleted = true via ViewModel update.
Settings UI & resources
app/src/main/java/com/cashpilot/android/ui/screen/SettingsScreen.kt, app/src/main/res/drawable/ic_github.xml, app/src/main/res/values/strings.xml
Replaced notification/usage permission section with battery optimization section driven by hasBatteryOptOut. Added ic_github drawable and updated About links to include the icon; added battery_unrestricted and battery_disable_optimization strings and adjusted GitHub link labels.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant Setup as SetupScreen
    participant VM as SettingsViewModel
    participant Store as SettingsStore
    participant Main as MainActivity

    User->>Setup: tap "Continue"/"Skip"
    Setup->>VM: updateSettings(serverUrl, apiKey, setupCompleted=true)
    VM->>Store: write preferences (server_url, api_key, setup_completed)
    Store-->>VM: confirm write
    Store-->>Main: emit updated Settings (setupCompleted = true)
    Main->>Main: hide SetupScreen, show main UI
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped to save a tiny bit of state,
A flag that keeps the setup straight.
With battery checks and a GitHub sign,
Users finish setup—everything’s fine!
A little hop, a little cheer, setup complete, my dear.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 accurately summarizes the main changes: preventing setup auto-dismiss and improving settings UI/UX. It aligns with the core objectives of persisting setup state and refactoring settings controls.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/setup-settings-ux

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

Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
app/src/main/java/com/cashpilot/android/ui/screen/SetupScreen.kt (1)

190-201: Consider extracting shared completion action to avoid drift.

Lines 190 and 201 duplicate the same updateSettings block. A small local lambda would keep Continue/Skip behavior synchronized.

Refactor sketch
+            val completeSetup = {
+                viewModel.updateSettings {
+                    it.copy(serverUrl = localUrl, apiKey = localKey, setupCompleted = true)
+                }
+                onComplete()
+            }
             Button(
                 onClick = {
-                    viewModel.updateSettings { it.copy(serverUrl = localUrl, apiKey = localKey, setupCompleted = true) }
-                    onComplete()
+                    completeSetup()
                 },
             ) { ... }

             TextButton(
                 onClick = {
-                    viewModel.updateSettings { it.copy(serverUrl = localUrl, apiKey = localKey, setupCompleted = true) }
-                    onComplete()
+                    completeSetup()
                 },
             ) { ... }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/cashpilot/android/ui/screen/SetupScreen.kt` around
lines 190 - 201, Extract the duplicated completion logic into a single local
lambda and call it from both buttons so their behavior stays synchronized:
create a val like completeAction = { viewModel.updateSettings {
it.copy(serverUrl = localUrl, apiKey = localKey, setupCompleted = true) };
onComplete() } and replace the two inline updateSettings/onComplete blocks in
the Continue and Skip TextButton onClick handlers with a call to completeAction;
reference viewModel.updateSettings, localUrl, localKey, setupCompleted, and
onComplete when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/src/main/java/com/cashpilot/android/ui/screen/SetupScreen.kt`:
- Around line 190-201: Extract the duplicated completion logic into a single
local lambda and call it from both buttons so their behavior stays synchronized:
create a val like completeAction = { viewModel.updateSettings {
it.copy(serverUrl = localUrl, apiKey = localKey, setupCompleted = true) };
onComplete() } and replace the two inline updateSettings/onComplete blocks in
the Continue and Skip TextButton onClick handlers with a call to completeAction;
reference viewModel.updateSettings, localUrl, localKey, setupCompleted, and
onComplete when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 35f28dee-435b-4225-b985-f84baa3c4d88

📥 Commits

Reviewing files that changed from the base of the PR and between 326c7ad and 5dceb61.

📒 Files selected for processing (7)
  • app/src/main/java/com/cashpilot/android/model/Settings.kt
  • app/src/main/java/com/cashpilot/android/ui/MainActivity.kt
  • app/src/main/java/com/cashpilot/android/ui/screen/SettingsScreen.kt
  • app/src/main/java/com/cashpilot/android/ui/screen/SetupScreen.kt
  • app/src/main/java/com/cashpilot/android/util/SettingsStore.kt
  • app/src/main/res/drawable/ic_github.xml
  • app/src/main/res/values/strings.xml

- Migration now requires both server URL and API key to be non-empty
  before marking setupCompleted (prevents skipping wizard for partial
  configs)
- Extract finishSetup lambda in SetupScreen so Continue and Skip
  stay in sync
Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
app/src/main/java/com/cashpilot/android/ui/screen/SetupScreen.kt (1)

80-83: Consider refactoring finishSetup to await DataStore persistence before calling onComplete.

updateSettings() launches a coroutine and returns immediately (fire-and-forget pattern), meaning onComplete() executes before the DataStore write completes. While the current code is safe—onComplete is an empty lambda and navigation is driven by the settings.setupCompleted Flow state—this pattern is fragile if onComplete gains meaningful logic in the future.

For robustness, expose a suspend variant in MainViewModel:

suspend fun updateSettingsAwait(transform: (Settings) -> Settings) {
    SettingsStore.update(getApplication(), transform)
}

Then await it in the composable:

val scope = rememberCoroutineScope()
val finishSetup: () -> Unit = {
    scope.launch {
        viewModel.updateSettingsAwait { it.copy(serverUrl = localUrl, apiKey = localKey, setupCompleted = true) }
        onComplete()
    }
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/cashpilot/android/ui/screen/SetupScreen.kt` around
lines 80 - 83, The finishSetup lambda currently calls viewModel.updateSettings
(which returns immediately) and then onComplete, risking onComplete firing
before DataStore write finishes; add a suspend API on MainViewModel (e.g.,
suspend fun updateSettingsAwait(transform: (Settings) -> Settings) that
internally calls SettingsStore.update(getApplication(), transform)), then in
SetupScreen use rememberCoroutineScope and replace the current finishSetup with
a coroutine: scope.launch { viewModel.updateSettingsAwait { it.copy(serverUrl =
localUrl, apiKey = localKey, setupCompleted = true) }; onComplete() } so
onComplete runs only after persistence completes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/src/main/java/com/cashpilot/android/ui/screen/SetupScreen.kt`:
- Around line 80-83: The finishSetup lambda currently calls
viewModel.updateSettings (which returns immediately) and then onComplete,
risking onComplete firing before DataStore write finishes; add a suspend API on
MainViewModel (e.g., suspend fun updateSettingsAwait(transform: (Settings) ->
Settings) that internally calls SettingsStore.update(getApplication(),
transform)), then in SetupScreen use rememberCoroutineScope and replace the
current finishSetup with a coroutine: scope.launch {
viewModel.updateSettingsAwait { it.copy(serverUrl = localUrl, apiKey = localKey,
setupCompleted = true) }; onComplete() } so onComplete runs only after
persistence completes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8b236fa0-a480-46ef-9999-0376b40af6e7

📥 Commits

Reviewing files that changed from the base of the PR and between 5dceb61 and 58f155e.

📒 Files selected for processing (2)
  • app/src/main/java/com/cashpilot/android/ui/screen/SetupScreen.kt
  • app/src/main/java/com/cashpilot/android/util/SettingsStore.kt
✅ Files skipped from review due to trivial changes (1)
  • app/src/main/java/com/cashpilot/android/util/SettingsStore.kt

@GeiserX GeiserX merged commit 197adcf into main Apr 4, 2026
3 checks passed
@GeiserX GeiserX deleted the fix/setup-settings-ux branch April 4, 2026 21:56
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.

1 participant