The only ways to re-fetch mail are the browser refresh gestures (F5,
Ctrl/Cmd+R, pull-to-refresh) that useRefreshGesture intercepts. On a
desktop that is discoverable enough, but there is no visible control,
and users who reach for one find nothing.
Extract the gesture's inline onRefresh into a shared handleManualRefresh
with a spinner state, then reuse it for a refresh button next to the
filter control in the mail-list toolbar. The icon spins while the fetch
is in flight and the button is disabled until it settles, so repeated
clicks can't stack requests.
Uses the existing common.refresh translation key, so no new strings.
Also guard the login submit with rememberMeEnabled: the checkbox is only
rendered when the server has a SESSION_SECRET, but the value was passed
through unconditionally, asking for a credential cookie the server will
not write.
Clicking an address in the app's own chrome - the recipient card, the
contact sidebar, the contact details - handed a mailto: URL to the OS
mail handler. In a webmail client that goes nowhere: the browser asks
which application should open the link, and the message is composed
somewhere else, if at all. Only users who registered Bulwark itself as
their system mailto handler got a usable result, and even then via a
detour through the OS into a new tab.
A new MailtoLink component sends the click to the built-in composer
instead. It stays a real anchor with an href, so "Copy email address",
middle click and assistive technology keep working; only the plain
left click is redirected, and only when a listener takes it - otherwise
the browser's own behaviour still applies. Modified clicks are left
alone.
The request reaches the mail page through a window event, the way
`bulwark:rate-limit-blocked` already does, rather than threading a
callback through three component levels and five call sites. It goes to
openMailtoDraft rather than the protocol path: an in-app click carries
its own context - the account being read - so the account picker that an
external mailto: needs would only get in the way here.
Message content is deliberately untouched. Plain text bodies render into
the main document rather than the sandboxed frame and their sanitizer
permits mailto:, so a document-wide interceptor would have swept up
untrusted links too; a component cannot be reached from message HTML at
all.
Opening a message with a calendar invitation printed the whole parsed
event to the console on every render - title, description, attendees and
location included - through a leftover `[PARSE DEBUG]` console.log.
The surrounding file already logs via debug.log('calendar', ...) in 22
places, which honours the debugMode setting and its category filters, so
this one line was the outlier. It also ran JSON.stringify with
indentation unconditionally, even though nothing was listening.
Passing the object to debug.log instead leaves the formatting to the
logger, so nothing is serialised unless the calendar debug category is
actually switched on.
If you have a large number of tags, getTagCounts would not be able to get the
unread counts because it did not respect maxCallsInRequest, even though the
value was actually read out, it was just ignored. There are also other places
where the limits were not respected.
Batching is now generalized in a helper that also takes maxObjectsInSet, which
also was ignored, into account and is applied to all functions.
In addition, the dev mock now also advertises and enforces the limits, so these
issues can get picked up during development.
Possible closes#699
Possibly closes#399
Nested tag toasts from drag-and-drop only showed the leaf name for
non-root tags, contradicting the comment above it and making two
same-named leaves under different parents (e.g. Personal/Receipts vs
Work/Receipts) indistinguishable in the toast.
The context menu's markAsRead handler was the one action left reading
the stale contextMenu.data instead of the live-refreshed
contextMenuEmail introduced alongside it, so it could act on outdated
email state while every sibling handler was already updated.
Previously tags where very much focused on color coding email and less about
adding additional information. They were also visualized in different ways in
different locations.
This commit gets rid of all "Color-coding" references, aligns visualization of
the tags across the whole project and tries to improve user experience of using
tags in general.
A search box is shown in the tagging control so the user can quickly search for
a tag if they have a huge (more than 10) amount of tags.
This code is nowhere used, so to prevent extra work during an upcoming refactor
of tags, it is removed and some related tests are now actually made useful
If you carefully crafted your tags and then click this button by accident, all
your hard work is gone. A confirmation message would be the other solution but
since I have difficulty to grasp when you would need such a button, I propose
to just remove it.
- levels are joined by forward slashes in the keywords
- behaviour is opt-in for now
- long paths are shortened if there is not enough display room
Closes#687.
- tailwind.config.ts is not actually being used so removed
- moved animate-fade-in to globals.css
- added tw-animate-css package to make animate-in work
- added a definition for animate-shake as it doesn't exist in tw-animate-css
process.env coerces assigned values to strings, so
process.env.TZ = originalTZ left the literal string "undefined"
behind (instead of clearing TZ) when it was unset before the test
ran. Delete the var in that case instead of assigning undefined.
supportsContacts and supportsCalendars checked the server-wide session
capability. An account with jmap-contact-* / dav-card-* revoked still got
Contacts, and one with jmap-calendar-* / dav-cal-* revoked still got
Calendars, with every action failing on an authorization error.
Setting process.env.TZ at module scope without restoring it could leak
into other test files sharing the same Vitest worker. Match the
beforeAll/afterAll restore pattern already used in
lib/__tests__/calendar-utils.test.ts.
buildForwardAsAttachmentPayload named the synthetic .eml attachment
using the user's own emailDownloadTemplate, which by default embeds
sender/recipient display names. Since this attachment can go to an
external recipient (e.g. an upstream spam gateway, or anyone else),
the filename now always renders as "{date}-{subject}.eml" regardless
of the user's configured template, while still honoring their
space/case/diacritics preferences.
The "empty outer body + message/rfc822 attachment" unwrap heuristic
(built for Outlook's blank-body forwards) was also matching Forward as
attachment sends, which intentionally use a blank body. Once unwrapped
for inline preview, the .eml attachment itself was filtered out of the
attachment list, so a sent forward-as-attachment message with a large
attached .eml appeared to have no attachments at all.