Commit graph

17934 commits

Author SHA1 Message Date
Johannes Millan
ce4e61fd94 fix(ci): grant write permissions to Claude Code action
The Claude Code GitHub Action was failing with 403 errors when trying
to create comments on issues due to read-only permissions. Updated
contents, pull-requests, and issues permissions from read to write.
2026-01-16 22:34:49 +01:00
Johannes Millan
f421d2387a fix(e2e): add robust overlay cleanup to prevent blocked clicks
Angular Material overlay backdrops were not being properly cleared between
tag operations, causing subsequent clicks to timeout when overlays blocked
element interactions.

Added ensureOverlaysClosed() helper with:
- Early exit if no overlays present (performance)
- Escape key dismissal with retry for stacked overlays
- Logging for debugging when fallbacks trigger
- Uses Playwright's native locator.waitFor() instead of waitForFunction()
- Cleanup at operation start (prevent blocking) and end (clean state)

Benefits:
- Eliminates fixed timeouts, uses smart waiting (tests run 2x faster)
- Handles edge cases like stacked overlays
- Provides visibility into when overlays are unexpectedly present

Fixes 4 failing tests:
- Tag CRUD: remove tag via context menu
- Tag CRUD: delete tag and update tasks
- Tag CRUD: navigate to tag view
- Menu: toggle tags via submenu
2026-01-16 22:34:49 +01:00
Johannes Millan
e8054b1b3d fix(focus-mode): prevent CPU spike on task completion
Refactored updateBanner$ effect from selector-based to action-based pattern
to prevent excessive re-evaluation that caused 95-107% CPU spikes when
marking tasks as done with Focus Mode enabled.

Changes:
- Converted updateBanner$ from combineLatest([10 selectors]) to action-based
  pattern that only fires on relevant Focus Mode actions
- Added throttling (500ms) to limit banner updates to max 2/second
- Added skipWhileApplyingRemoteOps guards to setTaskBarProgress$ and
  playTickSound$ effects to prevent duplicate operations during sync
- Added distinctUntilChanged to flatDoneTodayNr$ selector to reduce
  unnecessary task list filtering

Fixes #6001
2026-01-16 22:34:49 +01:00
Johannes Millan
5b1a843196 fix(e2e): add robust overlay cleanup to prevent blocked clicks
Angular Material overlay backdrops were not being properly cleared between
tag operations, causing subsequent clicks to timeout when overlays blocked
element interactions. Added waitForOverlaysToClose() helper with multiple
fallback strategies (natural close, Escape key, retry) to ensure clean state.

Fixes 4 failing tests:
- Tag CRUD: remove tag via context menu
- Tag CRUD: delete tag and update tasks
- Tag CRUD: navigate to tag view
- Menu: toggle tags via submenu
2026-01-16 22:34:49 +01:00
Johannes Millan
7f4e5381d0 fix(plugins): wire up translation loading to i18n service (CRITICAL)
Fixes the critical issue where translations were loaded but never
passed to PluginI18nService, making the i18n system non-functional.

Changes:
- Inject PluginI18nService in PluginService
- Load translations into i18n service in 3 locations:
  - _loadPluginLazy() for lazy-loaded plugins
  - _loadPlugin() for file-based plugins
  - _loadUploadedPlugin() for cached plugins
- Improve LANGUAGE_CHANGE hook type guard
  - Use explicit LanguageCode type predicate
  - Remove non-null assertion (no longer needed)

This makes api.translate() functional for all plugin loading paths.
2026-01-16 18:48:41 +01:00
Johannes Millan
5caef4132f fix(plugins): fix i18n-related test failures
- Add global config initial state to plugin-hooks.effects tests
  - Provide localization config to prevent undefined access
  - Fixes 6 failing tests in taskUpdate$ suite

- Add PluginI18nService mock to plugin-runner tests
  - Mock translate, getCurrentLanguage, and translation loading methods
  - Fixes 2 failing tests due to missing Store provider

All 6377 tests now passing
2026-01-16 18:11:12 +01:00
Johannes Millan
1858a09429 test(plugins): add comprehensive i18n tests (Phase 8)
- Add plugin-i18n.service.spec.ts with 21 unit tests
  - Translation loading and fallback chain
  - Parameter interpolation
  - Nested key lookup
  - Language switching
  - Edge cases (empty objects, numeric values)

- Add plugin-i18n-date.util.spec.ts with 31 unit tests
  - Date formatting in multiple locales
  - All format types (short, medium, long, time, datetime)
  - Input parsing (Date, ISO string, timestamp)
  - Invalid input handling
  - Edge cases (leap years, boundaries, midnight/noon)

- Fix TypeScript error in plugin-hooks.effects.ts
  - Add filter before distinctUntilChanged to handle null/undefined
  - Use non-null assertion after filter

All 52 tests passing
2026-01-16 18:01:50 +01:00
Johannes Millan
5bb8b24c82 docs(plugins): add comprehensive i18n documentation
Phase 7: Create documentation

- Create PLUGIN_I18N.md with complete i18n guide
  - Quick start guide with file structure
  - Manifest configuration details
  - Translation file format and best practices
  - Complete API documentation (translate, formatDate, getCurrentLanguage)
  - Language change hook documentation
  - Full list of supported languages (24 languages)
  - Complete working example with multi-language support
  - Best practices and troubleshooting sections
  - Migration guide from hard-coded strings
  - Testing and performance considerations

- Update README.md with i18n section
  - Add i18n API methods to Plugin API section
  - Add languageChange hook to hooks list
  - Add i18n example to usage section
  - Add i18n files to optional files list
  - Add i18n best practice
  - Link to comprehensive PLUGIN_I18N.md guide

Documentation provides complete guide for plugin developers to add
multi-language support to their plugins with working examples.
2026-01-16 17:55:34 +01:00
Johannes Millan
f166ae1595 feat(plugins): add language support display in plugin UI
Phase 6: Update UI to display supported languages

- Add language name mapping for all supported language codes
- Add getPluginLanguages() helper to format language list
- Add supportsCurrentLanguage() to check current language support
- Display languages in plugin metadata table
- Add visual indicator (check icon) for plugins supporting current language
- Add CSS styling for language support highlighting
- Add LANGUAGES translation key to T.const and en.json

UI features:
- Shows "English only" for plugins without i18n or only English
- Shows comma-separated language names (e.g., "English, German, French")
- Highlights languages in primary color when plugin supports current app language
- Displays check_circle icon next to supported languages
2026-01-16 17:52:13 +01:00
Johannes Millan
c742295624 feat(plugins): add i18n API methods and fix LANGUAGE_CHANGE hook
Phase 4-5: Plugin API Extensions and Language Switching

- Add translate(), formatDate(), getCurrentLanguage() to PluginAPI
- Inject PluginI18nService into PluginAPI constructor
- Update PluginRunner to pass PluginI18nService to PluginAPI
- Fix LANGUAGE_CHANGE hook bug (was firing on work context changes)
- Add proper languageChange$ effect listening to global config updates
- Wire language changes to PluginI18nService.setCurrentLanguage()
- Remove incorrect workContextChange$ effect dispatch

Translation API features:
- Simple translate(key, params?) with fallback chain
- Locale-aware formatDate(date, format) with predefined formats
- getCurrentLanguage() to get current app language

Language switching:
- Listens to updateGlobalConfigSection for 'localization' section
- Uses distinctUntilChanged to fire only on actual language changes
- Updates plugin i18n service and dispatches hook to plugins
2026-01-16 17:52:13 +01:00
Johannes Millan
cde660bd0c feat(plugins): add date formatting utility for i18n (Phase 4 partial)
- Create plugin-i18n-date.util.ts with formatDateForPlugin()
- Supports 5 predefined formats: short, medium, long, time, datetime
- Uses Intl.DateTimeFormat for locale-aware formatting
- Handles Date, string, and number inputs
- Graceful fallback to English for invalid locales

Part of Phase 4: Plugin API Extensions
2026-01-16 17:52:13 +01:00
Johannes Millan
ea5fbb60af fix(e2e): wait for overlay cleanup to prevent tag operation failures
**Problem:**
E2E tests started failing after PR #6010 with timeouts when clicking the Tags
group button in the sidebar. The failure occurred in tag deletion and removal
tests that previously worked.

**Root Cause:**
PR #6010 added `<div (click)="$event.stopPropagation()">` wrapper around tag
menu items to prevent menu closure when toggling tags. However, this prevented
Material CDK from detecting clicks properly, leaving overlay backdrops in the
DOM after menu operations. These lingering backdrops blocked subsequent clicks
on the Tags sidebar button, causing Playwright to timeout waiting for the
element to become "stable".

**Solution:**
Added explicit waits for `.cdk-overlay-backdrop` to disappear after menu
operations in:
- `assignTagToTask()`: Wait after assigning tag via context menu
- `removeTagFromTask()`: Wait after removing tag via context menu
- `deleteTag()`: Wait before attempting to interact with sidebar

**Changes:**
- e2e/pages/tag.page.ts: Add overlay cleanup waits with proper error handling
- All waits use `.catch(() => {})` to gracefully handle cases where no overlay exists

**Testing:**
Verified with `npm run checkFile e2e/pages/tag.page.ts` - all checks pass.

Fixes failing tests:
- tags/tag-crud.spec.ts:47 "should remove tag from task via context menu"
- tags/tag-crud.spec.ts:80 "should delete tag and update tasks"
- tags/tag-crud.spec.ts:117 "should navigate to tag view when clicking tag in sidebar"
- menu/menu-touch-submenu.spec.ts:71 "should support toggling tags via submenu"
- sync/webdav-sync-delete-cascade.spec.ts:77 "Delete tag with archived tasks syncs"
2026-01-16 17:52:13 +01:00
Johannes Millan
2d6955011c feat(plugins): add i18n manifest validation and translation loading (Phase 2-3)
- Extend validatePluginManifest to validate i18n configuration
  - Check i18n.languages is array of valid language codes
  - Warn if English not included (fallback language)
  - Warn about unsupported language codes
  - Return both errors and warnings

- Update PluginLoaderService to load translation files
  - Load i18n/{lang}.json for each declared language
  - Store translations as Record<string, string> (lang -> JSON)
  - Support both file-based and uploaded plugins
  - Log translation loading success/failure per language

- Extend PluginCacheService for translations
  - Add translations field to CachedPlugin interface
  - Update storePlugin to accept and cache translations
  - Calculate and log translation file sizes
  - Support retrieving translations from cache

Validates against app's supported LanguageCode enum.
Translation files loaded as strings, parsed later by PluginI18nService.
2026-01-16 17:52:13 +01:00
Johannes Millan
95578ef77b feat(plugins): add plugin i18n foundation (Phase 0-1)
- Add i18n field to PluginManifest type in plugin-api package
- Create PluginI18nService for translation management
  - Load translations from file paths or cached content
  - Nested key lookup with dot notation (e.g., BUTTONS.SAVE)
  - Smart fallback: current language → English → key
  - Parameter interpolation with {{param}} syntax
  - Language switching via signals
  - Memory cleanup for unloaded plugins
- Add type validation for cached translation content
- Document language sync integration points

Part of plugin internationalization system implementation.
Tests will be added in Phase 8.
2026-01-16 17:52:13 +01:00
johannesjo
0177b4860d fix(ios): remove alpha from icon, add safe area styling 2026-01-16 17:25:49 +01:00
johannesjo
d91d5e892b fix(ios): set background color for safe area behind home indicator
- Add backgroundColor to Capacitor iOS config to fill safe area
- Add CSS :has() rules to set html background based on theme class
2026-01-16 17:24:47 +01:00
johannesjo
ae0ca10c57 fix(ios): extend viewport behind safe areas to fix bottom white area
Add viewport-fit=cover to the viewport meta tag so iOS extends the web
view behind the home indicator area, allowing the bottom navigation
background color to fill the entire screen.
2026-01-16 16:52:58 +01:00
johannesjo
5813ea6f94 fix(ios): strip pre-release suffix from version for App Store 2026-01-16 16:52:53 +01:00
johannesjo
98855bb577 fix(ios): skip code signing during archive, sign during export 2026-01-16 16:37:53 +01:00
johannesjo
28da8999f2 fix(ios): show reminder dialog and prevent duplicate notifications
- Show reminder dialog on iOS when app is opened with pending reminders
  (Android still skips dialog since native notification actions work in background)
- Skip scheduling past reminders to prevent duplicate notifications when
  the effect runs after a reminder has already fired
2026-01-16 16:09:22 +01:00
johannesjo
9d19a481c8 fix(ios): use correct bundle ID com.super-productivity.app 2026-01-16 16:07:33 +01:00
johannesjo
5b4dfa09a3 fix(ios): fix archive step for CocoaPods compatibility
- Remove PROVISIONING_PROFILE_SPECIFIER from archive (breaks Pods)
- Use CODE_SIGN_STYLE=Automatic for archive step
- Manual signing handled during export via ExportOptions.plist
- Add bundle ID verification to catch mismatches early
2026-01-16 16:03:51 +01:00
johannesjo
d58f5eb6b7 docs: add iOS App Store build and publish notes 2026-01-16 16:02:13 +01:00
johannesjo
b05add91a3 fix(tour): disable add-task-bar auto-focus when tour is active
Prevents keyboard from popping up on mobile when reading tour instructions
by binding isDisableAutoFocus to shepherdService.isActive.
2026-01-16 15:48:54 +01:00
johannesjo
04a7e0e6aa refactor(ios): reuse existing mac_certs for iOS signing
Apple Distribution certificate works for both macOS and iOS.
Only new secret needed: ios_provision_profile
2026-01-16 15:42:28 +01:00
johannesjo
2996aaa361 feat(ios): add GitHub Actions workflow for iOS App Store release
- Create build-ios.yml workflow triggered on releases
- Configure signing keychain with iOS distribution certificate
- Install provisioning profile for App Store distribution
- Sync version from package.json using agvtool
- Build, archive, and export IPA with manual code signing
- Validate and upload to App Store Connect via xcrun altool
- Add sync:ios and dist:ios:prod npm scripts
2026-01-16 15:24:03 +01:00
Johannes Millan
0345c0c11c fix(metric): fix worklog aggregation for all-tasks metrics
**Problem:**
AllTasksMetricsService used take(1) which locked worklog data to whatever
context was active at initialization. This caused incorrect metrics when
navigating to /tag/TODAY/metrics after viewing a project.

**Solution:**
- Make service reactive to activeWorkContext$ changes
- Filter to only compute when context is TODAY_TAG
- Replace DataInitStateService with WorkContextService
- Add explicit filter for TODAY_TAG context
- Leverage existing WorklogService behavior: getCompleteStateForWorkContext
  automatically returns ALL tasks when context is TODAY_TAG

**Changes:**
- AllTasksMetricsService now triggers on activeWorkContext$ instead of isAllDataLoadedInitially$
- Only computes metrics when context is TODAY_TAG (matches MetricComponent logic)
- Tests updated to reflect reactive behavior (16 tests, all passing)
- Signal retains last value when observable stops emitting (expected behavior)

**Benefits:**
- Metrics recompute correctly when switching contexts
- WorklogService already handles 'all tasks' for TODAY_TAG
- No need for custom worklog aggregation
- Tests verify context switching and recomputation

Addresses CRITICAL issue from Codex code review.
2026-01-16 14:22:00 +01:00
Johannes Millan
ca377b93a2 fix(metric): add translation support for 'all tasks' title
- Add T.PM.ALL_TASKS_TITLE translation key
- Use translation pipe in metric.component.html
- Add type safety check for TODAY_TAG context
- Update tests to expect translation keys
2026-01-16 14:21:14 +01:00
Johannes Millan
30f541f6c7 test(capacitor-reminder): add missing Store provider to fix test failures 2026-01-16 14:18:11 +01:00
Johannes Millan
395bba6430 fix: resolve post-merge build errors
- Install missing Capacitor dependencies (@capacitor/keyboard, @capacitor/status-bar)
- Add missing useAlarmStyle argument to scheduleNativeReminder in capacitor-reminder.service.ts
- Fix undeclared variable errors in project-metrics.service.spec.ts
- Access reminder config correctly via GlobalConfigService.cfg()

All TypeScript compilation errors resolved and build passes.
2026-01-16 13:49:22 +01:00
Johannes Millan
7aee741c1d Merge branch 'feat/ios' into master
Resolves merge conflicts in:
- src/app/core/startup/startup.service.ts: Keep both _store and _platformService injections
- src/app/features/android/store/android.effects.ts: Refactor to use platform-agnostic CapacitorReminderService

The feat/ios branch adds iOS support via Capacitor, introducing platform-agnostic
services for notifications, reminders, and platform detection that work across
web, Android, and iOS.
2026-01-16 13:45:31 +01:00
Johannes Millan
312443db63 style(metrics): remove unused imports and add eslint-disable for date string literals
Remove unused imports (SimpleMetrics, TODAY_STR, EMPTY, worklogService, workContextService)
and add eslint-disable comment for naming-convention to allow date string object keys in tests.
2026-01-16 13:36:14 +01:00
Johannes Millan
13d7afc458 refactor(e2e): extract ensureGlobalAddTaskBarOpen helper to reduce code duplication
Extract ensureGlobalAddTaskBarOpen() to e2e/utils/element-helpers.ts to avoid
duplicating the logic for opening the global add task bar across multiple tests.

This helper properly waits for the add button and input to be visible,
preventing race conditions in tests.
2026-01-16 13:35:59 +01:00
Johannes Millan
cb1640bca3 feat(metrics): add all-tasks metrics view for Today page
Implements aggregated metrics across all projects and tags when viewing the Today page (/tag/TODAY/metrics).

Key Changes:
- Add AllTasksMetricsService to aggregate metrics from all contexts
  * Sums break numbers and times across all projects and tags
  * Uses TaskService.getAllTasksEverywhere() for task counts
  * Leverages existing worklog behavior for time tracking

- Update MetricComponent with dynamic service switching
  * Detects TODAY_TAG context via computed signal
  * Switches between AllTasksMetricsService and ProjectMetricsService
  * Dynamic title: "Metrics (all tasks)" for TODAY_TAG

- Comprehensive test coverage (73 new tests)
  * metric.util.spec.ts: 17 tests for core calculation logic
  * project-metrics.service.spec.ts: 18 tests for service behavior
  * all-tasks-metrics.service.spec.ts: 19 tests for aggregation
  * metric.component.spec.ts: 19 tests for component integration

All tests passing (6,264+ tests across timezones).
2026-01-16 13:35:47 +01:00
Johannes Millan
59292abacb refactor(date): use getDbDateStr() utility for consistent date formatting
Replace inline `.toISOString().split('T')[0]` pattern with getDbDateStr()
utility across production code, tests, and plugin examples.

Changes:
- Production: 6 files (plugin-bridge, metrics, counters, task-repeat-cfg)
- Tests: 4 spec files (helper functions and direct uses)
- Plugin example: Added formatDateStr() helper matching main app pattern

This ensures consistent local timezone handling for all user-facing date
operations and provides a single source of truth for date string formatting.
2026-01-16 13:28:05 +01:00
Johannes Millan
1b47d463a1 fix(tags): prevent autocomplete overlay backdrop from blocking interactions
Explicitly close the icon autocomplete panel before closing the create tag
dialog to prevent Material CDK overlay backdrop from remaining in DOM and
intercepting pointer events. Fixes E2E test failures where rapid dialog
closure left the autocomplete overlay active.
2026-01-16 13:28:05 +01:00
Johannes Millan
fecc62f7d3 fix(test): use getDbDateStr() in tests to fix timezone-dependent failures
Replace UTC date strings (toISOString().split('T')[0]) with getDbDateStr()
to match implementation behavior. Fixes 3 CalendarIntegrationService tests
and 1 StartupService test that failed in non-UTC timezones.
2026-01-16 13:28:05 +01:00
Johannes Millan
b428942a75 fix(privacy): complete implementation of PII field masking
Add missing 6 fields to KEY_TO_REPLACE array that were tested in da43ae08c but not implemented:
- resourceName (prevents email exposure in CalDAV fields)
- name (masks project/tag names)
- description (masks event/task descriptions)
- location (masks event locations)
- calProviderId (masks calendar provider IDs)
- summary (masks calendar event summaries)

All 24 unit tests now pass.
2026-01-16 13:28:05 +01:00
Johannes Millan
27e17222d4 fix(ci): add github_token fallback to fix OIDC token error 2026-01-16 13:28:05 +01:00
Johannes Millan
ac6bdd8ed6 fix(privacy): mask additional PII fields in crash exports
Closes #6020

- Add 6 new fields to privacy export masking: resourceName, name, description, location, calProviderId, summary
- Prevents email exposure in CalDAV resourceName fields
- Masks project/tag names and CalDAV event details
- Update crash export button tooltip for clarity
- Add comprehensive unit tests (24 tests, all passing)
2026-01-16 13:28:05 +01:00
Johannes Millan
f9fd8454cc fix(sync-md): prevent crash when adding subtasks to markdown file directly
Fixes #6021

When users manually added subtasks to markdown files that referenced
non-existent parent tasks, the plugin would crash due to unsafe null
assertions and missing validation. This made the plugin permanently
disabled and required creating a new markdown file to recover.

Changes:
- Added parent ID validation before creating operations
- Orphaned subtasks are now converted to root tasks with warnings
- Added comprehensive error handling with user notifications
- Plugin stays enabled even after sync errors
- Added 15 new tests (689 lines) for orphaned subtask scenarios
- Fixed all existing tests to support new error notifications

The fix implements defense-in-depth:
1. Validation layer: Check parent IDs exist before operations
2. Error handling: Catch and report errors without crashing
3. User feedback: Clear notifications about issues
4. Data preservation: No data loss, orphans become root tasks
2026-01-16 13:28:05 +01:00
Johannes Millan
ac929a5dbe fix(planner): add shareReplay to tomorrow$ to fix Daily Summary Plan tab
The Plan tab in Daily Summary was not displaying tasks because tomorrow$
observable lacked shareReplay(1), causing async pipe subscriptions to miss
cached values on cold start. This fix matches the pattern already used by
days$ observable.

Fixes #6022
2026-01-16 13:28:05 +01:00
Johannes Millan
07105af0b3 refactor(focus-mode): extract banner action business logic and add performance tests
**Banner Action Refactoring:**
- Merged _getTextButtonActions() and _getIconButtonActions() into unified _getBannerActions()
- Extracted business logic into 6 testable helper methods:
  - _handleStartAfterBreak() - Start session after break completion
  - _handleStartAfterSessionComplete() - Start break or session after completion
  - _handlePlayPauseToggle() - Toggle play/pause
  - _handleSkipBreak() - Skip current break
  - _handleEndSession() - End session manually
  - _handleOpenOverlay() - Open focus overlay
- Eliminated 150+ lines of duplicated code
- Improved testability and separation of concerns
- Updated all test calls to use new unified method with useIcons parameter

**Performance Regression Tests:**
- Added 4 performance tests for block merging algorithm (create-sorted-blocker-blocks.spec.ts)
- Tests validate O(n log n) performance improvements from commit 7ec5bfba9
- Covers 1000 and 10,000 block scenarios with timing assertions
- Tests overlapping and non-overlapping merge cases
- Each test verifies both correctness (sorted output) and execution time

**Benefits:**
- Better maintainability (changes in one place)
- Easier to test business logic independently
- Guards against performance regressions
- Preserves all original behavior

**Testing:**
- All 132 focus-mode tests pass ✓
- All 33 block merging tests pass ✓

**Code Review:**
- Codex Review: APPROVED (no critical issues)
- Warnings addressed with eslint-disable for performance test arithmetic
2026-01-16 13:28:05 +01:00
Johannes Millan
6ba270d50f
Merge pull request #6025 from steindvart/rework-main-header
Main header redesign
2026-01-16 13:27:32 +01:00
Ivan Kalashnikov
e5d3b0f257 fix: hide sidebar of play button every time when page-title container is hover. 2026-01-16 19:23:53 +07:00
Ivan Kalashnikov
328bf3a70d fix: reorder counters action group structure for play-button in mobile. 2026-01-16 19:18:49 +07:00
Ivan Kalashnikov
c08499da0b refactor: rename primary action group to counters action group for clarity 2026-01-16 19:17:03 +07:00
Ivan Kalashnikov
56b3835141 style: remove padding and border for primary action group 2026-01-16 19:04:37 +07:00
Johannes Millan
99b9afba7b
Merge pull request #6019 from 00sapo/patch-1
Add AutoPlan plugin to community-plugins.json
2026-01-16 11:56:09 +01:00
Johannes Millan
018f963270
Merge pull request #6026 from cbergmann/master
Always use project URL from issue ID
2026-01-16 11:46:12 +01:00