Say what the availability bar is showing, and show all of it #182

Closed
opened 2026-09-01 16:44:01 +00:00 by jcoffey-dev · 0 comments
Owner

Parts 1 and 2 of #172. Part 3 — the separate OX-style scheduling view — is deliberately not here; see the bottom.

The two complaints, and what they were

"No labels, so it's unclear which time frame it covers." The bar had no marks of any kind. Whether it was a whole day or working hours was unanswerable except by dragging the event about and watching where its own outline moved — which is exactly what the issue describes doing.

"Only shows a single day, even for multi-day events." Literally true in the code: the window was startOfDay(start) to + DAY_MS, and the free/busy lookup asked for that same 24 hours. A three-day event showed day one and gave no sign there was more.

What it does now

The window covers whole days from the day the event starts to the day it ends, and the lookup asks for the range it draws.

Above the bars there's an axis: hours every three across a single day, every six across two, day names beyond that. The same marks are drawn down the bars, so a busy block can be read against the hour it starts at instead of hovered for a tooltip.

Whole days, always — a bar starting at the event's own start time would move under you every time you nudged it, and "busy from about a third of the way along" is not a time anyone can read.

A week is as far as it goes. Something running longer isn't an event anybody is hunting a free slot in, and a month at eight pixels a day would say nothing; it reports how many days it left out instead ("The event runs 23 days longer than this shows.").

One correctness fix carried along: the span is measured between two real midnights rather than counted in 24-hour days. Twice a year those differ, and every position on the bar is a fraction of that span, so on a clock-change day the old arithmetic ended the bar an hour early and put every block after the change in the wrong place.

The mock

It answered with a single busy block on the first day whatever range it was asked for — all a day-wide bar could ever show. Left as-is, a multi-day bar would have looked like everyone was free from day two on, which is the exact failure this change is meant to prevent. It now answers across the range.

Verification

npm run typecheck, npm test (561 pass, 11 new on the window helper) and npm run build green. The helper is pure and takes the fiddly cases: an all-day event ending at the midnight it doesn't touch, start equal to end, tick spacing at each scale, fractions, the week cap, and that the span is the distance between real boundaries.

By hand against dev:mock:

  • one-day event → axis reads 12:00 AM / 6:00 AM / 12:00 PM / 6:00 PM / 12:00 AM, three-hourly ticks, busy block and event outline in place
  • extended to 1–4 Sep → heading becomes "Availability, 9/1/2026 to 9/4/2026", axis switches to Tue / Wed / Thu / Fri, four busy blocks, one per day
  • extended to 30 Sep → seven days shown plus "The event runs 23 days longer than this shows."

Screenshots of the one-day and multi-day bars are attached in the conversation.

Still open on #172

Part 3 needs a decision this PR doesn't make: what to show for participants who have no free/busy to read. Today they are silently dropped from the bar. In a single row that is survivable; in a scheduling grid, blank space reads as "free", and for people arriving through a ticketing system — the case in #173 — external attendees are the normal case, not the exception. A grid wants three states (busy / free / unknown), and it's also worth confirming whether Stalwart grants free/busy between accounts by default or wants an explicit ACL. Leaving #172 open for that.

Merged 2026-09-01 as coffey-labs/ihasmail@fb1ad3c5c8

Rebuilt from: git history, session transcript.

Parts **1 and 2** of #172. Part 3 — the separate OX-style scheduling view — is deliberately not here; see the bottom. ## The two complaints, and what they were **"No labels, so it's unclear which time frame it covers."** The bar had no marks of any kind. Whether it was a whole day or working hours was unanswerable except by dragging the event about and watching where its own outline moved — which is exactly what the issue describes doing. **"Only shows a single day, even for multi-day events."** Literally true in the code: the window was `startOfDay(start)` to `+ DAY_MS`, and the free/busy lookup asked for that same 24 hours. A three-day event showed day one and gave no sign there was more. ## What it does now The window covers **whole days from the day the event starts to the day it ends**, and the lookup asks for the range it draws. Above the bars there's an **axis**: hours every three across a single day, every six across two, day names beyond that. The same marks are drawn down the bars, so a busy block can be read against the hour it starts at instead of hovered for a tooltip. Whole days, always — a bar starting at the event's own start time would move under you every time you nudged it, and "busy from about a third of the way along" is not a time anyone can read. **A week is as far as it goes.** Something running longer isn't an event anybody is hunting a free slot in, and a month at eight pixels a day would say nothing; it reports how many days it left out instead ("The event runs 23 days longer than this shows."). One correctness fix carried along: the span is measured **between two real midnights** rather than counted in 24-hour days. Twice a year those differ, and every position on the bar is a fraction of that span, so on a clock-change day the old arithmetic ended the bar an hour early and put every block after the change in the wrong place. ## The mock It answered with a single busy block on the first day whatever range it was asked for — all a day-wide bar could ever show. Left as-is, a multi-day bar would have looked like everyone was free from day two on, which is the exact failure this change is meant to prevent. It now answers across the range. ## Verification `npm run typecheck`, `npm test` (561 pass, 11 new on the window helper) and `npm run build` green. The helper is pure and takes the fiddly cases: an all-day event ending at the midnight it doesn't touch, start equal to end, tick spacing at each scale, fractions, the week cap, and that the span is the distance between real boundaries. By hand against `dev:mock`: - one-day event → axis reads 12:00 AM / 6:00 AM / 12:00 PM / 6:00 PM / 12:00 AM, three-hourly ticks, busy block and event outline in place - extended to 1–4 Sep → heading becomes "Availability, 9/1/2026 to 9/4/2026", axis switches to Tue / Wed / Thu / Fri, four busy blocks, one per day - extended to 30 Sep → seven days shown plus "The event runs 23 days longer than this shows." Screenshots of the one-day and multi-day bars are attached in the conversation. ## Still open on #172 Part 3 needs a decision this PR doesn't make: **what to show for participants who have no free/busy to read.** Today they are silently dropped from the bar. In a single row that is survivable; in a scheduling grid, blank space reads as "free", and for people arriving through a ticketing system — the case in #173 — external attendees are the normal case, not the exception. A grid wants three states (busy / free / unknown), and it's also worth confirming whether Stalwart grants free/busy between accounts by default or wants an explicit ACL. Leaving #172 open for that. **Merged** 2026-09-01 as coffey-labs/ihasmail@fb1ad3c5c858 <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.