Skip to content

feat(ui): show the earnings the app was already receiving - #47

Merged
GeiserX merged 2 commits into
mainfrom
feat/show-earnings-and-attention-first
Aug 5, 2026
Merged

feat(ui): show the earnings the app was already receiving#47
GeiserX merged 2 commits into
mainfrom
feat/show-earnings-and-attention-first

Conversation

@GeiserX

@GeiserX GeiserX commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Closes CashPilot-android-9nb, and the first half of kv8.

The app was receiving earnings and showing you none of them

Before touching anything:

grep formatPlatformEarnings app/src/  -> ONLY EarningsDisplayTest.kt
grep -c earnings ui/MainViewModel.kt  -> 0

The entire pipeline shipped and is careful — Earnings/PlatformEarnings, recordEarnings, earningsToKeep for offline blips, earningsAsOf so staleness is expressible, formatPlatformEarnings returning an em-dash rather than $0.00. Every one of those was unreached from the UI. The phone asked, the server answered, the reply was parsed and stored — and no screen read it. That was your original complaint about this app.

Now shown: device total, the window it covers, how many platforms have no reading yet, a "may be out of date" badge — and each app's own figure on its card, with a note when the provider reports one balance for the whole account.

Three rules a display can silently discard

Each is now a test, not a comment:

Rule Why it matters
A missing figure is not zero null → "nothing read yet", never $0.00. A confident zero beside a running app claims the provider paid nothing — a measurement nobody took. A real 0.0 still shows as $0.00.
A kept figure is not a current one The last value survives an offline phone deliberately, so it must be labelled when old.
An account balance is not a device's earnings One balance per account, so a platform on several machines can't be attributed to this phone.

These live in EarningsPresentation, not inside the Composable. This module has no way to render a Composable in a test, so rules left in the UI go unverified — which is precisely how a complete pipeline came to be built and never displayed.

Problems now sort first

The dashboard sorted by AppState.ordinal — the enum's declaration order, which puts RUNNING first. So apps that were fine occupied the top of the screen and anything STOPPED was pushed underneath them. On a two-column grid with eleven apps, a stopped earner can sit below the fold — the one thing you opened the app to find out.

Ranking is now an explicit table rather than an ordinal, so reordering the enum cannot silently rearrange the screen, and a test fails if a new state is added without a rank. A secondary alphabetical key stops the grid reshuffling under your thumb on every 30-second refresh.

A test that could not fail

AppStateResolutionTest copied the production when-expression, and said so: "this is the exact same when-expression used in production". A copy cannot fail when production changes, so every case verified the copy. The logic moved to AppPresentation; both call it now.

I can build this locally again

There is no working JVM on either Mac — the dev machine has no Java, and Temurin 17 on the mini crashes with SIGBUS ... CodeHeap::allocate on java -version, in interpreted mode, and with a reduced code cache, so it is the JVM itself. scripts/remote-gradle.sh runs Gradle in eclipse-temurin:17-jdk on the build host instead: ~26s warm, versus a CI round trip. It is a faster pre-check, not a replacement for CI, and says so.

Evidence

356 tests, 0 failures, lintDebug clean. Nine negative controls fire:

  • revert the sort to ordinal → 4 fail
  • two states share a rank → 4 fail
  • drop the alphabetical tiebreak → 4 fail
  • undetectable app treated as running → 3 fail
  • treat an unread total as zero → 3 fail
  • report a real 0.0 as unread → 3 fail
  • drop the staleness label → 4 fail
  • claim staleness with no figure → 4 fail
  • show earnings on uninstalled apps → 3 fail

Not in this PR

The rest of kv8 — permissions as a blocking state rather than a dismissible banner, and QR pairing. Those are separate changes and this one was already the highest-value part.

Summary by CodeRabbit

  • New Features
    • Added an earnings dashboard card showing measured earnings, reporting periods, missing data, stale readings, and server setup guidance.
    • Added per-app earnings displays and shared-worker indicators where applicable.
    • Added clearer app status ordering, prioritizing stopped apps and using alphabetical tie-breaking.
  • Bug Fixes
    • Improved handling of unavailable earnings versus zero earnings.
    • Added stale-data indicators with relative timestamps.
  • Tests
    • Expanded coverage for app states, dashboard sorting, earnings display modes, and stale-data behavior.
  • Chores
    • Added a remote build helper for running Gradle tasks in a consistent environment.

The whole earnings pipeline shipped and nothing displayed it. Verified before
touching anything:

  grep formatPlatformEarnings app/src/  -> ONLY EarningsDisplayTest.kt
  grep -c earnings ui/MainViewModel.kt  -> 0

So the phone asked the server for earnings, the server answered, the reply was
parsed, stored, kept across offline blips and timestamped for staleness -- and
then no screen read any of it. That was the original complaint about this app.

WHAT IS NOW SHOWN
A card with the device total, the window it covers, how many platforms have no
reading yet, and a "may be out of date" badge; plus each app's own figure on its
card, with a note when the provider reports one balance for the whole account.

THE THREE RULES A DISPLAY CAN SILENTLY THROW AWAY, each now tested:
  - a missing figure is NOT zero. null renders as "nothing read yet", never
    $0.00, because a confident zero beside a running app claims the provider
    paid nothing -- a measurement nobody took. A real 0.0 IS shown as $0.00.
  - a kept figure is NOT a current one. The last value survives an offline phone
    deliberately, so it is labelled when old.
  - an account balance is NOT a device's earnings, and the card says so instead
    of implying it.

Those rules live in EarningsPresentation, not inside the Composable, because
this module has no way to render a Composable in a test -- leaving them in the
UI is how they would go unverified, which is exactly how the pipeline came to be
built and never displayed.

PROBLEMS NOW SORT FIRST
The dashboard sorted by AppState.ordinal -- the enum's DECLARATION order, which
puts RUNNING first -- so apps that were fine took the top of the screen and
anything STOPPED was pushed underneath. On a two-column grid with eleven apps a
stopped earner can sit below the fold, which is the one thing the user opened
the app to find out. Ranking is now an explicit table, not an enum's ordinal, so
reordering the enum cannot silently rearrange the screen, and a test fails if a
new state has no rank. A secondary alphabetical key stops the grid reshuffling
under the user's thumb on every 30-second refresh.

AppStateResolutionTest used to COPY the production when-expression and said so:
"this is the exact same when-expression used in production". A copy cannot fail
when production changes, so it verified the copy. It calls AppPresentation now.

Nine negative controls fire, including reverting the sort to ordinal, treating
an unread total as zero, reporting a real 0.0 as unread, and dropping the
staleness label.
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@GeiserX, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 52 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f90d712c-3b2c-4261-ab61-c4c04fff7912

📥 Commits

Reviewing files that changed from the base of the PR and between 7d18df4 and 256286f.

📒 Files selected for processing (1)
  • app/src/test/java/com/cashpilot/android/AppStateResolutionTest.kt
📝 Walkthrough

Walkthrough

The dashboard now uses explicit app-state ranking and earnings presentation rules. It displays server earnings and per-app earnings with stale-data handling. Tests cover the presentation rules. A remote Gradle execution helper was added.

Changes

Dashboard presentation

Layer / File(s) Summary
App state resolution and ordering
app/src/main/java/com/cashpilot/android/ui/AppPresentation.kt, app/src/main/java/com/cashpilot/android/ui/MainViewModel.kt, app/src/test/java/com/cashpilot/android/AppPresentationTest.kt, app/src/test/java/com/cashpilot/android/AppStateResolutionTest.kt
App state resolution now uses explicit precedence. Dashboard sorting uses attention ranks and case-insensitive display names. Tests cover ranking, sorting, stability, and status preservation.
Earnings rules and ViewModel state
app/src/main/java/com/cashpilot/android/ui/EarningsPresentation.kt, app/src/main/java/com/cashpilot/android/ui/MainViewModel.kt, app/src/test/java/com/cashpilot/android/EarningsPresentationTest.kt
Earnings modes, stale-data rules, and per-app visibility are centralized. MainViewModel exposes earnings and the corresponding timestamp.
Dashboard earnings rendering
app/src/main/java/com/cashpilot/android/ui/component/EarningsCard.kt, app/src/main/java/com/cashpilot/android/ui/screen/DashboardScreen.kt, app/src/main/res/values/strings.xml
The dashboard renders earnings status, figures, stale indicators, reporting windows, platform earnings, and shared-balance labels using new resources.

Remote build support

Layer / File(s) Summary
Remote Gradle synchronization and execution
scripts/remote-gradle.sh
The helper synchronizes the repository to a configurable host and runs Gradle tasks in an Eclipse Temurin 17 container with persistent SDK, source, and Gradle-cache mounts.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.59% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: exposing existing app earnings in the UI.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/show-earnings-and-attention-first

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/test/java/com/cashpilot/android/AppStateResolutionTest.kt`:
- Around line 33-37: Update the obsolete dashboard-order test that currently
sorts by AppState.ordinal and expects RUNNING first. Replace the ordinal-based
sorting with AppPresentation.sortForDashboard, then assert the resulting
production order with STOPPED first.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ab99bdf0-ef0d-45f1-932a-5ee970a4db57

📥 Commits

Reviewing files that changed from the base of the PR and between 05a587b and 7d18df4.

📒 Files selected for processing (10)
  • app/src/main/java/com/cashpilot/android/ui/AppPresentation.kt
  • app/src/main/java/com/cashpilot/android/ui/EarningsPresentation.kt
  • app/src/main/java/com/cashpilot/android/ui/MainViewModel.kt
  • app/src/main/java/com/cashpilot/android/ui/component/EarningsCard.kt
  • app/src/main/java/com/cashpilot/android/ui/screen/DashboardScreen.kt
  • app/src/main/res/values/strings.xml
  • app/src/test/java/com/cashpilot/android/AppPresentationTest.kt
  • app/src/test/java/com/cashpilot/android/AppStateResolutionTest.kt
  • app/src/test/java/com/cashpilot/android/EarningsPresentationTest.kt
  • scripts/remote-gradle.sh

Comment thread app/src/test/java/com/cashpilot/android/AppStateResolutionTest.kt
CodeRabbit caught this, and it is the same defect class as the resolveState copy
in the same file -- I fixed that one and walked past this.

`build display list and sort by state ordinal` re-implemented the OLD sort on
its own local list:

    }.sortedBy { it.state.ordinal }

and asserted RUNNING first. Because it sorted a list it built itself, it never
called the app, so it kept passing after production changed to put problems
first -- while documenting behaviour the dashboard no longer has. A future
reader would have taken it as the spec.

It now calls AppPresentation.sortForDashboard and asserts the real order,
including the alphabetical tiebreak within a rank.

Verified by control: reverting attentionRank so RUNNING leads again now fails
this test. Before the change, the same mutation left it green.
@GeiserX
GeiserX merged commit d10d0ec into main Aug 5, 2026
3 checks passed
@GeiserX
GeiserX deleted the feat/show-earnings-and-attention-first branch August 5, 2026 18:33
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