Commit Graph
52 Commits
Author SHA1 Message Date
Matthieu MALVACHE 77c1133f06 fix: improve mobile/tablet layout transitions and touch interactions
- Remove isMobile/isTablet JS state from layout classes to eliminate
  orientation-change blink (CSS breakpoints apply instantly)
- Extract dismissViewer() helper for return-to-list pattern (was duplicated 4x)
- Convert more-actions dropdown from hover-only to click-toggle (works on touch)
- Add Escape key and role attributes to more-actions menu
- Reset showMoreActions on email change to prevent stale menu
- Move overflow-x:auto to iframe inner body stylesheet (was inert on iframe element)
- Reset tabletListVisible when crossing to desktop breakpoint
2026-03-16 16:14:40 +01:00
Matthieu MALVACHE 27911dfe76 feat: add UX & robustness improvements (iframe rendering, retry, mobile, polish)
P0 Core:
- Sandboxed iframe rendering for rich HTML emails (CSS isolation)
- API retry with exponential backoff for transient JMAP failures
- Mobile bottom action bar with touch-friendly email actions
- Long-press context menu + tap-to-expand submenus on touch devices

P1 Polish:
- Tag counts sidebar section with JMAP batch queries
- Empty folder option for Junk/Trash with batch delete
- Extra-compact density option (28px rows, 44px on touch)
- SPF/DKIM/DMARC security tooltips with plain-language explanations
- Resizable sidebars with drag, touch, and keyboard support
- Expandable sender info panel in email viewer

All 8 locales updated (en/fr/ja/es/it/de/nl/pt).
2026-03-16 15:40:24 +01:00
Matthieu MALVACHE 4aaef1ebf1 fix: resolve calendar crash on undefined duration and sieve filter save error
parseDuration() now handles undefined event.duration gracefully instead
of crashing on .match(). CalendarEvent.duration type updated to reflect
that JMAP servers may omit this field.

Sieve filter activation replaced isActive (server-set per RFC 9661) with
onSuccessActivateScript. Create/update calls merged with activation into
single JMAP roundtrips.

Closes #31, closes #21
2026-03-16 12:41:10 +01:00
Matthieu MALVACHE c787acc34f fix: resolve context menu submenu usability and move-to-folder failures
The context menu's "Move to folder" submenu was practically unusable:
scroll events inside the folder list closed the entire menu, and moving
the mouse from the trigger to the submenu dismissed it instantly.

Additionally, the JMAP moveEmail call silently discarded server errors
and always used the primary account ID, causing moves to appear to
succeed locally while the server rejected them.

Closes root-fr/jmap-webmail#19
2026-03-02 09:08:36 +01:00
Matthieu MALVACHE 6a49514f7a feat(auth): add "Remember me" session persistence and simplify 2FA UX
Basic Auth sessions now persist across page refreshes via an encrypted
httpOnly cookie (AES-256-GCM). The feature is opt-in: admin sets
SESSION_SECRET env var, user checks "Remember me" at login.

Rework the 2FA input from a checkbox+animated panel to a discreet
"I have a 2FA code" text link that reveals the TOTP field on click.
2026-02-26 00:45:26 +01:00
Matthieu MALVACHE 2f20aa835b feat(auth): add RP-initiated logout and OAuth unit tests
Logout now revokes the refresh token and redirects to the IdP's
end_session_endpoint so the SSO session is fully terminated. The
end_session_url is validated server-side (HTTPS only) before being
returned to the client, preventing open redirect attacks.

Also adds 14 unit tests covering PKCE (including RFC 7636 test vector)
and OAuth discovery (fallback, caching, required field validation).
2026-02-26 00:07:38 +01:00
Matthieu MALVACHE d235403d4c feat(auth): add explicit OAuth issuer URL and improve discovery diagnostics
Support external IdPs (Keycloak, Authentik) that handle auth separately
from the JMAP server via OAUTH_ISSUER_URL. Also surfaces discovery
failures with logged errors and a visible warning banner instead of
silently hiding the SSO button.
2026-02-25 23:38:27 +01:00
Matthieu MALVACHE 2ae0520026 feat(auth): add OAuth2/OIDC with PKCE for SSO login
Add OAuth2 authorization code flow with PKCE as an alternative to
basic auth. Supports Stalwart's built-in OAuth provider with automatic
token refresh, session persistence via httpOnly cookies, and graceful
fallback to basic auth when OAuth is not configured.
2026-02-25 16:04:17 +01:00
Matthieu MALVACHE 5581ac768b refactor(jmap): extract shared constants and simplify client patterns
DRY: extract DEFAULT_MAILBOX_RIGHTS, EMAIL_LIST_PROPERTIES,
namespaceMailboxIds(), computeHasMore(), buildStatePollingRequest(),
STATE_TYPE_MAP, and parseEmailHeaders() to eliminate duplication
across multiple methods.

Simplify getContacts, batchMarkAsRead, batchMoveEmails, connect,
getBlobDownloadUrl, getEventSourceUrl, and ping with cleaner patterns.

Remove redundant comments and leftover console.log debug statements.
2026-02-25 14:15:51 +01:00
Matthieu MALVACHE 8843b70add fix(jmap): use path extraction instead of origin-length slicing for URL rewrite
When Stalwart returns session URLs with explicit :443 port, the URL
parser normalizes it out but the original string keeps it, causing
string slicing to produce wrong offsets. Now extracts the path by
finding the first / after // instead of relying on origin length.
2026-02-25 12:44:41 +01:00
Matthieu MALVACHE 602d50b703 fix(email): include identity display name in From header when sending
Sent emails only included the bare email address in the From field,
so recipients saw <user@example.com> with no display name. Now the
identity name from the JMAP server is included in the From header
for composed emails, quick replies, and saved drafts.

Also sorts identities so the one matching the login username is
selected by default in the composer dropdown.
2026-02-25 12:35:00 +01:00
Matthieu MALVACHE 97ffb72d04 fix(jmap): rewrite session URLs to match user-configured server origin
JMAP servers (e.g. Stalwart behind Docker) may return internal hostnames
in their session response (https://jmap/ instead of the actual server URL).
Replace the origin of all session URLs (apiUrl, downloadUrl, uploadUrl,
eventSourceUrl) with the user-configured server origin, using string
slicing to preserve RFC 6570 template placeholders without encoding.

Fixes #16
2026-02-24 15:17:03 +01:00
Matthieu MALVACHE 403eb565c9 fix(login): detect CORS errors and show actionable message instead of generic network failure
When the JMAP server is reachable but lacks CORS headers, users saw
"Unable to reach the server" which is misleading. Now a no-cors probe
distinguishes CORS blocking from true network failure, surfacing a
specific message that points users to their server's CORS config.
2026-02-23 18:19:28 +01:00
Matthieu MALVACHE 82a27a1f52 feat(calendar): add drag-create, resize, recurring scope, quick-create, and duplication
Address GitHub issue #13 (events not spanning real duration, hour label alignment)
and add five standard calendar UX features:

- Click-drag on empty time slots to create events with pre-filled time range
- Resize events by dragging bottom edge handle (15-min snap, optimistic JMAP update)
- Recurring event edit/delete scope dialog (this/following/all occurrences)
- Double-click quick create with inline title input (PT1H default)
- Event duplication button in modal (clones +1 day, opens for editing)

Shared interaction logic extracted to hooks/use-time-grid-interactions.ts.
All features include i18n (8 locales), accessibility (ARIA, pointer events),
and proper error handling with toast feedback.
2026-02-22 17:31:16 +01:00
Matthieu MALVACHE 40c7fa3649 feat: add UI/UX polish, templates settings, confirm dialog, welcome banner, and navigation rail
- Add ConfirmDialog component with promise-based useConfirmDialog hook
- Add WelcomeBanner onboarding component with localStorage persistence
- Add NavigationRail (desktop icon rail + mobile bottom tab bar)
- Polish login form (shake on error, TOTP slide, password visibility, session expired banner)
- Add inline form validation with shake animation in email composer and contacts
- Add empty state patterns for contacts (no data vs no search results)
- Improve toast system with undo action support and typed durations
- Add template settings tab to settings page
- Refactor sidebar (cleaner code, remove unused imports)
- Add WCAG AA reduced-motion, safe area insets, sr-only live region
- Add shake/slide animations in globals.css
- Update i18n for all 8 locales
2026-02-17 02:30:09 +01:00
Matthieu MALVACHE 7da9fa61e2 feat(templates): add email templates with placeholder variables and composer integration
Local-storage templates with dynamic {{variable}} placeholders that auto-fill
from composer context (recipient, sender, date/time). Includes template manager,
category filtering, quick picker in composer toolbar, and settings tab.
48 tests covering placeholder extraction, filling, and variable suggestions.
2026-02-17 01:34:13 +01:00
Matthieu MALVACHE 5d2f5dcf7e feat(calendar): add participant scheduling with iTIP invitations and inline calendar invitation banner
Add organizer/attendee UI with RSVP buttons (accept/maybe/decline),
contact autocomplete in participant input, scheduling message support
via sendSchedulingMessages flag, and DnD notification for participant
events. Add inline calendar invitation banner in email viewer that
auto-detects .ics attachments, parses event details, and allows RSVP
or import to calendar. Includes cancellation display and 51 tests.
2026-02-17 01:09:03 +01:00
Matthieu MALVACHE 613eb6ce84 fix(email): improve dark mode readability and fix long subject overflow
Dark mode color transform had 3 bugs preventing it from working:
- Hook only ran when external content was blocked, not for all emails
- Checked `theme` ('system') instead of `resolvedTheme` ('dark')
- Only transformed CSS style attrs, missing <font color> and bgcolor

Replaced invert+boost algorithm with blend-toward-white for text
(preserves hue, brighter results) and added background darkening
for light email backgrounds. Also handles color/bgcolor HTML attrs.

Fixed long email subjects pushing action buttons off-screen by adding
min-w-0 to the email viewer flex container in the page layout.
2026-02-17 00:38:46 +01:00
Matthieu MALVACHE a603ee496b feat(calendar): add event notifications with client-side alert evaluation
Evaluate JMAP CalendarEventAlert triggers client-side and display toast
notifications when alert times are reached. Extracts notification sound
into shared utility and mounts ToastContainer globally (fixes silent
toast failures across all components).

- Pure alert utilities: offset parsing, fire time computation (start/end),
  effective alerts resolution with useDefaultAlerts, pending alert filtering
- Zustand persist store for acknowledged alert deduplication (24h retention)
- Global hook with 60s interval check, proactive 24h event fetch via ref
  (isolated from calendar store), stale closure protection
- Settings: calendarNotificationsEnabled, calendarNotificationSound toggles
- Toast icon customization, CalendarAlertProvider in layout
- i18n: all 8 locales (EN/FR/JA/ES/IT/DE/NL/PT)
- 44 tests (36 alert utils + 8 notification store), 558 total passing
2026-02-16 23:46:12 +01:00
Matthieu MALVACHE 9be02e8dd7 feat(filters): add email filters with Sieve rules, review fixes and hardening
Implement JMAP Sieve Scripts (RFC 9661) with visual rule builder and raw
Sieve editor. Includes post-review fixes: parser validation guards,
generator empty-rule skipping, JMAP client error hardening, focus trap
accessibility, mailbox name fix, toast validation feedback, auto-save with
rollback, and "Reset to visual builder" for opaque scripts. 514 tests pass.
2026-02-16 23:07:06 +01:00
Matthieu MALVACHE b84e543e2e feat(calendar): add drag-and-drop rescheduling and iCalendar import
Calendar phase 2 features with review-driven fixes:
- Drag events in week/day views to reschedule (15-min snap intervals)
- Drag events in month view to change date (preserves time)
- Visual snap indicators with time labels during drag
- iCalendar (.ics) import via JMAP CalendarEvent/parse
- Import modal with file upload, event preview, calendar selector
- File validation (5MB max), bulk import with progress
- i18n: calendar.import.* and event_move_error keys (8 locales)
2026-02-16 21:07:44 +01:00
Matthieu MALVACHE a27196bdeb feat(calendar): add JMAP Calendar integration with full review fixes
Calendar phase 1 with comprehensive review-driven hardening:
- JMAP CalendarEvent CRUD (RFC 8984 types, capability detection)
- Month/week/day/agenda views with multi-day event spanning
- Column-based overlap layout for concurrent events
- Event modal with create/edit/delete, recurrence, reminders
- Mini-calendar sidebar with calendar visibility toggles
- Settings: firstDayOfWeek and timeFormat wired to all views
- Locale-aware date formatting via next-intl useFormatter (8 locales)
- Push notification handling for Calendar/CalendarEvent state changes
- ARIA grid roles, event card labels, focus trap, 44px touch targets
- Input validation, color sanitization, timezone auto-detection
- Error handling with toast feedback, capability page guard
- ICU pluralization for alert translations, debug logger integration
2026-02-16 20:32:06 +01:00
Matthieu MALVACHE 83e01ae551 feat: add contacts phase 2, advanced search, vacation responder, Docker & TOTP 2FA
Contacts:
- Contact groups/lists with JMAP members map and composer expansion
- vCard import/export with RFC 6350 parser and duplicate detection
- Bulk operations (multi-select, delete, group add, export)

Search:
- Advanced search panel with JMAP filter fields
- Search chips for active filters visualization
- Debounced inputs with AbortController deduplication

Vacation:
- JMAP VacationResponse singleton management
- Settings tab with date range and message configuration
- Sidebar indicator when vacation responder is active

Auth:
- TOTP 2FA support with Stalwart-compatible password$totp format

Infrastructure:
- Docker multi-stage build with standalone Next.js output
- Structured server-side logger with text/JSON format
- CSP Report-Only and security headers via proxy middleware
- Layout refactoring (HTML structure in root layout)
- Playwright E2E framework setup

Testing: 450+ tests (identity, contacts, vCard, threads, headers, components)
i18n: All new strings added to all 8 locales
2026-02-16 18:46:33 +01:00
Matthieu MALVACHE dddbcf75db fix(email): resolve layout overflow and blocked image empty spaces
Remove width: max-content and display: inline-block from email content
CSS that caused horizontal scroll and left-side text clipping. Collapse
empty table cells/containers when external images are blocked to prevent
large blank areas in newsletter emails.
2026-02-16 17:18:38 +01:00
Matthieu MALVACHE 17abe7ff56 feat(contacts): add address book with JMAP sync and local fallback
Implement Phase 1 of contacts support — the biggest missing feature
on the roadmap. Uses RFC 9553 (JSContact) data model and RFC 9610
(JMAP for Contacts) when the server advertises the capability,
falling back to localStorage for servers without contact support.

- JMAP types for ContactCard, AddressBook, NameComponent, etc.
- JMAP client methods: CRUD, search, address book listing
- Refactored request() to accept dynamic `using` capabilities
- Zustand contact store with dual-mode persistence
- Full contacts page with two-column layout (list + detail/edit)
- Contact form with multi-email/phone, context labels, validation
- Composer autocomplete on To/Cc/Bcc with keyboard nav and ARIA
- Sidebar navigation link to contacts
- Auth integration: fetch on login, clear on logout
- i18n: contacts.* namespace across all 8 languages
2026-02-16 17:07:45 +01:00
Matthieu MALVACHE e600cd387d fix(security): prevent global CSS injection from email style tags
Emails containing <style> tags were injecting global CSS rules that
affected the entire application UI. For example, button:hover rules
from emails would override all button hover states app-wide.

Changes:
- Block <style> tags in email sanitization config (security fix)
- Keep inline style attributes for element-specific formatting
- Add explicit bg-transparent to banner buttons (defense in depth)
- Add CSS isolation to banner container
- Fix theme dependency in email content memoization

This prevents malicious or poorly-formatted emails from breaking the
UI or being used for phishing via CSS injection attacks.
2026-01-08 19:18:59 +01:00
Matthieu MALVACHEandClaude Sonnet 4.5 395f92c47f feat(email): add newsletter unsubscribe and enhance dark mode
Implement RFC 2369 List-Unsubscribe support with security validation (protocol whitelist, XSS prevention), two-step confirmation UI, and localStorage persistence. Add intelligent color transformation for dark mode to fix unreadable inline email styles using WCAG 2.0 luminance calculation. Complete batch spam undo operations for context menu multi-select. Update documentation with new features and comprehensive test coverage (97 tests).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-08 18:39:27 +01:00
Matthieu MALVACHEandClaude Sonnet 4.5 da9fa4e28a feat(email): add spam reporting with bidirectional handling
Implement comprehensive spam management allowing users to report spam emails and restore false positives. Emails are moved to/from the Junk folder with toast notifications and undo support.

Key features:
- Mark as spam: moves email to Junk folder with 5-second undo
- Mark as not spam: restores email from Junk to inbox
- Batch spam operations via context menu
- Smart toggle based on current folder (Junk vs other folders)
- Keyboard shortcut (Shift+!) for quick spam reporting
- Full i18n support (EN/FR)
- Shared mailbox compatibility

UX improvements:
- Toast notifications with undo action using sonner library
- Auto-deselect email after spam action
- Visual distinction with red destructive styling
- Desktop button + context menu + keyboard shortcuts

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-08 17:55:24 +01:00
Matthieu MALVACHE f62f5adf29 refactor(identity): enhance i18n and improve sub-address detection
Complete internationalization coverage for all identity-related UI elements,
including mobile navigation, batch actions, and recipient formatting. Improve
sub-address badge to detect and display tags for emails received at
user+tag@domain.com addresses, not just sent emails. Add contextual recipient
display showing "me" for current user and "Name and X others" for multiple
recipients. Update documentation to reflect completed identity management
implementation.
2026-01-08 17:20:13 +01:00
Matthieu MALVACHEandClaude Sonnet 4.5 e8d6e0431f test(identity): add comprehensive unit tests for security utilities
- Setup Vitest testing framework with React Testing Library
- Add 27 test cases for email-sanitization.ts
  - XSS prevention (scripts, event handlers, iframes)
  - Safe HTML preservation (tables, formatting)
  - Signature-specific sanitization rules
  - HTML parsing safety checks

- Add 29 test cases for validation.ts
  - RFC 5322 email validation
  - Header injection prevention
  - Length limit enforcement
  - Email list validation

Coverage: >90% for both security-critical utilities
Zero new runtime dependencies (dev-only)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-08 16:19:45 +01:00
Matthieu MALVACHE 040b34977b refactor(identity): improve performance and code quality
- Add useMemo for expensive tag suggestions computation
- Add useCallback for identity CRUD handlers
- Extract magic numbers to constants (MAX_RECENT_TAGS, MAX_TAG_LENGTH)
- Improve JMAP error messages with context

Performance: Reduces unnecessary re-renders in identity modal
Maintainability: Makes limits configurable and self-documenting
UX: Users get clearer error messages when operations fail

No functional changes. All tests passing.
2026-01-08 16:12:53 +01:00
Matthieu MALVACHE d301dd15ff security: fix XSS vulnerabilities in identity management
- Add unified email sanitization utility (lib/email-sanitization.ts)
  - Safe HTML parsing using DOMParser instead of innerHTML
  - Consistent DOMPurify configuration across components
  - Separate signature sanitization with stricter rules

- Add secure email validation (lib/validation.ts)
  - RFC 5322 compliant validation
  - Block header injection attempts (\r\n<>)
  - Validate comma-separated email lists

- Fix HTML signature XSS (identity-form.tsx)
  - Sanitize signatures before storage and rendering
  - Add live preview with safe rendering
  - Input length limits for defense in depth

- Fix innerHTML XSS (email-viewer.tsx, thread-conversation-view.tsx)
  - Replace unsafe innerHTML with DOMParser for format detection
  - Use shared EMAIL_SANITIZE_CONFIG
  - Add <meta>, <link>, <base> to forbidden tags

Security issues resolved:
- H1: Stored XSS in HTML signatures (CVSS 7.1)
- H2: XSS during HTML parsing (CVSS 6.8)
- M1: Email validation bypass (CVSS 4.3)
- M3: Inconsistent sanitization (CVSS 5.0)

Zero new dependencies. All tests passing.
2026-01-08 16:12:53 +01:00
Matthieu MALVACHE 275aa8290f feat(identities): Add identity management and sub-addressing support
Enable users to manage multiple sending identities and organize incoming mail with sub-addressing (user+tag@domain.com format). Provides better email organization, privacy through disposable addresses, and flexibility for users with multiple roles or accounts.
2026-01-08 16:12:53 +01:00
Matthieu MALVACHE e50404cf42 feat(identities): Use JMAP identities for email sender address
- Add getIdentities() method to JMAP client
- Fetch and store identities in auth store on login
- Add From selector in composer (dropdown for multiple identities)
- Pass identity email/ID through send flow instead of using username
- Add translations for From field (en/fr)

This allows proper sender address handling for LDAP and other auth
backends where username differs from email address.

Closes #5
2026-01-08 01:19:09 +01:00
Matthieu MALVACHE 7d77415801 feat: Add search pagination and UX improvements
- Add pagination support to searchEmails (was limited to 50 results)
- Search now scoped to current mailbox/folder
- Support shared mailbox folders in search
- Display total results count (e.g., "50 of 1400 conversations")
- Add clear (X) button to search input
- Re-run search when changing folders during active search
- Remove unused sidebar screenshot
2025-12-17 18:32:47 +01:00
Matthieu MALVACHE 8eefba5047 feat: Add email threading support with Gmail-style conversation view
- Implement thread grouping by threadId in email list
- Add ThreadListItem with collapsed/expanded states for desktop
- Create ThreadConversationView for full-screen mobile experience
- Add thread utility functions for grouping and sorting
- Extend JMAP client with getThread() and getThreadEmails() methods
- Add keyboard shortcut 'x' to expand/collapse threads
- Add thread expansion state management in email store
- Fix React hooks order in login page
- Add .env.example for environment configuration
- Add husky for git hooks
2025-12-10 17:38:03 +01:00
Matthieu MALVACHE f344725e2c feat: Add keyboard shortcuts, drag-drop, context menus, and mobile responsive design
New features:
- Keyboard shortcuts (j/k navigation, r/R/f for reply/forward, s star, e archive, # delete)
- Drag-and-drop emails to mailbox folders with multi-select support
- Right-click context menus on email list items with all actions
- Mobile-responsive adaptive layout with hamburger menu navigation

New files:
- hooks/use-keyboard-shortcuts.ts - Global keyboard shortcut handling
- hooks/use-email-drag.ts, hooks/use-mailbox-drop.ts - Native HTML5 DnD API
- hooks/use-context-menu.ts, components/ui/context-menu.tsx - Context menu system
- hooks/use-media-query.ts, stores/ui-store.ts - Responsive breakpoint handling
- components/layout/mobile-header.tsx - Mobile navigation header
- contexts/drag-drop-context.tsx - Drag-drop state management
- components/keyboard-shortcuts-modal.tsx - Help modal (? key)
- components/email/email-context-menu.tsx - Email-specific context menu

Updated TODO.md with completed tasks and code audit summary
2025-12-10 16:58:01 +01:00
Matthieu MALVACHE 81ec4cb471 feat: Add error boundaries and real-time push notifications
- Implement error boundaries with graceful fallbacks for all major components
  (sidebar, email list, email viewer, composer)
- Add push notifications via EventSource for real-time email updates
- Show connection status indicator in sidebar footer
- Play notification sound and show toast for new emails
- Add global error handler and error reporting utilities
- Configure ESLint with modern flat config
- Various fixes: unused variable warnings, proper cleanup on disconnect
- Update translations for error messages (EN/FR)
2025-12-10 00:43:46 +01:00
Matthieu MALVACHE 63cb80cb1d feat: Wire up disconnected settings to actual functionality
- showPreview: Conditionally render email preview in list based on setting
- externalContentPolicy: Control external content (ask/block/allow modes)
- deleteAction: Move to trash or permanently delete based on preference
- debugMode: Add conditional logging via new lib/debug.ts utility

Also includes:
- Add moveToTrash method to JMAP client for trash-based deletion
- Update TODO.md with verified implementation status
- Migrate middleware.ts to proxy.ts (Next.js deprecation)
2025-12-09 23:51:20 +01:00
Matthieu MALVACHEandClaude 1e563a8973 fix: Fix sidebar footer overflow causing page height extension
- Add overflow-hidden to sidebar container to prevent absolute positioned menu from extending page height
- Improve theme consistency in email viewer security indicators
- Add AI spam analysis display (X-Spam-LLM header support)
- Enhance dark mode support with better color variables
- Update CLAUDE.md with commit/push guidelines

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-02 14:48:45 +02:00
Matthieu MALVACHE b2598caf90 fix: Implement infinite scroll without total count from server
Fix pagination to work with Stalwart JMAP server which doesn't return
the total count. Instead of relying on total:
- If we get exactly limit emails (50), assume there might be more
- If we get fewer than limit, we've reached the end

Also fixed observer target rendering - it now always renders instead of
being conditional, ensuring the intersection observer can work properly.
2025-10-02 14:44:37 +02:00
Matthieu MALVACHE aa7b8c1604 feat: Implement infinite scroll with pagination for email list
Add pagination support to load emails progressively as user scrolls:
- Update JMAP client getEmails() to accept position parameter
- Return pagination metadata (hasMore, total) from getEmails()
- Add loadMoreEmails() method to email store for loading next page
- Implement intersection observer in EmailList for automatic loading
- Display loading indicator while fetching more emails
- Show "no more emails" message at end of list

Users can now seamlessly scroll through all emails, loading 50 at a time.
2025-10-02 14:34:31 +02:00
Matthieu MALVACHE 5f45cbe993 feat: Add comprehensive shared folders support with multi-account access
Implemented full support for JMAP shared folders (delegated mailboxes):

Core Features:
- Multi-account mailbox fetching from all available JMAP accounts
- Virtual "Shared Folders" parent with account grouping in sidebar
- ID namespacing to prevent collisions across accounts (e.g., "b0:a" for shared Inbox)
- Account-aware email operations (fetch, view, mark as read)

Technical Implementation:
- Store all accounts from JMAP session (primary + shared)
- Transform mailbox IDs: primary uses original, shared use "accountId:mailboxId" format
- Store originalId for JMAP queries while using namespaced IDs in UI
- Transform email mailboxIds to match namespaced mailbox IDs for proper counter updates
- Pass accountId parameter through entire operation chain (fetch, view, mark read)

UI/UX Enhancements:
- Blue Users/User icons for shared folders in sidebar
- Hierarchical display with account grouping
- Proper unread counters that update when marking emails read
- Auto-mark emails as read when opened in shared folders

Bug Fixes:
- Fixed email content fetching showing wrong message in shared folders
- Fixed unread counters not updating for shared mailboxes
- Fixed mark-as-read not working on server for shared accounts
- Cleaned up debug logging
2025-10-02 13:57:19 +02:00
Matthieu MALVACHE f4ec48ad4a fix: Mark sent emails as seen to prevent unread indicator
When sending emails, they were not being marked with the $seen keyword,
causing them to appear as unread in the Sent folder. Added $seen: true
to keywords for both draft-based sends and new email sends.
2025-10-02 13:21:54 +02:00
Matthieu MALVACHE a453585e2d fix: Remove partId from attachment objects to comply with JMAP spec
JMAP spec states "The client may specify a partId OR a blobId, but not both."
We were incorrectly including both fields, causing "Cannot specify both partId
and blobId" errors when creating drafts with attachments.

Fixed by removing partId and size fields from attachment objects. The server
calculates size automatically from the blob content.
2025-10-02 13:11:27 +02:00
Matthieu MALVACHE 8b52fa4719 fix: Implement destroy+create pattern for draft updates with attachments
JMAP doesn't allow updating immutable properties (from, to, subject) on
existing drafts. Changed createDraft to destroy old draft and create new
one when updating, which allows attachments to be added/modified properly.

Also added comprehensive debug logging to uploadBlob and createDraft to
help troubleshoot upload issues.
2025-10-02 13:09:00 +02:00
Matthieu MALVACHEandClaude ec515c7da9 chore: Remove debug console.log statements
Cleaned up console output by removing all debug console.log statements
throughout the codebase:

- Removed 31 debug logs from lib/jmap/client.ts (connection, requests, etc.)
- Removed 4 debug logs from stores/email-store.ts (quota fetch, markAsRead)

Kept all console.error() statements for proper error handling.
The console is now clean with only relevant error messages displayed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-02 13:03:19 +02:00
Matthieu MALVACHEandClaude 258f10d4ba feat: Implement functional quick reply and fix draft auto-save integration
Email Sending:
- Fix identity lookup for email submission (now properly fetches identities from server)
- Add Sent mailbox requirement for sending emails
- Add proper error handling and validation for email submission

Quick Reply Form:
- Replace dummy input with fully functional quick reply system
- Add state management for reply text with auto-expand on focus
- Implement direct send functionality without modal
- Add loading states and error handling
- Show character counter and action buttons (Cancel, More options, Send)
- Use real user avatar and email from auth store
- Auto-refresh email list after sending

Draft Auto-Save Integration:
- Fix critical issue where auto-saved drafts were ignored when sending
- Pass draftId through entire send chain (composer → store → client)
- Wait for pending auto-save before sending to prevent race conditions
- Add draft cleanup with discard confirmation when closing composer
- Delete orphaned drafts when user confirms discard
- Add i18n support for discard confirmation (EN/FR)

Benefits:
- No more orphaned drafts in Drafts folder
- Better performance (updates existing draft vs creating new email)
- Cleaner UX with proper draft lifecycle management
- Quick replies are truly quick (no modal needed)
- All user-facing messages properly translated

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-02 13:01:14 +02:00
Matthieu MALVACHE 5f804e7703 fix: Improve invalid credentials error handling for login
- Update auth store to properly detect 'Invalid username or password' error
- Map error to existing 'invalid_credentials' translation key
- Provide better user feedback when login fails
2025-10-02 12:31:29 +02:00
Matthieu MALVACHEandClaude 7c6ace427f feat: Add server capabilities, keep-alive, draft auto-save, and attachment upload
- Added server capability detection to check supported features
- Implemented keep-alive mechanism with periodic ping (every 30s)
- Added automatic session reconnection on connection failure
- Implemented draft auto-save with 2-second debounce
- Added visual indicators for draft save status
- Implemented attachment upload support with progress indication
- Added file attachment UI in email composer
- Updated JMAP client with blob upload functionality
- Enhanced disconnect handling to properly cleanup resources
- Updated TODO.md to reflect completed tasks

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-02 12:29:53 +02:00