Migrate mobile-common features from IS_ANDROID_WEB_VIEW to IS_NATIVE_PLATFORM
so iOS gets the same behavior as Android:
- Service worker: disabled on both mobile platforms
- Reminder notifications: skip dialog on native (use Capacitor notifications)
- Share functionality: detect Capacitor Share plugin on both platforms
- File operations: use native filesystem on both platforms
- Add iOS local backup using Capacitor Filesystem (Directory.Data)
- Add iOS app lifecycle listeners (appStateChange, appUrlOpen)
- Update download utility to use IS_NATIVE_PLATFORM for iOS support
- Update file-imex component to show success snack on iOS exports
Add iOS-specific UX improvements:
Safe Areas:
- Add CSS variables for safe area insets (notch, home indicator)
- Add body classes: isNativeMobile, isIOS, isKeyboardVisible
- Apply safe area padding on native mobile platforms
Keyboard Handling:
- Install @capacitor/keyboard plugin
- Track keyboard visibility via keyboardWillShow/Hide events
- Set --keyboard-height CSS variable for layout adjustments
- Remove bottom safe area padding when keyboard is visible
Status Bar:
- Install @capacitor/status-bar plugin
- Sync status bar style with app dark/light theme
- Configure overlaysWebView: false in capacitor.config.ts
These changes ensure proper display on iPhone devices with
notch/Dynamic Island and home indicator.
- Increase task creation timeout from 10s to 15s for slow renders
- Use force:true on backdrop click to bypass overlay coverage
- Replace page.evaluate() with Playwright locators in expandSection()
- Add proper condition-based waiting for collapsible panel visibility
Replace arbitrary waitForTimeout calls with condition-based waiting:
- Use specific selector scoped to .mat-mdc-menu-content for tag button
- Wait for tag to appear on task instead of waiting for menu to close
- Keep only the justified 350ms wait for touch protection delay testing
Add unit tests for selectAllTasksWithDueDay and selectAllTasksDueToday
selectors to ensure the performance optimizations work correctly.
Tests cover:
- Filtering tasks by dueDay and dueWithTime
- Sorting by dueDay chronologically
- Handling empty state and missing entities
- Deduplication of tasks from multiple sources
- Edge cases like subtasks with dueDay
Consolidate two Object.values() calls into a single iteration over
taskState.ids. This avoids creating two intermediate arrays and
reduces the number of passes over all tasks from 2 to 1.
Iterate taskState.ids instead of using Object.values() to avoid
creating an intermediate array. Also simplified the sort comparator
using localeCompare since dueDay is in YYYY-MM-DD format which is
lexicographically sortable.
Emojis in tag icons were rendered inside <mat-icon> elements, causing
excessive whitespace. Now emojis are detected via isSingleEmoji() and
rendered in a <span> with proper emoji styling.
Fixes#5977
Archives (archiveYoung, archiveOld) were being loaded into NgRx state
at startup but their selectors were never read anywhere in the codebase.
All code that needs archives loads them directly from IndexedDB via
ArchiveDbAdapter (StateSnapshotService, TaskArchiveService, etc).
This change removes the archive store registrations from NgRx, which:
- Reduces memory usage for users with large archives
- Improves startup time (no longer dispatching large archive data to NgRx)
- Reduces GC pressure (fewer large objects in memory)
Archive functionality is unaffected - archives are still stored in
IndexedDB and loaded on-demand when needed for worklog, sync, etc.
When importing a backup while connected to SuperSync, the server was
rejecting the operation with 409 SYNC_IMPORT_EXISTS error. This happened
because backup imports were using OpType.SyncImport which maps to
reason='initial' on the server.
Fix:
- Change OpType.SyncImport → OpType.BackupImport in backup.service.ts
- OpType.BackupImport maps to reason='recovery' on the server
- Server allows reason='recovery' even when SYNC_IMPORT exists
This enables users to recover data by importing backups when connected
to SuperSync, which is essential for disaster recovery scenarios.
Add unit test verifying OpType.BackupImport is used and E2E test that
validates the full backup recovery flow works when SYNC_IMPORT exists.
SYNC_IMPORT operations and local backups were losing archived tasks because
they used sync snapshot methods that return empty DEFAULT_ARCHIVE instead of
loading real archive data from IndexedDB.
Changes:
- ServerMigrationService: use getStateSnapshotAsync() to include archives
- LocalBackupService: use getAllSyncModelDataFromStoreAsync() for backups
- ArchiveOperationHandler: add safety guard to prevent overwriting non-empty
archives with empty ones
Add unit tests for archive preservation and E2E test that triggers actual
server migration by switching sync providers.
The previous fix didn't fully work because:
1. Submenus don't trigger openMenu() in Angular Material 21
2. Element-level capturing listeners don't intercept before Angular handlers
This fix adds:
- MutationObserver to detect when any menu panel appears (including submenus)
- Document-level capturing listener that intercepts clicks on menu items
BEFORE they reach Angular's event handlers
- Shared timestamp between monkey patch and directive via exports
The document-level approach is critical because capturing at the document
runs before the event reaches the target, allowing stopImmediatePropagation()
to block Angular's (click) bindings.
Issue: #4436
The monkey patch that prevents accidental submenu selection on touch
devices was broken after the Angular Material 21 update.
Root cause: Angular Material renamed MatMenuItem._handleClick to
_checkDisabled in version 21.
Changes:
- Update patch to override _checkDisabled instead of _handleClick
- Add unit tests to detect future Angular Material API changes
- Add e2e tests for context menu submenu functionality
- Document API dependencies and change history in code comments
Fixes touch devices accidentally selecting menu items when submenus
open under the user's finger near screen edges.
Issue: #4436
Remove complex CSS positioning that caused the update icon to be
vertically misaligned. The icon now uses the button's native flexbox
centering like the other icons (chat/close) in the same position.
Replace fixed timeouts with condition-based waiting to improve test
reliability under load:
- USE_REMOTE test: wait for sync completion instead of fixed 2s delay
- Encryption test: add retry logic for error state checking
- Undo delete test: use waitForTask polling instead of fixed timeout
- Task ordering test: add UI settling time after sync operations
The startup banner was incorrectly showing any recently created note as a
"Reflection Note". Now it only shows reflections added via "Evaluate Day"
by querying MetricService instead of NoteService.
Electron validates the GUID argument even when undefined is passed,
causing "Invalid GUID format" error on Linux/macOS. Only pass the
GUID argument on Windows where it's needed.
Add potential fix for repeat tasks not appearing in Today view:
- Add event loop yield after creating repeat tasks to ensure store
processes dispatched actions before querying
- Add isPaused filter to selectors to exclude paused repeat configs
- Add unit tests for isPaused filter
- Add E2E test for regression protection
Note: We're not 100% certain the event loop yield fixes#5976, but it
follows the established pattern used elsewhere in the codebase.
- Fix break pause not stopping tracking (syncSessionPauseToTracking$ now
handles break purpose)
- Fix manual break start not resuming tracking when isPauseTrackingDuringBreak
is disabled
- Fix banner icon not updating when break is paused via tracking button
(BannerService now creates new object instead of mutating)
- Fix typo: "session" -> "sessions" in sync setting label
Add comprehensive unit tests for all fixes and E2E tests for basic
Pomodoro focus mode behavior.
Remove overflow: hidden from .g-multi-btn-wrapper which caused
Angular Material's CDK overlay to miscalculate menu position after
DOM changes from toggling project visibility.
Fixes#5955
Add a static GUID to the Electron Tray constructor on Windows to persist
tray icon position and visibility preferences across app updates. This
prevents the tray icon from being hidden in the overflow menu after
Microsoft Store updates change the executable path.
Closes#5973
Add explicit per-route rate limits to /verify-email and /magic-login
endpoints to address CodeQL security alerts. These endpoints previously
relied only on global rate limiting.
Add unit tests verifying the If-Match header is properly used when
the expectedRev is an ETag (not a valid date):
- If-Match header sent instead of If-Unmodified-Since for ETags
- ETags properly quoted per RFC 7232
- Already-quoted ETags not double-quoted
- 412 Precondition Failed handled correctly for ETag conflicts
Add E2E tests covering the scenario where two clients both migrate from
the old Super Productivity format (pre-operation-log) and then sync.
Tests include:
- Both clients migrated with different data (keep local/remote resolution)
- Both clients migrated with same entity IDs (ID collision handling)
- Archive data preservation after migration + sync (WebDAV only)
New files:
- legacy-migration-helpers.ts: Helper functions for seeding legacy DB
- 4 JSON fixtures for legacy data scenarios
- webdav-legacy-migration-sync.spec.ts: 4 WebDAV tests
- supersync-legacy-migration-sync.spec.ts: 3 SuperSync tests (1 skipped)
When WebDAV servers don't return Last-Modified headers and ETags
are used as revisions, use the If-Match header instead of skipping
conflict detection entirely. This ensures proper conflict detection
on servers that only support ETags.
Previously, non-date revisions (ETags) would cause the conditional
header to be skipped, disabling server-side conflict detection.
Now ETags are properly quoted per RFC 7232 and sent via If-Match.
Replace `as any` type assertions with proper HTMLInputElement |
HTMLTextAreaElement typing for event target access. This improves
type safety and follows CLAUDE.md guidelines to avoid any types.