The recovery page was using the old SimpleWebAuthn API format:
startRegistration(options)
Updated to use the v13 format:
startRegistration({ optionsJSON: options })
This was causing "nothing happens" when clicking Register New Passkey.
The server was updated to @simplewebauthn/server v13.2.2 but the
browser library was still at pre-v13. The v13 API expects options
wrapped as { optionsJSON: options }, causing allowCredentials to be
lost during login with the old library.
This caused "no passkeys found" errors when users tried to login
after registering.
Change residentKey from 'preferred' to 'required' for passkey registration.
Discoverable credentials (resident keys) are required for synced passkeys
like Google Password Manager to properly store and retrieve the passkey.
Also add detailed logging of registration options for debugging.
- Update @simplewebauthn/server from v11 to v13.2.2 for compatibility
- Add debug logging for WebAuthn config and authentication options
- Browser library also needs to be updated to v13.2.2 (manual step)
The version mismatch between server and browser libraries may have been
causing passkey registration/login issues.
The @simplewebauthn/server library defaults to requireUserVerification: true,
but our registration options set userVerification: 'preferred'. This mismatch
caused passkey registration and login to fail with:
"User verification was required, but user could not be verified"
Add requireUserVerification: false to all verification calls to match
the 'preferred' setting in registration/authentication options.
The deleteAccount fetch request sent Content-Type: application/json
but no body, causing Fastify's JSON parser to fail with 400 Bad Request.
Add empty JSON body to match the pattern used by refreshToken.
This file was incorrectly kept during merge resolution. It depends on
files that no longer exist (indexed-db-adapter.service, db-adapter.model,
android-db-adapter.service) which were removed as part of the operation
logs refactor.
- Add waitForAppReady and tour dismissal after page reload in
webdav-sync-tags test to prevent task-list timeout
- Use existing providerSelect locator instead of role-based combobox
selector in sync.page.ts for more reliable dropdown interaction
- Add graceful error handling for scrollIntoViewIfNeeded with fallback
- fix(focus-mode): prevent double startFocusSession dispatch in Pomodoro
when clicking Start after break time is up (check shouldAutoStartNextSession)
- fix(task-repeat): gracefully fall back to CUSTOM quickSetting when
startDate is missing instead of throwing error
- fix(work-context): also validate TAG context after sync, not just PROJECT
- fix(android): add error handling for cancelNativeReminder calls
- fix(android): add error handling for immediate save operations
- fix(reminder): use type guard in Promise.all filter for better type safety
- docs(audio): add comment explaining fire-and-forget pattern for resume()
* origin/master:
use Log.warn instead of console.warn added more warning output if board reference is not found
adjusted css so that right click on the whole tab label works to open the context menu removed import of no longer required ContextMenuComponent
chore(deps): bump actions/upload-artifact from 5 to 6
chore(deps): bump actions/cache from 4 to 5
chore(deps-dev): bump @typescript-eslint/utils from 8.41.0 to 8.51.0
Disable play button and show tooltip when no tasks available
use translations for board actions
updated boards context menu to include delete and edit actions
- fix(android): await async _syncElapsedTimeForTask in notification handlers
to ensure time is synced before saving/pausing
- fix(notes): prevent duplicate emit in fullscreen markdown editor by
tracking last emitted content
Repeat configs with date-dependent quickSettings (WEEKLY_CURRENT_WEEKDAY,
YEARLY_CURRENT_DATE, MONTHLY_CURRENT_DATE) require a startDate to derive
the correct weekday/date. This repair changes invalid configs to CUSTOM
to prevent crashes.
Fixes#5802
- Add startup warning in passkey.ts when running with in-memory
challenge storage in production (multi-instance deployments)
- Use LOCK_NAMES.OPERATION_LOG constant in stale-operation-resolver
instead of hardcoded 'sp_op_log' string
- Add client-side validation for empty/whitespace entityId to match
server-side validation and provide earlier feedback
The computeOrderedTaskIdsForToday() selector only checked task.dueDay
for TODAY membership, ignoring tasks with dueWithTime set to today.
This caused daily repeating tasks with overdue scheduled times to not
appear in the Today view after SuperSync import.
Add dueWithTime fallback to computeOrderedTaskIdsForToday():
- Check dueDay first (primary source of truth)
- If dueDay doesn't match today, check if dueWithTime is for today
- This matches the pattern already used in selectLaterTodayTasksWithSubTasks
Add 6 unit tests covering the new fallback behavior:
- Task with dueWithTime for today but no dueDay
- Task with dueWithTime for today but stale dueDay
- Task with dueWithTime for tomorrow (should NOT be included)
- Task with both dueDay and dueWithTime for today (no duplicates)
- Mix of dueDay and dueWithTime fallback tasks
Fixes#5841
When returning to the app after tracking time in the background, the app
would show less time than the notification because the sync from the
native Android foreground service was unreliable.
Root causes:
1. The _syncElapsedTimeForTask method used .subscribe().unsubscribe()
anti-pattern which could fail to execute the callback
2. No protection against double-counting when both native sync and
the app's tick$ added time for the same period
This fix:
- Refactors _syncElapsedTimeForTask to use async/await with firstValueFrom()
- Adds resetTrackingStart() to prevent double-counting after native sync
- Adds immediate save to IndexedDB when notification buttons are clicked
- Adds comprehensive unit tests for sync logic (10 new tests)
Fixes#5840
Related to #5842
Fixes critical bug where multiple clients with existing data could each
create a SYNC_IMPORT when enabling SuperSync, causing data loss.
Root cause: When clients had local data (from legacy migration), each
would detect "server needs migration" and create competing SYNC_IMPORTs.
The last one uploaded would invalidate all prior data.
Fixes:
- Server rejects duplicate SYNC_IMPORT with SYNC_IMPORT_EXISTS (409)
- Client double-checks server is empty before creating SYNC_IMPORT
- Client merges all local op clocks into SYNC_IMPORT's vector clock
- hasSyncedOps() excludes MIGRATION/RECOVERY ops from check
- Upload service gracefully handles SYNC_IMPORT_EXISTS rejection
Tests added:
- ServerMigrationService: double-check and clock merging (5 tests)
- OperationLogStoreService: hasSyncedOps() MIGRATION exclusion (7 tests)
- OperationLogUploadService: SYNC_IMPORT_EXISTS handling (5 tests)
- E2E: Multiple clients with existing data merge correctly
Show only one error alert when IndexedDB fails (e.g., disk full) instead
of spamming the user with dozens of confirm dialogs. The app now blocks
completely and restarts after the user dismisses the alert, preventing
potential data loss from retry attempts.
Fixes#5845
- Show "Unable to set sync provider" alert only in development mode,
log warning in production (pfapi.service.ts)
- Replace "Remote model version newer" alert with console warning
(sync-wrapper.service.ts)
Fixes#5839
Fix race condition where dismissed reminders could reappear every few
seconds. The issue occurred because the worker's 10-second interval
could fire before the async DB save completed, sending stale reminder
data back to the dialog.
Two-part fix:
1. Update worker immediately when reminders change (before DB save)
2. Track dismissed reminder IDs in dialog and filter them from incoming
worker data
Fixes#5826
Add isLoading signal to disable Save button while async data loads.
This prevents the "Initial task repeat cfg missing" error when users
click Save before the repeat config finishes loading.
Fixes#5828
Wrap all androidInterface native method calls in try-catch blocks to
prevent app crashes when Java exceptions are raised during method
invocation. Users are now notified via snackbar when service start
fails, while update/stop failures are logged silently.
Changes:
- Add _safeNativeCall helper to TypeScript effects for DRY error handling
- Add safeCall inline helper to Kotlin JavaScriptInterface
- Wrap focus mode and tracking service calls with error handling
- Show user-friendly error notification on service start failures
- Add unit tests for error handling logic
Fixes#5819
On Android, native reminders scheduled via AlarmManager were not being
canceled reliably when tasks were deleted. This happened because the
reactive cancellation via reminders$ observable could fail when:
- App was backgrounded (WebView suspended)
- 5-second startup delay in Android effects
- Race conditions with async callbacks
Now explicitly cancel native reminders in the task deletion effects
(clearRemindersOnDelete$, clearMultipleReminders, clearRemindersForArchivedTasks$)
immediately when tasks are deleted, before the web-side cleanup.
Fixes#5831
Replace unsafe string cast `origErr as string` with proper error text
extraction using getErrorTxt utility. This ensures GitHub issue titles
generated from crashes show meaningful error messages instead of
"[object Object]".
Fixes#5822
Authentication changes:
- Add passkey (WebAuthn) as primary login method
- Add email magic link as fallback for devices without passkey support
- Remove password-based authentication entirely
New features:
- Passkey registration and login via @simplewebauthn/server
- Magic link login with 15-minute expiry tokens
- Passkey recovery via email link
- Self-hosted simplewebauthn-browser.min.js for reliability
Database changes:
- Add Passkey model for WebAuthn credentials
- Add PasskeyChallenge model for registration/auth challenges
- Add loginToken and loginTokenExpiresAt fields for magic links
- Add passkeyRecoveryToken fields for passkey recovery
UI changes:
- Login form: email + "Login with Passkey" + "Send Login Link"
- Register form: email + terms checkbox + "Register with Passkey"
- Consistent token display UI for both passkey and magic link login
- Remove password fields and forgot password flow
Security:
- CSP-compliant magic link redirect using external script
- Rate limiting on all auth endpoints
- Single-use magic link tokens
When creating a weekly repeating task with WEEKLY_CURRENT_WEEKDAY quick
setting, the weekday was incorrectly set based on today's date instead
of the task's scheduled date (startDate). This caused tasks scheduled
for future days to not appear in the planner.
The fix passes the startDate to getQuickSettingUpdates() so that the
correct weekday is set based on when the task is scheduled, not when
it was created.
Fixes#5806
The Focus Mode timer was using GlobalTrackingIntervalService.tick$
which only emits at the configured trackingInterval (up to 100s).
When users set a high tracking interval to reduce disk writes,
the Pomodoro timer would only update every N seconds.
Now uses its own interval(1000) for smooth 1-second updates,
independent of the global tracking interval setting.
Fixes#5813
When using focus mode with the overlay hidden (banner-only mode), users
can now skip an active break directly from the banner. Previously, the
Skip Break button was only available in the full-screen overlay.
Fixes#5818
Add debounced auto-save (500ms) to the fullscreen task note editor
to prevent data loss. Changes are now saved automatically while typing
and when pressing Escape. Remove the Cancel button since all changes
are preserved.
Fixes#5804
Decoupled textarea input state from persisted store state. The component
now uses a local inputText signal for the textarea binding instead of
directly binding to the store value. This prevents the trimmed store
value from overwriting user input after the 500ms debounce.
Fixes#5800
When a SuperSync account is deleted, clients now properly detect
authentication failures and prompt for reconfiguration:
- Add _checkHttpStatus() helper to SuperSyncProvider that throws
AuthFailSPError on 401/403 responses
- Clear cached credentials on auth failure to allow reconfiguration
- Add DELETE /api/test/user/:userId endpoint for E2E testing
- Add deleteTestUser() helper in supersync-helpers.ts
- Add E2E tests for account deletion and reconfiguration scenarios
The existing SyncWrapperService already handles AuthFailSPError by
showing a snackbar with "Configure" action, so no UI changes needed.
Add JsonParseError class that provides human-readable error messages
when sync data is corrupted or incomplete. This prevents the cryptic
"[object Object] SyntaxError" from appearing in error dialogs.
- Add JsonParseError class with position extraction and data sampling
- Wrap JSON.parse in decompressAndDecrypt with proper error handling
- Add comprehensive unit tests (16 tests)
Fixes#5771
Improve error text extraction utilities to never return "[object Object]"
when displaying error messages to users. The fix adds more robust fallback
mechanisms including:
- Check for message, name, statusText properties before calling toString()
- Detect "[object Object]" result and fallback to JSON.stringify()
- Provide meaningful fallback messages when all extraction methods fail
Fixes#5790
Use singleton AudioContext and cache audio buffers to prevent memory
leak and resource exhaustion. Previously, a new AudioContext was
created for every sound play (once per second with ticking enabled),
causing ~480+ instances after 8 minutes and browser GC pauses.
Also fixes race condition where source.start() was called before
audio data was loaded.
Fixes#5798
- e2e/utils/waits.ts: Add proper retry logic and error handling when
waiting for magic-side-nav to appear. Now throws a clear error if
app doesn't load within 30s instead of silently continuing.
- packages/super-sync-server/src/auth.ts: Use nullish coalescing for
passwordHash to handle passkey-only users (who have no password).
- packages/super-sync-server/src/passkey.ts: Add WebAuthn passkey
authentication support. Fix TypeScript errors by importing types
from @simplewebauthn/types and converting credential IDs to
base64url strings.
Fixes flaky "Multiple fresh clients join and sync correctly after
snapshot" test that was failing when app didn't fully load.
Add debounceTime, distinctUntilChanged, and shareReplay to isOnline$
observable to prevent rapid banner flickering caused by unreliable
navigator.onLine behavior in Electron on Linux.
Fixes#5738
The condition used || instead of && causing askForFileStoreBackupIfAvailable()
to return early on both Electron and Android platforms, preventing backup
restoration prompt from appearing on first launch.
Fixes#5796
When LWW conflict resolution updates a task's parentId (making it a subtask
or moving it to a different parent), we must also update the corresponding
parent task's subTaskIds arrays to maintain bidirectional consistency:
- Remove task from old parent's subTaskIds (if it was a subtask)
- Add task to new parent's subTaskIds (if it becomes a subtask)
This is analogous to the existing syncProjectTaskIds, syncTagTaskIds, and
syncTodayTagTaskIds functions that handle other cross-entity relationships.
Adds 10 unit tests covering:
- Moving subtask between parents
- Task becoming a subtask
- Subtask becoming top-level
- Edge cases (deleted parent, non-existent parent, order preservation)
The server was accepting empty string entityId values because the validation
only checked type and length, not content. This could allow corrupt operations
to be synced to other clients.
Changes:
- Add check for empty/whitespace-only entityId (returns INVALID_ENTITY_ID)
- Add 3 unit tests for empty, whitespace-only, and tab/newline entityId
- Update existing empty string test to expect INVALID_ENTITY_ID (format error)
instead of MISSING_ENTITY_ID (null/undefined check)