Schedule against the whole guest list, and say who cannot be read

The availability bar showed the guests it had free/busy for and quietly
left out the ones it did not. In one row that is survivable. As a grid it
would be a lie: a row with nothing in it reads as a diary with nothing in
it, and "we cannot see this person's calendar" is the one thing that must
not look like "this person is free".

So everyone the event concerns now gets a row, and the ones with no
free/busy to read are drawn hatched rather than empty, with a line under
the grid saying how many and why. You get a row too, first. Scheduling
around the other people and not around yourself is how two things end up
at the same time, and the organiser was the one calendar the panel never
showed.

Your own row is never unknown. Where the directory does not list you under
the address your identity sends from -- an alias, a login that differs
from the address -- the account is still yours to read, and Stalwart
answers for it under the account's own id.

The window steps backwards and forwards a screenful at a time without
touching the event, which is the "movable forwards & backwards" the report
asks for, and offers its way back when you have wandered off.

And the bars are somewhere to put the event rather than only something to
read: the pointer shows the half hour it is over, and a click moves the
event there keeping its length. Clicking while stepped away moves the
event to where you clicked and returns the view to it, so it lands where
you were looking instead of jumping.

Free/busy is answered per principal, and only the server's own accounts
are principals. Somebody at another domain has none to read -- which is
not a gap to be closed, it is what the protocol can see -- so the grid
says so rather than drawing them blank.

Closes #172
This commit is contained in:
2026-09-01 10:05:50 -07:00
parent fb1ad3c5c8
commit e1402e472f
4 changed files with 174 additions and 41 deletions
+15
View File
@@ -918,6 +918,21 @@ select optgroup { background-color: var(--bg-elev); color: var(--fg); }
.freebusy .fb-axis-label.end { left: auto; right: 0; transform: none; }
.freebusy .fb-tick { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--border); }
.freebusy .fb-tick.major { background: var(--fg-faint); }
.freebusy .fb-head { display: flex; align-items: center; gap: 6px; margin-bottom: 2px; font-size: .85em; }
.freebusy .fb-range { font-weight: 600; }
.freebusy .fb-self { font-weight: 600; }
/* A bar is somewhere to put the event, not only something to read. */
.freebusy .fb-bar { cursor: crosshair; }
.freebusy .fb-guide { position: absolute; top: -2px; bottom: -2px; width: 1px; background: var(--fg); opacity: .55; pointer-events: none; }
/*
* "We cannot say", which is not the same as "free" and must not look like it.
* Somebody outside this server has no free/busy to read, and an empty bar in a
* grid reads as an empty diary -- the one reading that is certainly wrong.
*/
.freebusy .fb-bar.no-data {
background-image: repeating-linear-gradient(135deg, var(--bg-sunken), var(--bg-sunken) 4px, var(--border) 4px, var(--border) 8px);
cursor: crosshair;
}
/* ==========================================================================
Files