Skip to content

fix(toast): one host, painted on dark, clearing only the bar that is drawn - #185

Draft
cestercian wants to merge 2 commits into
mainfrom
claude/design-qa-138-screens-vcgq8l
Draft

fix(toast): one host, painted on dark, clearing only the bar that is drawn#185
cestercian wants to merge 2 commits into
mainfrom
claude/design-qa-138-screens-vcgq8l

Conversation

@cestercian

@cestercian cestercian commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Addresses #179 — the toast theme from the Sep-2026 design QA (epic #161). All three of that issue's findings, all about the same capsule.

⚠️ Draft, and it stays draft until someone builds it

This has not been compiled and has not been on a device. The Gradle build needs dl.google.com for AGP, every androidx/Compose artifact and the SDK itself; this environment's network policy denies that host (CONNECT tunnel failed, response 403), and maven.google.com is only a 301 redirector to it. Maven Central, services.gradle.org and GitHub are reachable; the Aliyun, Tencent and TUNA mirrors are not, and there is no /dev/kvm, so the emulator is out too. The repo has no .github/workflows/, so no check here will build it either.

What I could verify: I pulled the standalone kotlinc 2.1.0 from GitHub releases and parsed all five changed files. No syntax and no structural diagnostics. Every message is either an unresolved reference from compiling without the Android classpath (androidx, AppTheme, Modifier, hiltViewModel, …) or a knock-on of one — the single non-unresolved-reference diagnostic is 'when' expression must be exhaustive on themeRole, which is RootGate's sealed hierarchy being invisible, and it appears identically on the unmodified baseline. That proves the files parse; it does not prove they type-check.

Before this leaves draft:

./gradlew :app:assembleDebug
./gradlew :app:testDebugUnitTest
./gradlew :app:lintDevDebug

No unit test touches statusNote, showBottomBar or lightTabBarHeight, and ToastControllerTest covers the controller, which is unchanged — so I do not expect test movement, but that is an expectation, not a result.

What changed

F-SH-03 (P1) — every toast from a pushed screen floated 88 dp above nothing.
ArtistantNavHost cleared the tab bar whenever the gate was RootGate.Tabs. That is "inside the tab shell", which is a different fact from "the bar is on screen": every pushed destination — chat, a booking, the press kit — lives under that gate with showBottomBar false. The host is a sibling of the scaffold, not a descendant, so a CompositionLocal can't reach it; the scaffolds now report showBottomBar up through an onTabBarVisibilityChange callback and the host keeps the gate check as the outer guard.

The host's padding also moves from vertical = bottomPadding to bottom = bottomPadding. Nothing visible moved, but vertical padded the top of the host's bounds by the same amount — on a tab shell that made the node up to two tab bars taller than the capsule, and that node is a LiveRegionMode.Polite semantics node.

F-PK-01 (P1) — the press kit mounted a second toast host, under the tab bar.
EpkScreen had its own ToastHost inside its Box, taking the default bottom gap, so every "Pricing saved." / "Photo added." / "Cover saved." on screens 23, 87 and 76 drew underneath the bar. statusNote now goes through the same @Singleton ToastController the root host observes, and the local host is deleted — there is one ToastHost call site in the app again.

I bridged it at the screen rather than injecting ToastController into EpkViewModel, because that would touch 18 _state.update {} sites for no behavioural gain here. The ViewModel-side move is the tidier end state and belongs in its own change; noted below.

F-CC-06 (P3) — the wrong black, and a shadow on a flat design.
The capsule painted colors.ink (#14150F) — the text colour, used as a surface — and cast a 12 dp shadow. Now colors.dark (#16171A), the palette's actual dark surface, flat. docs/REDESIGN_2026-09.md §2 has no shadow anywhere and a near-black capsule on the off-white page separates without one.

Files

File Why
designsystem/component/Toast.kt dark not ink, no shadow, bottom not vertical
navigation/ArtistantNavHost.kt tabBarVisible (rememberSaveable); padding driven by gate and visibility
navigation/ClientTabsScaffold.kt reports showBottomBar up
navigation/ArtistTabsScaffold.kt reports showBottomBar up
feature/epk/EpkScreen.kt routes statusNote to the root host; local host deleted

Review rounds

  • Greptile round 1toast briefly overlaps the tab bar across a configuration change. Valid: the controller is a @Singleton, so a live toast survives an activity recreation, and a plain remember came back false and drew that first restored frame over the bar. Fixed in 942aee2 with rememberSaveable, which restores whatever was true at recreation and so is right on a tab root and on a pushed screen alike. Thread resolved.

Still open on #179 after this

  • The green-tree gate (above) — a maintainer step until dl.google.com is allowed.
  • The device walk. [design-qa][P1] Toasts: the wrong black, a shadow, a second host under the tab bar, 88 dp above nothing on pushed screens #179's screens are 77 and every screen that raises a toast; the ones that specifically regressed are the press kit (23 / 87 / 76) and any pushed screen — chat, booking detail, the report flow (ToastIcon.Flag). Worth adding a rotation-with-a-toast-up pass for the round-1 fix. RELEASE.md §10, and remember am start -S.
  • Follow-up, not this PR: inject ToastController into EpkViewModel and drop statusNote from EpkState, so the press kit raises toasts the way ArtistProfileViewModel already does.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SQHtArE39dDg2TUDzwfBAW

…s drawn

Three findings from the Sep-2026 design QA, all in issue #179, all about the
same capsule.

F-SH-03 (P1) — the root host cleared the tab bar whenever the gate was
`RootGate.Tabs`. That is "inside the tab shell", not "the bar is on screen":
every pushed destination — chat, a booking, the press kit — lives under that
gate with `showBottomBar` false, so their toasts floated 88dp above nothing.
Only the scaffolds know the answer, so they now report `showBottomBar` up to
the host, which keeps the gate check as the outer guard. The host's padding
also moves from `vertical` to `bottom`: the top half padded an invisible node
up to two tab bars taller than the capsule, and that node is a live region.

F-PK-01 (P1) — the press kit mounted a SECOND `ToastHost` inside its own Box.
It took the default bottom gap, so every "Pricing saved." on screens 23/87/76
drew underneath the tab bar. `statusNote` now goes through the same
`ToastController` the root host observes and the local host is gone; there is
one `ToastHost` call site in the app again. The state field stays where it is,
so nothing in `EpkViewModel` or its callers moves — pushing the controller
into that ViewModel is the tidier end state and a separate change.

F-CC-06 (P3) — the capsule painted `colors.ink`, the TEXT colour, and cast a
12dp shadow on a design that has no shadows anywhere else. Now `colors.dark`,
the palette's actual dark surface, flat.

Verified: kotlinc 2.1.0 parses all five files clean (no syntax or structural
diagnostics; every remaining message is an unresolved reference from compiling
without the Android classpath). NOT compiled and NOT device-walked — the
Gradle build needs `dl.google.com` for AGP, androidx and the SDK, and this
environment's network policy denies that host, which also rules out the
emulator. The green-tree gate still has to run before this merges.

Addresses #179.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SQHtArE39dDg2TUDzwfBAW
@greptile-apps

greptile-apps Bot commented Sep 10, 2026

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

The reviewed changes appear safe to merge, with no outstanding actionable code issue identified.

Summary

  • Routes EPK status notes through the shared singleton toast controller and removes the local host.
  • Reports tab-bar visibility from both role scaffolds to the root host.
  • Preserves visibility across activity recreation to avoid first-frame overlap.
  • Updates toast padding, surface color, and elevation treatment.

Reviews (2) · Last reviewed commit: "fix(toast): survive an activity recreati..."

Comment thread app/src/main/java/in/artistant/app/navigation/ArtistantNavHost.kt Outdated
Review catch on the first commit. `ToastController` is a `@Singleton`, so a
toast that is up when the activity is recreated — a rotation, a theme switch,
"don't keep activities" — comes straight back with the composition. But
`tabBarVisible` was a plain `remember`, so it came back false, and the first
restored frame drew the capsule OVER the tab bar before the scaffold's
`LaunchedEffect` reported true and it jumped.

`rememberSaveable` restores whatever was true at recreation, so it is right in
both directions: true on a tab root, false on a pushed screen. `remember` has
no other user in the file, so its import goes with it.

Verified: kotlinc 2.1.0 parses the file clean — the only non-unresolved-
reference diagnostic is the pre-existing "'when' expression must be
exhaustive" on `themeRole`, which is `RootGate`'s sealed hierarchy being
invisible without the Android classpath (it appears identically on the
unmodified baseline). Still NOT compiled and NOT device-walked; see #185.

Addresses #179.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SQHtArE39dDg2TUDzwfBAW
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