Commit graph

12573 commits

Author SHA1 Message Date
Johannes Millan
ab6df00a72 fix(sync): handle duplicate operation IDs gracefully in appendBatch
When concurrent sync triggers occur (especially on Android), a race condition
between filterNewOps() and appendBatch() can cause ConstraintError due to
duplicate operation IDs in the byId unique index.

Instead of failing sync, catch ConstraintError and return empty array - the
ops were already inserted by a concurrent sync.

Fixes #6213
2026-01-27 14:49:00 +01:00
Johannes Millan
ebe72c14df fix(touch): limit click blocking to submenu triggers only
Regular menu items (like tag toggle buttons) were having their clicks
blocked by the touch fix directive. This caused tag assignment from
context menu to not work on mobile devices.

The click blocking is now limited to elements with matMenuTriggerFor
attribute, which are the actual submenu triggers that need protection
against accidental selection when submenus open under the finger.
2026-01-27 14:49:00 +01:00
Johannes Millan
6f79209466 fix(op-log): remove noisy warning for empty entityChanges
Empty entityChanges is intentional for most actions since the
actionPayload is sufficient for replay and sync. The warning
was triggering constantly for normal operations like updateTask.
2026-01-27 14:49:00 +01:00
Johannes Millan
e2c585ee45 fix(dropbox): use hash-wasm fallback for PKCE on Android
On Android, Capacitor serves the app from http://localhost which is not
a secure context. WebCrypto's crypto.subtle API is unavailable in
insecure contexts, causing Dropbox OAuth PKCE code generation to fail.

Use hash-wasm (already a dependency for Argon2id) as fallback when
crypto.subtle is unavailable.
2026-01-27 14:49:00 +01:00
Alberto Avon
3fc0be6924
feat(boards): move tasks to and from backlog when dragging to a panel with backlogState set (#6209)
Co-authored-by: Alberto Avon <a.avon@nehos.it>
2026-01-27 14:47:30 +01:00
Benjamin Hooper
2e20746fb8
feat: Copy Version Number #5108 (#6184)
* feat: Copy Version Number #5108

* Use Claude Code Review Suggestion

* Added Translation

* Refactor to use shared copyToClipboard function
2026-01-27 14:41:35 +01:00
Johannes Millan
ac700b6eed fix(sync): prevent race condition in encryption password change
Move unsynced operations check inside runWithSyncBlocked() to prevent
race condition where background operations could add unsynced ops
between the check and acquiring the lock.

Also add verification step after createCleanSlate() to ensure
SYNC_IMPORT was stored before proceeding, and keep new password
config on upload failure (reverting would encrypt with wrong password).

Fixes flaky E2E test in CI.
2026-01-26 21:20:50 +01:00
Johannes Millan
6ca7223dc6 refactor: add markdown renderer tests and remove dead IPC code
- Add 18 unit tests for marked-options-factory covering:
  - Image renderer (sizing syntax, lazy loading, dimensions)
  - Paragraph renderer (headings h1./h2. syntax)
  - parseImageDimensionsFromTitle helper
  - preprocessMarkdown hook
- Export parseImageDimensionsFromTitle and preprocessMarkdown for testability
- Remove unused IPC.TOGGLE_FULLSCREEN event and handler
  (F11 fullscreen is handled directly in main-window.ts)
2026-01-26 20:55:29 +01:00
Johannes Millan
a1af9d5d59 17.0.5 2026-01-26 19:48:56 +01:00
Johannes Millan
8f935294a5 fix(schedule): apply dialog max-height to schedule task dialog only
Move the max-height constraint from global mat-dialog-content to the
dialog-schedule-task component specifically, since it has no header
and needs different sizing.
2026-01-26 19:37:50 +01:00
Johannes Millan
3534706f16 fix(open-project): make legacy config fields optional for backward compatibility
Fields progressOnDone, transitionConfig, and availableTransitions were added
after some users already had OpenProject configs, causing Typia validation
errors. Making them optional allows legacy data to load without errors.
2026-01-26 19:35:15 +01:00
Johannes Millan
7c49cc2e6b fix(android): flush time tracking data when app goes to background #6207
When the app goes to background on Android, accumulated time tracking
data was not being flushed to IndexedDB. If Android terminated the app
while backgrounded, tracked time would be lost.

This fix adds a flushOnPause$ effect that:
- Syncs elapsed time from the native foreground service
- Flushes accumulated time from the BatchedTimeSyncAccumulator
- Writes pending operations to IndexedDB

This follows the same pattern already used for notification button
clicks (handlePauseAction$, handleDoneAction$).
2026-01-26 18:28:06 +01:00
Johannes Millan
0f68c35932 17.0.4 2026-01-26 18:14:43 +01:00
Johannes Millan
84776535f4 test: fix flaky validate-state.service tests
Mock validateAndRepair directly instead of relying on global state
(environment.production, window.confirm spy) that could be affected
by test pollution when running the full test suite.
2026-01-26 18:12:44 +01:00
Johannes Millan
070469f239 fix(focus-mode): dispatch completeFocusSession when isManualBreakStart=true #6206
When isManualBreakStart was enabled, the detectSessionCompletion$ effect
was blocking the completeFocusSession action entirely. This prevented:
- Screen transition from Main to SessionDone
- Completion sound from playing
- Window focus on Electron
- "Start break" button from appearing

The isManualBreakStart check was incorrectly placed in detectSessionCompletion$.
It belongs only in autoStartBreakOnSessionComplete$ (which already has it)
to prevent breaks from auto-starting while still allowing session completion.
2026-01-26 17:53:02 +01:00
Johannes Millan
550eedefa8 fix: schedule dialog height #6187 2026-01-26 17:34:00 +01:00
Johannes Millan
73b9bb580f 17.0.3 2026-01-26 16:50:58 +01:00
Johannes Millan
2108e69e0b feat(sync): re-enable repair system with user confirmation
Re-enable the data repair system with user confirmation before execution.
When invalid data is detected, show a confirmation dialog asking if the
user wants to repair, giving users control and visibility.

Changes:
- Add translated confirmation dialog before repair executes
- Add isRemote flag to loadAllData dispatch in sync contexts to prevent
  LOCAL_ACTIONS effects from firing
- Add user notification when repair fails after confirmation
- Add comprehensive tests for validateAndRepairCurrentState()
- Add documentation for implicit contracts and limitations
- Improve type safety (replace `as any` with proper casts)
2026-01-26 16:29:21 +01:00
Johannes Millan
df3f68c3a9 fix(tasks): handle undefined config in createNewTaskWithDefaults
Add optional chaining for cfg()?.tasks to prevent "Cannot read
properties of undefined (reading 'defaultProjectId')" error during
Dropbox sync when config is not yet loaded.

Fixes #6199
2026-01-26 15:04:51 +01:00
Johannes Millan
afa8b7da4d test(ui): fix marked-options-factory tests for marked v17 API
Update link renderer tests to provide tokens array and mock parser
context as required by marked v17. Remove URL auto-linking tests
since gfm:true handles this automatically in the lexer now.
2026-01-26 13:48:11 +01:00
Johannes Millan
e0e03446b4 feat(dropbox): add Capacitor HTTP support for native platforms
Use CapacitorHttp instead of fetch() on iOS/Android to avoid
issues with WebKit and WebView handling of HTTP requests. Native
platform tests are skipped as CapacitorHttp cannot be properly
mocked in Jasmine.
2026-01-26 13:48:11 +01:00
Johannes Millan
6ffc414693 fix(tasks): allow subtask drag-drop reordering in Today/Tag views
Subtasks appearing as top-level items in Today/Tag views (when their
parent is not in the view) were snapping back to original position
when reordered via drag-drop.

Root cause: selectTodayTagRepair selector was excluding ALL subtasks
from valid taskIds, even those that should appear as top-level items.
After reordering, the repair effect immediately "fixed" the order by
removing the subtask.

Changes:
- Update selectTodayTagRepair to include subtasks whose parent is NOT
  in the Today list (these appear as top-level items)
- Update enterPredicate to check if subtask's parent is in target
  list's tasks for more reliable detection
- Add guard for OVERDUE to BACKLOG edge case in _move method
- Add comprehensive tests for enterPredicate (26 tests)
- Add edge case tests for selectTodayTagRepair (38 tests)
2026-01-26 13:48:11 +01:00
Johannes Millan
3c6d0369c6 fix(gitlab): use null as default for project field
Change DEFAULT_GITLAB_CFG.project from '' to null for semantic
correctness. The type GitlabCfg.project is string | null, so null
properly represents "not configured" rather than an empty string.

Fixes #6138
2026-01-26 13:48:11 +01:00
Johannes Millan
e8e4ed6913 fix(config): prevent crash when appFeatures is undefined
Add safe appFeatures selector and signal to prevent crash when
config.appFeatures is missing during data migration or sync.

The crash "Cannot read properties of undefined (reading
'isPlannerEnabled')" occurred when cfg() returned a partial config
object without the appFeatures property.

Changes:
- Add selectAppFeaturesConfig selector with fallback to defaults
- Add appFeatures signal to GlobalConfigService with safe initial value
- Fix selectIsFocusModeEnabled to use optional chaining on appFeatures
- Update all usages to use appFeatures() instead of cfg()?.appFeatures

Fixes #6182
2026-01-26 13:48:11 +01:00
Johannes Millan
02959b56ca fix(markdown): restore inline formatting and links for marked v17
The upgrade to marked v17 (commit 4bdd94cbf) broke several markdown
features:
- Bold/italic text not rendering
- Link syntax [text](url) not working
- Plain URLs not becoming clickable

Root causes:
1. In marked v17, renderer methods receive token objects with a
   `tokens` array. To get rendered HTML with inline formatting,
   you must call `this.parser.parseInline(tokens)` instead of
   using the raw `text` property.

2. The redundant `provideMarkdown()` call in main.ts was overriding
   the `MarkdownModule.forRoot()` configuration that included the
   custom markedOptionsFactory.

3. The custom renderer.text URL linkifier was causing double-processing
   since marked v17 with gfm:true already auto-links URLs at the
   lexer level.

Changes:
- Convert listitem, link, paragraph renderers to regular functions
  to access `this.parser.parseInline(tokens)` for inline rendering
- Restore image renderer with sizing syntax support
- Restore preprocessing hooks for image sizing (=WIDTHxHEIGHT)
- Remove custom URL linkifier (GFM handles it natively)
- Remove redundant provideMarkdown() from main.ts

Fixes #6181
2026-01-26 13:48:10 +01:00
Johannes Millan
f94818641c fix(config): merge defaults for tasks config on data load
Fill missing tasks config fields with defaults when loading data from
older app versions or synced snapshots. This prevents state validation
errors for recently added fields like isAutoMarkParentAsDone,
isAutoAddWorkedOnToToday, isTrayShowCurrent,
isMarkdownFormattingInNotesEnabled, and notesTemplate.
2026-01-26 13:48:10 +01:00
Johannes Millan
61590fdc1b fix(mobile-nav): fix hamburger menu not working with background image
The mobile navigation drawer was opening from the wrong side (left instead
of right) and immediately closing when a background image was set via Unsplash.

Root cause: backdrop-filter was incorrectly applied to the HOST element
(<magic-side-nav>) which has width: 0 on mobile. Per CSS spec, backdrop-filter
creates a new containing block for position: fixed descendants, causing
.nav-sidenav with right: 0 to be positioned relative to the zero-width host
instead of the viewport.

Fix: Move backdrop-filter styles from HOST to .nav-sidenav using correct
:host-context() & syntax, ensuring the milkglass effect is applied to the
fixed-position sidebar that has its own containing block (the viewport).

Fixes #6177
Fixes #6133
2026-01-26 13:48:10 +01:00
Johannes Millan
e4d2a3ac45 fix(sync): distinguish permanent rejections from resolved conflicts
Sync was incorrectly reporting ERROR status for CONFLICT_CONCURRENT
rejections even when conflicts were successfully resolved via LWW merge.

Changes:
- Add RejectionHandlingResult interface with mergedOpsCreated and
  permanentRejectionCount fields
- Update handleRejectedOps to return both counts
- Check permanentRejectionCount instead of rejectedCount in sync-wrapper
- Add fallback to rejectedCount for backward compatibility
- Suppress MatFormField onContainerClick timing errors during sync
2026-01-26 13:48:10 +01:00
Gitoffthelawn
bf0b318571
Further improvements to English i18n (#6186) 2026-01-26 13:06:11 +01:00
Gitoffthelawn
74530302b3
Resolves #6195: Tag vertical clipping (#6196)
Resolves #6195: Tag vertical clipping
2026-01-26 12:39:49 +01:00
Gitoffthelawn
27d67d0291
Changed 'Due' to 'Schedule' in 'Add task' UI (#6194)
This change makes the function of the button accurate.  
But because it's a change people will certainly notice, I made it a separate PR from other changes (if we need to discuss).
2026-01-26 11:59:49 +01:00
Johannes Millan
0781d44edb test(sync): add unit tests for setProtectedClientIds and improve E2E reliability
- Add 3 unit tests verifying setProtectedClientIds is called for
  SYNC_IMPORT and BACKUP_IMPORT, and not called for regular ops
- Fix inconsistent hideExpression patterns in sync-form.const.ts
  using safe field?.model access pattern
- Improve E2E test uniqueness with testRunId + timestamp + random suffix
- Increase error snackbar detection timeout from 2000ms to 5000ms
2026-01-25 20:18:31 +01:00
Johannes Millan
95b969d1b3 fix(ui): restore filled icons for impact stars and play button
Override font-variation-settings to use 'FILL' 1 for icons that should
appear filled, after global Material Symbols changed to outlined default.
2026-01-25 20:12:32 +01:00
Johannes Millan
1138c2c1c0 feat(sync): enhance vector clock management and improve error handling in sync process 2026-01-25 20:09:19 +01:00
Johannes Millan
8963b525a0 fix(sync): improve hideExpression null safety and E2E test reliability
- Fix hideExpression patterns in sync-form.const.ts that could show
  buttons when field is undefined by adding ?? false fallback
- Replace fixed waitForTimeout in supersync.page.ts with proper
  Promise.race that waits for sync signals (spinner, error, dialog)
- Remove redundant timeouts after checkbox click and password fill,
  use proper toBeEnabled assertion instead
2026-01-25 20:08:08 +01:00
Johannes Millan
6ece08c51b fix(sync): handle DELETE operations in StaleOperationResolverService
Previously, when a DELETE operation was rejected due to concurrent
modification, the service tried to create a replacement UPDATE op by
fetching current entity state. Since the entity was deleted locally,
getCurrentEntityState() returned undefined, causing the DELETE to be
discarded and sync to never complete.

Now, DELETE operations are detected and handled separately by creating
a new DELETE operation with the merged vector clock, preserving the
original actionType and payload from the stale op.
2026-01-25 20:04:49 +01:00
Johannes Millan
bb7a3ec2b1 fix(sync): remove state mutation from cross-model validation
Validation was trying to self-heal TODAY_TAG orphaned IDs by mutating
the frozen NgRx state, causing "Cannot assign to read only property"
errors after SYNC_IMPORT on fresh clients. Validation should be
read-only - it now logs a warning without mutating. Orphaned IDs in
TODAY_TAG are harmless since it's a virtual tag (membership is via
task.dueDay, taskIds only stores ordering).
2026-01-25 19:53:27 +01:00
Johannes Millan
e6022cec77 test: add comprehensive tests for vector clock pruning fix
- Add E2E tests for SuperSync vector clock pruning scenarios:
  - Tasks created after receiving SYNC_IMPORT sync correctly
  - Multiple sync cycles after SYNC_IMPORT maintain consistency
- These tests verify the end-to-end flow that would fail without the fix

The E2E tests use the actual SuperSync server and verify that:
1. Client A imports backup (creates SYNC_IMPORT)
2. Client B receives import and creates new tasks
3. Client B's tasks sync back to Client A correctly
4. No sync errors occur (tasks aren't filtered incorrectly)
2026-01-25 19:41:35 +01:00
Johannes Millan
91cab8e974 fix(sync): preserve SYNC_IMPORT client ID through vector clock pruning
Vector clock pruning was removing the SYNC_IMPORT client ID during hydration,
causing new operations to appear CONCURRENT with the import instead of
GREATER_THAN. This led to SyncImportFilterService incorrectly discarding
valid ops as "invalidated by SYNC_IMPORT".

Root cause: setProtectedClientIds() was only called in RemoteOpsProcessingService
(for live sync) but NOT during app startup hydration when loading from snapshot.

Changes:
- Add migration in OperationLogHydratorService to restore protectedClientIds
  from existing SYNC_IMPORT ops in the ops log (handles old data)
- Fix setVectorClock() to preserve existing protectedClientIds
- Fix appendWithVectorClockUpdate() to preserve protectedClientIds
- Add setProtectedClientIds calls in all hydration code paths
- Add comprehensive unit tests for all scenarios

The migration runs early during hydration and scans the ops log for existing
full-state ops (SYNC_IMPORT/BACKUP_IMPORT/REPAIR), setting their client ID
as protected if protectedClientIds was never set (old data from before fix).
2026-01-25 19:37:35 +01:00
Johannes Millan
82efd05add test: fix flaky time-only schedule syntax test
Use fixed date (Jan 15, 2024 at 10am) instead of new Date() to
eliminate race condition with system clock near day boundaries.
2026-01-25 18:16:50 +01:00
Johannes Millan
100b8af5f2 fix(sync): return existingClock in conflict rejections to fix infinite loop
When encryption is enabled, clients could get stuck in infinite conflict
resolution loops because they couldn't create vector clocks that dominate
the server's state. The server already computed the existingClock during
conflict detection but didn't return it to the client.

This fix:
- Adds existingClock to UploadResult type (server)
- Returns conflict.existingClock in CONFLICT_CONCURRENT/CONFLICT_STALE rejections
- Passes existingClock through client upload service to rejected ops handler
- Merges entity clocks into extraClocks for stale operation resolution

Includes E2E tests for encryption + conflict resolution scenarios.
2026-01-25 18:10:08 +01:00
Johannes Millan
ed8b9a5f88 fix(mobile): prevent menu flash on Android by disabling swipe during animation
On Android, tapping the hamburger menu caused the menu to flash and
immediately close. This happened because the touch event from the
button was captured by the drawer's swipe handler during its enter
animation. Fixed by temporarily disabling swipe detection for 300ms
when opening the menu.
2026-01-25 18:10:08 +01:00
Serif
b05887593e
feat(i18n): update turkish (#6170) 2026-01-25 18:09:58 +01:00
Johannes Millan
298c956518 17.0.2 2026-01-25 13:50:41 +01:00
Johannes Millan
b82253d0b6 test(sync): add WrappedProviderService mock to encryption password change tests
Update test mocks to match the service's new dependency on
WrappedProviderService for cache invalidation on encryption changes.
2026-01-25 13:45:26 +01:00
Johannes Millan
fe9db14d7a fix(sync): clear adapter cache on encryption changes and add safety limits
- Clear wrapped provider cache when encryption is enabled/disabled/changed
  to ensure new encryption settings take effect immediately
- Add hard limit (100) for deferred actions buffer to prevent unbounded
  memory growth if sync gets stuck
- Limit vector clock size to prevent unbounded growth with many clients
- Prevent circular references when moving subtasks to avoid infinite loops
2026-01-25 13:34:16 +01:00
Johannes Millan
2f25daf507 refactor(sync): improve op-log error handling and code quality
- Sanitize sensitive data (tokens, passwords) in error logs
- Add StorageQuotaExceededError for IndexedDB quota handling
- Remove duplicated FULL_STATE_OP_TYPES, import from operation.types
- Add deprecation warning for legacy PBKDF2 encryption
- Document why `any` is intentional in PersistentAction interface
2026-01-25 13:01:17 +01:00
Johannes Millan
955709d789 refactor(sync): extract duplicated native error handling into helper method
DRY up identical CapacitorHttp error handling code in _fetchApiNative()
and _fetchApiCompressedNative() by extracting to _handleNativeRequestError().
2026-01-25 13:00:31 +01:00
Johannes Millan
661e72691a test(e2e): fix failing supersync e2e tests
- Fix import-sync merge test: use goto instead of reload for reliability
  with service workers
- Fix tag management test: add proper menu close handling with multiple
  Escape presses and fallback click to dismiss overlays
- Fix encryption enable/disable tests: use fresh client contexts instead
  of reconfiguring existing clients to avoid clean slate conflicts
- Fix encryption password change tests: correct wait states and method
  names
- Add encryption password field to SuperSync form with e2e class selector
2026-01-25 12:49:21 +01:00
Johannes Millan
e48749d0f7 fix(sync): address code review findings and add encryption tests
- Remove sensitive debug logging from sync-config.service.ts
- Fix dialog handler memory leak in E2E tests (page.on -> page.once)
- Remove duplicate code block in supersync.page.ts
- Fix silent error in rejected-ops-handler.service.ts (now re-throws)
- Improve CapacitorHttp error classification in super-sync.ts
- Add isEntityState type guard in operation-log-sync.service.ts
- Import DOWNLOAD_PAGE_SIZE constant and fix timeout leak in download service
- Fix waitForTask to only catch timeout errors in supersync-helpers.ts
- Add unit tests for EncryptionEnable/Disable/ImportHandler services
2026-01-25 12:41:03 +01:00