Use beandroid's date scroller for the Stream tab #324

Closed
opened 2026-10-24 09:58:00 +02:00 by agentic-developer · 2 comments
Collaborator

The Stream tab of the app scrolls with our own FastScroller (ui/FastScroller.kt, ui/FastScroll.kt), whose handle moves by rows and whose bubble names the day of the first row.

beandroid's journal has a better one, DateScroller (app/src/main/kotlin/de/konstantinfickel/beandroid/ui/DateScroller.kt, pure part in ui/journal/DateScroll.kt). Bring it to the Stream tab:

  • the track is a time axis (newest to oldest day), not rows: how many cards a day has does not move it; a drag snaps to the nearest day with cards
  • while held: the handle widens, a gradient panel shows month marks (thinned to quarters or years when they do not fit) that can be tapped to jump, and a large date preview (date and weekday) follows the handle
  • a haptic tick when the month changes
  • the handle hides after 1.5 s; a narrow edge target shows it again without taking the gestures of the list
  • jumps go through a conflated channel, so a slow list does not replay old drag positions
  • accessibility: content and state description, progress range, a TalkBack step moves to the next or previous day with cards, actions for the previous and next month
  • split into the stateful DateScroller and a stateless DateScrollerContent for the screenshot tests

Reference commits in beandroid: 6f8ea91, 414698b.

The Stream tab of the app scrolls with our own `FastScroller` (`ui/FastScroller.kt`, `ui/FastScroll.kt`), whose handle moves by rows and whose bubble names the day of the first row. beandroid's journal has a better one, `DateScroller` (`app/src/main/kotlin/de/konstantinfickel/beandroid/ui/DateScroller.kt`, pure part in `ui/journal/DateScroll.kt`). Bring it to the Stream tab: - the track is a time axis (newest to oldest day), not rows: how many cards a day has does not move it; a drag snaps to the nearest day with cards - while held: the handle widens, a gradient panel shows month marks (thinned to quarters or years when they do not fit) that can be tapped to jump, and a large date preview (date and weekday) follows the handle - a haptic tick when the month changes - the handle hides after 1.5 s; a narrow edge target shows it again without taking the gestures of the list - jumps go through a conflated channel, so a slow list does not replay old drag positions - accessibility: content and state description, progress range, a TalkBack step moves to the next or previous day with cards, actions for the previous and next month - split into the stateful `DateScroller` and a stateless `DateScrollerContent` for the screenshot tests Reference commits in beandroid: 6f8ea91, 414698b.
Author
Collaborator

Plan

Decisions

  • Only the Stream tab gets the new scroller. The Tags and File types tabs of the configuration keep FastScroller: they are sorted by name, not by date.
  • The Capture button hides while the scroller is held, as in beandroid.
  • The large preview says Today / Yesterday by dayName, with the full date below, else the date (with the year only where showsYear) and the weekday.

Pure part: ui/DateScroll.kt (from beandroid's ui/journal/DateScroll.kt, in the style of this repo)

  • DateScroll(newest, oldest, days) with fraction, dateAt, snap, months, adjust (A11Y_STEP), dayForMonth; MonthMark and List<MonthMark>.visible(track, spacing).
  • dateScroll(days: List<Day>): none with fewer than two distinct days.
  • dayIndices(leading, days): the index of each day header in the LazyColumn (one header plus the cards of each day). leading is the number of rows above the days (pull banner, unreadable files, working, reminders, "nothing"), which Stream.kt already counts for streamRows.
  • The day of the first visible row comes from these indices by binary search, as in beandroid.
  • Unit tests in src/test/.../ui/DateScrollTest.kt: fraction/dateAt round trip, snap (exact, between days with a tie going to the newer one, the ends), months and visible (all → quarters → years), adjust (one step to the next or previous day, any other value snaps), dayForMonth, dayIndices with leading rows, and dateScroll being none for one day.

Composable: ui/DateScroller.kt

  • DateScroller(scroll, listState, indices, onDraggingChange, modifier) (stateful: visibility, expansion, drag fraction, a conflated Channel of target dates collected with collectLatest → scrollToItem, SegmentTick haptics when the month changes, hidden 1.5 s after scrolling or dragging stops) and the stateless internal DateScrollerContent(scroll, fraction, dragging, visible, …).
  • Visuals as in beandroid: a handle that is primary, 32 → 36 dp, animated with MaterialTheme.motionScheme; while held, the gradient panel on surface, the rule, the month/year marks (tap to jump, 48 dp touch target, contentDescription) and the primaryContainer preview in StreamdTheme.mono at a larger size (date) with labelMedium (weekday). A narrow 36 dp edge target at the right shows the handle again; a tap away from the handle jumps there.
  • Accessibility as in beandroid: contentDescription, stateDescription, progressBarRangeInfo, setProgress through adjust, custom actions for the previous and next month.
  • Icon: add ic_unfold_more.xml (Material Symbols Rounded, weight 400, without android:tint) and StreamdIcons.UnfoldMore.
  • Strings in res/values/strings.xml: scroll by date, previous month, next month, jump to %s. Remove the strings of FastScroller only if the configuration no longer uses them.

Wiring

  • Stream.kt: replace FastScroller with DateScroller over found (the days after the filter and the search), aligned CenterEnd and still ending above the Capture button (bottom = 96.dp). Without a DateScroll (fewer than two days) nothing is drawn. StreamActions (or a parameter of StreamScreen) gains onScrubbing: (Boolean) -> Unit.
  • App.kt: hold scrubbing and hide the ExtendedFloatingActionButton while it is true. It is reset when the tab changes; the DisposableEffect of the scroller reports false when it leaves.
  • streamRows / dayAt of FastScroll.kt (and their tests) go away if nothing uses them any more. handleFraction, rowAt and FastScroller stay for Config.kt.

Screenshot tests (StreamScreenshots.kt, by DateScrollerContent, since layoutlib renders no gesture): one scene with the handle shown and resting, one held, with marks and the preview over the stream (a fixture spanning several months, across a new year, so year marks show), one held on today showing "Today". Record with updateDebugScreenshotTest and look at each image; existing Stream scenes change only if the hidden scroller changed them.

Docs

  • AGENTS.md: replace the description of the Stream tab's FastScroller with DateScroller / DateScroll.kt (FastScroller stays for the configuration); mention the hidden Capture button.
  • README.md and REQUIREMENTS.md (the Stream tab of R35, wherever the fast scroller is described): the time axis, the month marks, the preview, and the accessibility.

Checks: nix build .#streamd-app (unit tests and screenshots), then nix flake check.

Reference: beandroid ui/DateScroller.kt, ui/journal/DateScroll.kt, commits 6f8ea91 and 414698b.

## Plan **Decisions** - Only the Stream tab gets the new scroller. The Tags and File types tabs of the configuration keep `FastScroller`: they are sorted by name, not by date. - The Capture button hides while the scroller is held, as in beandroid. - The large preview says **Today** / **Yesterday** by `dayName`, with the full date below, else the date (with the year only where `showsYear`) and the weekday. **Pure part: `ui/DateScroll.kt`** (from beandroid's `ui/journal/DateScroll.kt`, in the style of this repo) - `DateScroll(newest, oldest, days)` with `fraction`, `dateAt`, `snap`, `months`, `adjust` (`A11Y_STEP`), `dayForMonth`; `MonthMark` and `List<MonthMark>.visible(track, spacing)`. - `dateScroll(days: List<Day>)`: none with fewer than two distinct days. - `dayIndices(leading, days)`: the index of each day header in the `LazyColumn` (one header plus the cards of each day). `leading` is the number of rows above the days (pull banner, unreadable files, working, reminders, "nothing"), which `Stream.kt` already counts for `streamRows`. - The day of the first visible row comes from these indices by binary search, as in beandroid. - Unit tests in `src/test/.../ui/DateScrollTest.kt`: fraction/dateAt round trip, `snap` (exact, between days with a tie going to the newer one, the ends), `months` and `visible` (all → quarters → years), `adjust` (one step to the next or previous day, any other value snaps), `dayForMonth`, `dayIndices` with leading rows, and `dateScroll` being none for one day. **Composable: `ui/DateScroller.kt`** - `DateScroller(scroll, listState, indices, onDraggingChange, modifier)` (stateful: visibility, expansion, drag fraction, a conflated `Channel` of target dates collected with `collectLatest` → `scrollToItem`, `SegmentTick` haptics when the month changes, hidden 1.5 s after scrolling or dragging stops) and the stateless `internal DateScrollerContent(scroll, fraction, dragging, visible, …)`. - Visuals as in beandroid: a handle that is `primary`, 32 → 36 dp, animated with `MaterialTheme.motionScheme`; while held, the gradient panel on `surface`, the rule, the month/year marks (tap to jump, 48 dp touch target, `contentDescription`) and the `primaryContainer` preview in `StreamdTheme.mono` at a larger size (date) with `labelMedium` (weekday). A narrow 36 dp edge target at the right shows the handle again; a tap away from the handle jumps there. - Accessibility as in beandroid: `contentDescription`, `stateDescription`, `progressBarRangeInfo`, `setProgress` through `adjust`, custom actions for the previous and next month. - Icon: add `ic_unfold_more.xml` (Material Symbols Rounded, weight 400, without `android:tint`) and `StreamdIcons.UnfoldMore`. - Strings in `res/values/strings.xml`: scroll by date, previous month, next month, jump to %s. Remove the strings of `FastScroller` only if the configuration no longer uses them. **Wiring** - `Stream.kt`: replace `FastScroller` with `DateScroller` over `found` (the days after the filter and the search), aligned `CenterEnd` and still ending above the Capture button (`bottom = 96.dp`). Without a `DateScroll` (fewer than two days) nothing is drawn. `StreamActions` (or a parameter of `StreamScreen`) gains `onScrubbing: (Boolean) -> Unit`. - `App.kt`: hold `scrubbing` and hide the `ExtendedFloatingActionButton` while it is true. It is reset when the tab changes; the `DisposableEffect` of the scroller reports `false` when it leaves. - `streamRows` / `dayAt` of `FastScroll.kt` (and their tests) go away if nothing uses them any more. `handleFraction`, `rowAt` and `FastScroller` stay for `Config.kt`. **Screenshot tests** (`StreamScreenshots.kt`, by `DateScrollerContent`, since layoutlib renders no gesture): one scene with the handle shown and resting, one held, with marks and the preview over the stream (a fixture spanning several months, across a new year, so year marks show), one held on today showing "Today". Record with `updateDebugScreenshotTest` and look at each image; existing Stream scenes change only if the hidden scroller changed them. **Docs** - `AGENTS.md`: replace the description of the Stream tab's `FastScroller` with `DateScroller` / `DateScroll.kt` (`FastScroller` stays for the configuration); mention the hidden Capture button. - `README.md` and `REQUIREMENTS.md` (the Stream tab of R35, wherever the fast scroller is described): the time axis, the month marks, the preview, and the accessibility. **Checks**: `nix build .#streamd-app` (unit tests and screenshots), then `nix flake check`. Reference: beandroid `ui/DateScroller.kt`, `ui/journal/DateScroll.kt`, commits 6f8ea91 and 414698b.
Author
Collaborator

Implemented in PR #325: #325

Time: approximately 13 minutes (08:17–08:30 UTC).

Token usage, from the session counter immediately before this report: 4,587,618 total tokens, including 4,455,936 cached input tokens; the remainder is 113,002 uncached input tokens and 18,680 output tokens.

Findings and implementation:

  • Ported the local beandroid reference without adding dependencies. The Stream track now measures elapsed days, snaps to days with cards, and uses header indices that include the leading banners, reminders and working cards.
  • Added the expanding handle, month/quarter/year marks and month jumps, relative-day/date preview, month haptics, conflated scrolling and 1.5-second hiding. Capture hides during the gesture and resets on disposal or navigation.
  • Kept accessibility progress and actions on the permanent narrow edge target so TalkBack can still reach them when the handle is hidden. Configuration lists retain their existing row-based scroller.
  • Added date-axis unit tests using a failing-then-passing cycle and reviewed six new light/dark screenshot references. Existing screenshot references did not change.
  • Updated README.md, REQUIREMENTS.md and AGENTS.md; created three signed commits and pushed the branch.

Validation passed: nix build .#streamd-app (offline build, native-backed unit tests and screenshot comparison) and nix flake check.

Implemented in PR #325: https://git.konstantinfickel.de/kfickel/streamd/pulls/325 Time: approximately 13 minutes (08:17–08:30 UTC). Token usage, from the session counter immediately before this report: 4,587,618 total tokens, including 4,455,936 cached input tokens; the remainder is 113,002 uncached input tokens and 18,680 output tokens. Findings and implementation: - Ported the local beandroid reference without adding dependencies. The Stream track now measures elapsed days, snaps to days with cards, and uses header indices that include the leading banners, reminders and working cards. - Added the expanding handle, month/quarter/year marks and month jumps, relative-day/date preview, month haptics, conflated scrolling and 1.5-second hiding. Capture hides during the gesture and resets on disposal or navigation. - Kept accessibility progress and actions on the permanent narrow edge target so TalkBack can still reach them when the handle is hidden. Configuration lists retain their existing row-based scroller. - Added date-axis unit tests using a failing-then-passing cycle and reviewed six new light/dark screenshot references. Existing screenshot references did not change. - Updated README.md, REQUIREMENTS.md and AGENTS.md; created three signed commits and pushed the branch. Validation passed: `nix build .#streamd-app` (offline build, native-backed unit tests and screenshot comparison) and `nix flake check`.
Sign in to join this conversation.
No labels
android
cli
epic
planned
tui
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
kfickel/streamd#324
No description provided.