Adds useAppBadge alongside the existing useFaviconBadge, mounted from the
same FaviconBadge component and gated by the same faviconUnreadBadge
setting. Targets the separate icon Chrome/Edge/WebKit show for an
installed PWA (taskbar/dock/Home Screen/pinned-app-shortcut), which does
not react to favicon changes at all - a merely pinned browser tab is
unaffected, only an actually installed app.
No unmount cleanup: the badge is scoped to the installed app/origin, not
to this component or window, so clearing on every unmount would blank
another still-open window's accurate badge. A count of zero or the
setting being off already clears it through the main effect.
After a reload the unified mailbox showed only the active account's mail;
other accounts appeared only once visited. Intermittent (~1 in 14), and
not a timeout - nothing on this path uses setTimeout/AbortSignal/race.
checkAuth awaits only the active account and restores the rest in a
fire-and-forget background loop, registering each client only after
connect() resolves. buildUnifiedAccountClients drops accounts with no
live client (`if (!c) continue`). And isConnected was persisted, so it
rehydrated stale-true: the account passed the isConnected filter while
failing the client lookup, and connectedAccountsSignature already looked
complete at mount so nothing re-fired - and the effects keyed on it only
recompute counts, never the list.
Clear isConnected/hasError on rehydrate so both the filter and the
signature describe this session, and refetch the open unified/cross list
when an account joins the connected set (growth only, not on first
render, not while a search owns the list). Extracted
connectedAccountsGrew so the membership check is testable without
rendering MailApp.
Verified the reset cannot block reconnection: checkAuth reads the full
account list and never filters on isConnected.
Fixes#950
List rows lay out with items-start, because the sender line, subject and
preview stack and the body should govern the row height. The avatar
inherited that and pinned itself to the top, level with the sender line,
leaving it visibly high against a two- or three-line row.
Give the avatar self-center in both the single-message and thread rows.
The row keeps items-start, so nothing else moves — in particular the
extra-compact checkbox, which is mutually exclusive with the avatar and
carries its own mt-2 offset, is untouched.
Switching folders currently keeps an active search applied and re-runs
it in the newly selected folder — deliberate behavior from #553, where
users wanted the query to follow them across folders. Others expect the
opposite: clicking Inbox/Sent should drop the search and just browse the
folder.
Add an opt-in setting (Settings → email behavior) that reconciles both.
Default is off, so #553's search-follows-you behavior is unchanged for
everyone. When on, any folder click clears the text query and advanced
filters and browses the folder instead of re-running the search.
Applied uniformly across the four folder-entry paths in MailApp — normal
mailboxes, per-account (Pro sidebar) folders, unified views, and cross
views — via one helper so no path diverges.
Opening the toolbar feeds its height back into scrollTop so rows keep
their position on screen. At the top of the list there are no rows above
to hold steady, so that scroll instead slides the first message under the
toolbar — select the top message and it disappears behind the bar acting
on it.
Skip the compensation when the list is already at the top and let the
content move down there, which is what the toolbar taking up space
should do. Mid-list behaviour is unchanged, and the scroll is clamped at
zero so collapsing the toolbar cannot drive it negative.
A message with attachments showed a paperclip and nothing else, so the
only way to reach a file was to open the message, scroll to the bottom
and find it. The list rows never had the information to do better:
EMAIL_LIST_PROPERTIES asked for hasAttachment, a boolean.
Request attachments alongside it and render the files as chips under the
preview, each one opening that file directly. Chips honour the existing
mailAttachmentAction setting, so they preview or download exactly as the
viewer's attachment list does.
Inline parts are filtered out. A single Outlook message routinely carries
half a dozen cid: signature spacers of under 200 bytes; chipping those
would bury the one file the reader actually wants. Only parts that are
neither inline nor cid-referenced, and that carry a filename, are shown —
two at a time, with the remainder collapsed into a count.
Downloads resolve the blob source from the row's own message rather than
the selected one, so they stay correct in the unified inbox where each
row can belong to a different account.
Once a search runs the header field shows the active query, but the only
way back to the full list was to open the panel and clear it there.
Put a clear button at the trailing end of the field, shown only while a
query is applied. The field is now a container rather than one button,
since nesting a button inside a button is invalid markup — the label
area and the clear control are siblings.
The full-screen panel is fixed inset-0, so it sat on top of the very
list it filters: submitting a query worked, updated the list underneath,
and left the reader staring at an empty panel.
Close it on submit, on picking a contact suggestion, and on clear, so
the results it just produced are what you land on. The header field then
shows the active query rather than the placeholder, so a filtered list
does not look unexplained, and tapping it reopens the panel to refine.
SearchBox gains an autoFocus prop so the panel opens straight to the
keyboard instead of needing a second tap.
Phones stacked two 56px bars: the mailbox title, then a row holding
select-all, the search box, the filter toggle and refresh. That is 112px
of chrome before the first message, and the title merely repeats what
the drawer already shows.
The header now carries the account avatar and a tappable search field,
and the second row is presented full-screen when that field is tapped —
so the same SearchBox, suggestions, history and filter chips appear,
none of it rebuilt. Select-all and refresh remain in that panel, and the
list already supports long-press selection and pull-to-refresh.
Tablet and desktop are unchanged; the row is only relocated on phones.
The rail variant flies its popover out sideways, which is right for a
narrow vertical sidebar but wrong at the top of the screen: anchored to
a trigger at the inline-end edge it runs straight off the viewport. In
RTL that edge is the left one, so the menu opened past the left border
and was unreachable.
Add a "header" variant that drops the menu below the trigger and pins it
by its trailing edge, with a max-width so it can never exceed the
viewport. rail and expanded are untouched.
The mobile/tablet sidebar was pinned with physical direction utilities —
left-0 and -translate-x-full — which do not flip under dir="rtl". In
Hebrew the drawer therefore anchored to the left edge and slid in from
the left, away from the hamburger button that opens it.
Use the logical start-0 for the inset, and pair the physical translate
with an rtl: override, since translate-x has no logical equivalent. The
RTL rule is emitted after the LTR one, so it wins the cascade despite
Tailwind wrapping rtl: in a zero-specificity :where().
Recurring events defaulted skip:"omit"+rscale:"gregorian" on every rule,
which Stalwart serialises into the CalDAV RRULE as RSCALE=GREGORIAN;SKIP=OMIT.
DAVx5 rejects that ("Invalid recurrence rule"), breaking Android sync. Those
RFC 7529 fields only matter for non-Gregorian scales / invalid-date handling,
so stop emitting them for plain Gregorian rules.
- lib/recurrence-rule.ts: createRecurrenceRule() helper with no skip/rscale.
- CalendarRecurrenceRule.rscale/skip are now optional.
- event-modal + recurrence-editor build rules via the helper.
- demo fixture cleaned; unit test asserts no skip/rscale.
Closes#805.