Android: the background-jobs popover is hidden behind other UI — make it a notification instead #62

Closed
opened 2026-08-18 05:59:09 +00:00 by logan · 3 comments
Collaborator

Report

The background jobs popup at the top is obscured by other UI, so it cannot be read while jobs run. Since the top bar is going away, jobs should probably become a notification that disappears when the work finishes.

Findings

  • job-indicator lives in the header (which the top-bar issue removes) and has its own live region whose label swings between "Scanning Music", "3 background jobs" and "Finished".
  • The app already has one notification surface (store/notification-store.ts) with four levels, coalescing in the store, and the rule that the bottom band belongs to the player. A job notice is the Transient/Persistent shape, not a fifth pattern.
  • Whatever replaces it must keep pause/cancel reachable — job-controls.ts gates its "you will discard hours of downloading" confirmation on KindIndexBuild.

Direction

Route active jobs through the notification surface on phone widths: a persistent entry while work runs, with progress and a cancel action, dismissed automatically on completion. Consider an actual Android system notification for long work (the foreground service already exists for playback — reusing it for scans needs its own argument). Sequence after the top-bar removal.

**Report** The background jobs popup at the top is obscured by other UI, so it cannot be read while jobs run. Since the top bar is going away, jobs should probably become a notification that disappears when the work finishes. **Findings** - `job-indicator` lives in the header (which the top-bar issue removes) and has its own live region whose label swings between "Scanning Music", "3 background jobs" and "Finished". - The app already has one notification surface (`store/notification-store.ts`) with four levels, coalescing in the store, and the rule that the bottom band belongs to the player. A job notice is the **Transient/Persistent** shape, not a fifth pattern. - Whatever replaces it must keep pause/cancel reachable — `job-controls.ts` gates its "you will discard hours of downloading" confirmation on `KindIndexBuild`. **Direction** Route active jobs through the notification surface on phone widths: a persistent entry while work runs, with progress and a cancel action, dismissed automatically on completion. Consider an actual Android system notification for long work (the foreground service already exists for playback — reusing it for scans needs its own argument). Sequence after the top-bar removal.
Author
Collaborator

The Direction sentence contradicts this issue's own blocker.

It ends "Sequence after the top-bar removal", but #57 (drop the top
bar) carries a real Gitea dependency on #62, and #73's Phase 3
graph reads #62 ──► #57. So anyone reading this issue alone will go
looking for #57 first and find it blocked behind this one.

The dependency is almost certainly the right way round — the job
indicator needs somewhere to live before the bar holding it goes
away, which is the whole content of this issue — so it is the sentence
that should change, to something like "Sequence before the top-bar
removal; #57 depends on this."

Filed in passing while working #159; not changing it here, since the
wording is this issue's to own.

**The Direction sentence contradicts this issue's own blocker.** It ends "Sequence after the top-bar removal", but #57 (drop the top bar) carries a real Gitea dependency **on #62**, and #73's Phase 3 graph reads `#62 ──► #57`. So anyone reading this issue alone will go looking for #57 first and find it blocked behind this one. The dependency is almost certainly the right way round — the job indicator needs somewhere to live *before* the bar holding it goes away, which is the whole content of this issue — so it is the sentence that should change, to something like "Sequence *before* the top-bar removal; #57 depends on this." Filed in passing while working #159; not changing it here, since the wording is this issue's to own.
logan self-assigned this 2026-08-20 21:56:40 +00:00
logan added the
Status
In Progress
label 2026-08-20 21:56:40 +00:00
Author
Collaborator

Taking this on feat/62-jobs-as-a-notification. It is #73 Phase 3's
first item and #57 is blocked on it.

Measuring before designing. The report is that the popover "is obscured
by other UI", and the Direction proposes replacing it with a
notification — but those are answers to two different questions, and
which one is right depends on why it is obscured. .top-bar has no
overflow: hidden and no z-index, while .main-panel and
div.sidebar both carry contain: layout style paint, which creates a
stacking context; that is a specific, checkable hypothesis and it is
cheaper to test than to reason about. So: reproduce it at 390px in the
browser tier first, then decide.

What I am committing to either way is that the job UI stays one UI.
applyJobControl is what carries the "you will discard hours of
downloading" confirmation, keyed on KindIndexBuild, and a surface
that draws its own buttons drops that silently — #27 already established
that shape. So whatever the phone gets is the existing panel presented
differently, not a second implementation of it.

The system-notification half of the Direction is explicitly out of
scope here: the issue itself says it needs its own argument, and the
foreground service currently exists for playback only.

Taking this on `feat/62-jobs-as-a-notification`. It is #73 Phase 3's first item and #57 is blocked on it. Measuring before designing. The report is that the popover "is obscured by other UI", and the Direction proposes replacing it with a notification — but those are answers to two different questions, and which one is right depends on *why* it is obscured. `.top-bar` has no `overflow: hidden` and no `z-index`, while `.main-panel` and `div.sidebar` both carry `contain: layout style paint`, which creates a stacking context; that is a specific, checkable hypothesis and it is cheaper to test than to reason about. So: reproduce it at 390px in the browser tier first, then decide. What I am committing to either way is that the job UI stays **one** UI. `applyJobControl` is what carries the "you will discard hours of downloading" confirmation, keyed on `KindIndexBuild`, and a surface that draws its own buttons drops that silently — #27 already established that shape. So whatever the phone gets is the existing panel presented differently, not a second implementation of it. The system-notification half of the Direction is explicitly out of scope here: the issue itself says it needs its own argument, and the foreground service currently exists for playback only.
logan closed this issue 2026-08-20 22:32:47 +00:00
gitea-actions bot removed the
Status
In Progress
label 2026-08-20 22:32:55 +00:00
Author
Collaborator

Correcting my own conclusion above, found while handing over.

I wrote that the "obscured" symptom "did not reproduce in any tier" and
that this was therefore not a stacking fix. The first half is true and
the second half was too strong: the tier could not have seen it.

wa-popup renders <div popover="manual"> and feature detects:

var SUPPORTS_POPOVER = globalThis?.HTMLElement?.prototype.hasOwnProperty("popover");
...
strategy: SUPPORTS_POPOVER ? "absolute" : "fixed",

Chromium and WebKit in CI both have the Popover API, so the panel is
promoted to the top layer and cannot be clipped by anything —
which is exactly what I measured, four times, and it says nothing
about the reference device. Chrome 113 has no Popover API and falls
back to position: fixed, which escapes ancestor overflow but not
contain: paint, since that makes an element a containing block for
fixed descendants and clips them.

For this component that fallback probably still lands correctly —
job-indicator is in .top-bar and no ancestor of the top bar has
containment — so I still cannot say the report was this. But
.main-panel and div.sidebar both carry contain: layout style paint, and every context menu in the app is inside .main-panel,
which is #60 (Reviewed/Confirmed, same words: "behind other UI").
Same library, same browser, two outcomes decided by which side of
contain: paint the popup is on. I have written the detail up on #60.

None of this changes what shipped: the band is the right home for
background work on a phone whether or not the popover was clipped, and
both reasons in the PR (a disclosure is wrong for status, and #57 needs
the bar emptied) stand on their own. But "did not reproduce" should be
read as "no tier here can reproduce it", which is a weaker statement
and the honest one.

**Correcting my own conclusion above, found while handing over.** I wrote that the "obscured" symptom "did not reproduce in any tier" and that this was therefore not a stacking fix. The first half is true and the second half was too strong: **the tier could not have seen it.** `wa-popup` renders `<div popover="manual">` and feature detects: ```js var SUPPORTS_POPOVER = globalThis?.HTMLElement?.prototype.hasOwnProperty("popover"); ... strategy: SUPPORTS_POPOVER ? "absolute" : "fixed", ``` Chromium and WebKit in CI both have the Popover API, so the panel is promoted to the **top layer** and cannot be clipped by anything — which is exactly what I measured, four times, and it says nothing about the reference device. Chrome 113 has no Popover API and falls back to `position: fixed`, which escapes ancestor *overflow* but not `contain: paint`, since that makes an element a containing block for fixed descendants and clips them. For this component that fallback probably still lands correctly — `job-indicator` is in `.top-bar` and no ancestor of the top bar has containment — so I still cannot say the report was this. But `.main-panel` and `div.sidebar` both carry `contain: layout style paint`, and **every context menu in the app is inside `.main-panel`**, which is #60 (`Reviewed/Confirmed`, same words: "behind other UI"). Same library, same browser, two outcomes decided by which side of `contain: paint` the popup is on. I have written the detail up on #60. None of this changes what shipped: the band is the right home for background work on a phone whether or not the popover was clipped, and both reasons in the PR (a disclosure is wrong for status, and #57 needs the bar emptied) stand on their own. But "did not reproduce" should be read as "no tier here can reproduce it", which is a weaker statement and the honest one.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: yonlu/yellowjacket#62