* feat: Extended ukrainian translation
Added the properties that do exist in en.json, but didn't exist in uk.json.
BUT, PLEASE PAY ATTENTION, Most of that new properties that I added, were translated using LLM, I didn't review each of them, so they still need human review
* feat: Extended czech translation
Added the properties that do exist in en.json, but didn't exist in cz.json.
BUT, PLEASE PAY ATTENTION, Most of that new properties that I added, were translated using LLM, I didn't review each of them, so they still need human review
* fix: Fixes after claude PR review
https://github.com/super-productivity/super-productivity/pull/6292#discussion_r2749397256
* feat: introduce habit tracking page and enhance simple counters with streak tracking and comprehensive configuration options.
* feat: add habit tracker for simple counters with navigation and internationalization.
* feat: Introduce habit tracker component and tests for simple counter streak duration calculation.
* Fix the dialog style that appears in the Habit interface
* bug fix
* Fixed translation and time zone issues
* habit icon
* app features
* Add a Settings button to the Habits page; clicking it opens the Settings interface and automatically expands the corresponding configuration section.
Clicking a habit icon allows you to configure that habit. To add a new habit, click the “Add” button.
* If a habit does not have a custom icon, its first character can be displayed inside a circular placeholder instead.
* The dialog box appears only after you release the button following a long press.
* Add the functionality to display names; clicking the button shows the habitual name. This can be persisted
* bug修复:Date adjustment (left/right) in shrink mode is not functioning as expected.
* Set Text Icon
* Fix abnormal icon size
* These development artifacts should not be committed to the repository.
* Lost Chinese translations
* Flaky Date-Dependent Tests
* Logic error in streak calculation
* Fix the icon issue
* Translation file violation
Use of any type
---------
Co-authored-by: Xinjie <xinjie_zhou@163.com>
* fix(backup): support importing legacy v10-v13 backups into v17
When v17 rewrote the sync architecture (PFAPI → operation-log), all
cross-model migrations (2 through 4.5) were dropped. This means backups
created by v10-v13 cannot be imported — they fail Typia validation
because the data shape is incompatible and archived tasks are silently
lost.
Add `migrateLegacyBackup()` which consolidates the essential v16
cross-model migrations into a single pass that runs before validation:
- Migration 2: Split flat `taskArchive` → `archiveYoung`/`archiveOld`,
extract `timeTracking` from project/tag properties
- Migration 3: Planner→dueDay, INBOX project, legacy tag cleanup
- Migration 4: `plannedAt`→`dueWithTime`, strip TODAY_TAG from tagIds
- Migration 4.1-4.2: Task repeat config cleanup
- Migration 4.3-4.5: menuTree, localization, appFeatures, lowercase
The detection (`isLegacyBackupData`) only triggers on definitive legacy
markers (`taskArchive`, `improvement`, `obstruction` keys) so modern
backups are unaffected.
Tested against a real v10-era backup (253 tasks, 155 archived, 19
projects) — all data preserved after migration.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test(backup): replace large fixture with minimal v10 scaffold
Replace the 17k-line migrated backup dump with a small hand-crafted
fixture (~160 lines) that exercises every legacy edge case: flat
taskArchive, workStart/workEnd on projects, _showSubTasksMode, numeric
issueId, null timeEstimate/created, plannedAt, TODAY_TAG in repeat
configs, and lang config section.
Add 12 fixture-based tests that verify each of these conversions.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(backup): resolve lint issues in legacy migration code
- Cast legacy project/tag to `any` (v17 types lack workStart/workEnd)
- Remove unused TaskState import
- Rename functions to camelCase (eslint naming-convention)
- Add eslint-disable for prefer-arrow-functions and naming-convention
- Replace require() with ES import for test fixture
- Fix prettier formatting in backup.service.ts import
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(backup): preserve archiveYoung for v13-v15 backups in migration 2
Migration 2's skip condition checked timeTracking.project length, which
is 0 for users who never used project-level work tracking. For v13-v15
backups (already archive-split but detected via improvement/obstruction),
this caused archiveYoung to be overwritten with empty data since
taskArchive is undefined.
Fix: skip migration 2 when archiveYoung already has tasks, or when
archiveYoung + archiveOld + timeTracking all exist without taskArchive
(meaning migration 2 already ran in v13+).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(backup): use nullish coalescing for numeric task fields in migration
Use ?? instead of || for issueAttachmentNr and issuePoints to preserve
valid 0 values during legacy backup migration. Add tests for this case.
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Update es.json
Modified typos found while using the app, also gave better structure to some phrases on the UI that weren't clear due to lack of context.
Will check further whenever I am able to.
* Update src/assets/i18n/es.json
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---------
Co-authored-by: Johannes Millan <johannesjo@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
After upload, latestSeq was set to mergedOps.length (total ops count)
instead of finalSyncVersion. This caused false "Server sequence
decreased" warnings when ops count exceeded syncVersion, and could
trigger unnecessary re-downloads. Aligns upload with download behavior
which already correctly uses syncVersion.
- Replace fragile VectorClockComparison[result] enum lookup with safe cast
- Return CONCURRENT instead of EQUAL when only one side has non-shared
keys in pruning-aware mode (safe direction: triggers LWW instead of
silent skip)
- Fix docs claiming clocks "reset" at MAX_SAFE_INTEGER (they throw)
- Add Logger.warn() in ValidationService when oversized vector clocks
are pruned server-side, improving observability for buggy clients
- Add tests for hasVectorClockChanges (previously zero coverage)
- Add symmetric LESS_THAN test for pruning-aware comparison mode
- Export MAX_LWW_REUPLOAD_RETRIES constant to eliminate magic number
coupling between sync-wrapper service and its test
- Add test verifying uploading client ID is preserved during server-side
clock pruning
- Add test for limitVectorClockSize when preserveClientIds exceeds MAX
Make hasVectorClockChanges pruning-aware by checking clock size before
logging missing keys — downgrade to verbose when pruning is likely,
warn when corruption is likely. Add return value assertion to LWW retry
exhaustion test, asymmetric pruning test cases, server-side pruning
tradeoff documentation, and fix stale "max 50" in docs.
When both vector clocks are at MAX size (pruning-aware mode) and shared
keys compare as equal, non-shared keys on both sides indicate genuinely
different causal histories. Returning EQUAL caused silent data loss by
skipping operations as duplicates. Returning CONCURRENT safely triggers
LWW conflict resolution instead.
Also fixes server snapshot pruning to preserve the requesting client's
ID and the snapshot author's client ID when limiting vector clock size.
Return CONCURRENT instead of EQUAL when two max-size clocks share no
keys (independent client populations). Fix misleading SyncStatus.InSync
return when LWW retry exhaustion leaves pending ops. Document known
limitation of size-based pruning heuristic. Add missing edge case tests.
Address issues found by code review agents after the LWW loop fix
(cb36c09538):
- Enforce MAX_VECTOR_CLOCK_SIZE on server upload to prevent oversized
clocks from buggy/adversarial clients
- Limit aggregated snapshotVectorClock in download service to prevent
unbounded growth with many clients
- Set UNKNOWN_OR_CHANGED status when LWW retry cap is exhausted instead
of misleadingly reporting IN_SYNC
- Wrap startPostSyncCooldown() in try-catch so endApplyingRemoteOps()
always runs even on failure
- Cap limitVectorClockSize output at MAX even when preserved IDs exceed
the limit
- Update stale doc comments referencing MAX_VECTOR_CLOCK_SIZE=8 to 10
Three root causes addressed:
1. Pruning asymmetry: Different clients preserve their own clientId
during vector clock pruning, producing different clock shapes.
When compared, pruned-away keys default to 0, causing false
CONCURRENT verdicts and infinite rejection loops. Fix: when both
clocks are at MAX_VECTOR_CLOCK_SIZE, compare only shared keys
(intersection) instead of the union.
2. Timing gap: Between endApplyingRemoteOps() and
startPostSyncCooldown(), isInSyncWindow() returns false, allowing
selector-based effects to fire and create TAG:TODAY operations.
Fix: start cooldown BEFORE ending remote ops flag.
3. No retry limit: LWW re-upload had no cap, enabling infinite loops.
Fix: cap at 3 retries, defer remaining to next sync cycle.
Also moves MAX_VECTOR_CLOCK_SIZE and limitVectorClockSize into
@sp/shared-schema so client and server share the same pruning
constant and algorithm.
- Fix isLoading staying true after search results arrive in issue panel
- Use getPayloadKey() for correct entity key derivation in conflict resolution
- Update archive-wins comments/logs to reflect all op types, not just updates
- Add archive-wins unit tests for local/remote archive vs UPDATE/DELETE
- Tighten WebDAV archive E2E assertion to deterministic archive-wins check
- Add @deprecated JSDoc to legacy pfapi meta-model-ctrl.js
- Refactor wasMaximizedBeforeHide from exported let to getter/setter
saveCurrentStateAsSnapshot() omitted snapshotEntityKeys, causing the
"Old snapshot format detected" warning and triggering compaction on
every sync. Extract entity key logic to shared utility and use it in
both snapshot and compaction services.
Add null/NaN guard in LocaleDatePipe to return null for invalid values
instead of letting Angular's DatePipe throw NG02100. Add devError warning
in task-reminder snack$ effect for early detection of invalid dueWithTime.
CapacitorHttp returns empty response bodies for GET requests on some
Android WebDAV providers (e.g. Koofr bridging Google Drive), causing
InvalidFilePrefixError with an unhelpful "Unknown error" message.
Route all HTTP methods through the OkHttp-based WebDavHttp plugin on
Android instead of only WebDAV-specific methods. Add empty-body guard
in download() as defense-in-depth and clarify null guard in base provider.
Closes#6262
The ensureTasksDueTodayInTodayTag$ defensive effect only checked
selectTasksDueForDay (dueDay field), but scheduleTaskWithTime clears
dueDay when setting dueWithTime (mutual exclusivity). This caused
scheduled recurring tasks to become invisible to the recovery effect
on synced devices, leading to tasks disappearing from Today view.
Now also checks selectTasksWithDueTimeForRange to catch tasks with
only dueWithTime set. Also fixes workContextId in repeat task creation
to use the config's projectId instead of the active work context.
When _uploadWithRetry re-downloads after a 412 and gets the same rev as
before, the server's If-Unmodified-Since check is broken (timezone offset
or precision rounding). Force the upload since no real concurrent
modification occurred. Fixes#6218.
Mark pfapi-config.js, api/pfapi.js, and api/index.js as deprecated
legacy code from the old PFAPI sync system, kept only for
backward-compatibility during migration to the operation-log system.
After the refactor in e6a6b8c453 that replaced the LWW Update regex with
a Set/Map derived from ENTITY_TYPES, getLwwEntityType returns undefined
for unknown types instead of matching via regex. The action is silently
passed through without reaching the warn path, so remove the
console.warn expectation.
Verify that merged vector clocks exceeding MAX_VECTOR_CLOCK_SIZE (8) are
pruned in SupersededOperationResolverService (entity, moveToArchive, and
DELETE paths) and ConflictResolutionService (local-win update and
archive-win paths). Also verify that current and protected client IDs
are preserved during pruning.
Routine sync events (concurrent modifications, gap recovery, vector clock
pruning, orphaned TODAY_TAG IDs) were logged at warn level, cluttering the
console and obscuring actual problems. Downgrade these to normal/info since
they represent expected multi-client behavior, not actionable issues.
Add limitVectorClockSize() after mergeAndIncrementClocks() in all code
paths that create operations outside the regular capture pipeline. The
regular pipeline prunes to MAX_VECTOR_CLOCK_SIZE (8 entries), but
SupersededOperationResolverService and ConflictResolutionService were
storing unpruned merged clocks (17+ entries). This mismatch caused an
infinite rejection loop: unpruned replacement ops were accepted by the
server, then subsequent pruned regular ops appeared CONCURRENT and were
rejected, triggering another unpruned replacement, ad infinitum.
The legacy pfapi meta-model-ctrl.js rejected client IDs shorter than
10 chars, but ClientIdService now generates compact 6-char IDs
(e.g., "B_FXMz"). This caused a crash on startup for users who
upgraded and set up sync. Mirror the dual-format validation from
ClientIdService to accept both old (10+ chars) and new compact format.
Fixes#6274
Replace brittle regex matching of LWW Update action types with typed
lookup structures (Set and Map) derived from the ENTITY_TYPES source
of truth. This eliminates regex overhead and ensures action type
construction stays in sync with entity type definitions.
Also fixes a bug in the integration spec where '[Task] LWW Update'
(mixed case) was used instead of the correct '[TASK] LWW Update'.
Includes backward compatibility: client accepts both CONFLICT_SUPERSEDED
and CONFLICT_STALE from the server, and the server keeps a deprecated
CONFLICT_STALE alias. Remove after all deployments are updated.
Starting a task from the reminder popup or countdown banner called
unscheduleTask which cleared both dueDay and dueWithTime, removing the
task from Today. Use dismissReminderOnly instead, which only clears
remindAt while preserving scheduling fields.
Closes#6263
- Fix unknown entity test: restore pass-through assertion by
reconfiguring the global window.confirm spy instead of try/catch
- Add replace-vs-merge test to verify singleton LWW fully replaces
state rather than merging with existing
- Use imported feature name constants (CONFIG_FEATURE_NAME,
TIME_TRACKING_FEATURE_KEY) instead of string literals
- Strengthen "no id required" test to verify updated state content
- Add type/meta stripping constraint comment in meta-reducer
- Add initial state assertions in E2E test before toggling
Two complementary fixes prevent moveToArchive operations from being
undone by LWW Update conflict resolution:
Fix 1 (conflict-resolution.service.ts): Archive always wins over
field-level updates in _resolveConflictsWithLWW(), bypassing timestamp
comparison. This prevents LWW Update ops from being created for entities
being archived, regardless of which side has a newer timestamp.
Fix 2 (bulk-hydration.meta-reducer.ts): Pre-scan operation batches for
moveToArchive ops and skip LWW Updates targeting the same entities.
Uses both entityIds array and entityId fallback for robustness.
This handles the same-batch case (3+ clients) where both ops arrive
in a single sync cycle.
Also adds Bug A test coverage gaps (clientId and clock merging) and
E2E test for the Bug B scenario.
Singleton entities (GLOBAL_CONFIG, TIME_TRACKING) were silently dropped
by lwwUpdateMetaReducer because it only handled adapter entities. Add a
singleton branch that replaces the entire feature state with the winning
LWW data, and use devError for programming error paths.
When moveToArchive was rejected by the server due to concurrent conflicts,
the stale operation resolver permanently discarded it because archived tasks
are no longer in the NgRx store (getCurrentEntityState returns undefined).
Re-create the operation with a merged vector clock preserving the original
payload, and extract shared _recreateOpWithMergedClock helper used by both
DELETE and moveToArchive handling.
Lazy load ical.js (~76 KB) in CalDAV client service using the existing
loadIcalModule() pattern and lazy load chrono-node (~150 KB) in
short-syntax.ts by making date parsing async. Reduces initial bundle
from 5.54 MB to 5.42 MB (120 KB savings, well under 5.50 MB budget).
Replace sequential archiveYoung/archiveOld writes with a single
IndexedDB transaction in _handleFlushYoungToOld(), ensuring both
stores are written atomically. This prevents potential data loss
if a failure occurs between the two writes during remote sync.
Problem:
The note fullscreen dialog leaves out some space at the bottom due to max-height being set to 95vh.
Changes:
Change the max-height hardcoded value of 95vh to 100vh in `.cdk-overlay-pane.mat-mdc-dialog-panel`
Use parse() instead of parseInline() for list items to handle block-level
tokens in loose lists. Strip <p> tags from task items to keep checkbox
and text on the same line.
When the native Android foreground service has less elapsed time than
the app (negative duration), the old code was incorrectly adding the
native value to the current time, corrupting the data.
Now when duration is negative (indicating the native service crashed
and restarted), we keep the app's higher value to prevent data loss,
update the native notification to show the correct time, and reset
the tracking interval to prevent double-counting.
When syncing time from the Android foreground service after app resume,
addTimeSpent only updated local NgRx state but did not create an operation
in the log. This caused time loss if the app was closed before the next
flush cycle.
Now dispatches syncTimeSpent after addTimeSpent to ensure the synced time
is captured in the operation log and persisted to IndexedDB.
Fixes#6207