* fix(build): keep @electron/asar on minimatch v3 for MAS universal build
The `overrides.app-builder-lib.minimatch` pin to v10 cascaded into
@electron/asar (a child of both app-builder-lib and @electron/universal),
which only works with minimatch v3. minimatch v9/v10 ship as ESM with
`__esModule: true` and no `default` export, so @electron/asar's compiled
default import (`minimatch_1.default(...)`) resolved to undefined and threw
`TypeError: (0 , minimatch_1.default) is not a function` during the
universal-app asar merge (makeUniversalApp -> mergeASARs -> shouldUnpackPath),
failing `dist:mac:mas:buildOnly` in the Mac Store release workflow.
Carve @electron/asar back to minimatch 3.1.2 via nested overrides while
app-builder-lib (and @electron/universal) keep minimatch v10 as intended.
* fix(ci): declare export compliance for iOS App Store submission
The iOS release build, upload and processing all succeed, but
`upload_to_app_store` fails at submit-for-review with:
[!] Export compliance is required to submit
Example: submission_information: { export_compliance_uses_encryption: false }
Super Productivity only relies on exempt encryption (HTTPS / standard OS
crypto), so declare that the app does not use non-exempt encryption:
- ios/App/App/Info.plist: ITSAppUsesNonExemptEncryption=false (canonical,
build-time declaration; also covers TestFlight, auto-resolved by ASC).
- fastlane/Fastfile: export_compliance_uses_encryption: false in
submission_information, so the submit-for-review API call carries the
declaration regardless of the binary.
* refactor(build): simplify @electron/asar minimatch override to top-level
Follow-up to the minimatch carve-out. Hoisting a top-level
`overrides["@electron/asar"].minimatch` pin produces a byte-identical
package-lock.json to the previous nested form, but is simpler and covers
every @electron/asar consumer (app-builder-lib, @electron/universal,
electron-winstaller) regardless of which parent wins hoisting — rather than
relying on a nested branch under app-builder-lib (whose @electron/universal
sub-branch was dead config given asar dedupes to a single instance).
* test(op-log): de-flake LockService mutex-invariant timeout test
"should preserve mutex invariant after timeout" flaked on the macOS CI
runner (TZ=America/Los_Angeles leg): it asserted that C always times out
waiting for the lock, but on a slow/loaded runner A can release the lock
before C's 50ms timeout fires, so C legitimately acquires it and runs —
after A has finished. The observed `['a-start','a-end','c-start']` actually
satisfies the invariant (C ran after A, never concurrently); only the
brittle "C must time out" assertion failed.
Assert the real no-concurrent-execution invariant instead: C must never
start before A ends. This still catches the original regression (concurrent
C would push 'c-start' before 'a-end') but is independent of runner timing.
---------
Co-authored-by: Claude <noreply@anthropic.com>
iOS reads a stale sync-data.json on every WebDAV sync: URLSession's
default configuration caches GET responses, so the client never sees
remote changes and — because the pre-upload conflict check re-reads the
same cached body — blindly overwrites newer remote data. Android (OkHttp,
no cache) and web (fetch `cache: 'no-store'`) are unaffected, which is why
this is iOS-only.
- iOS WebDavHttpPlugin: disable the HTTP cache (urlCache=nil +
reloadIgnoringLocalCacheData) so sync reads always hit the network.
- WebDavHttpAdapter: send `Cache-Control: no-cache, no-store` + `Pragma`
on the native request path, covering an upstream proxy/CDN cache too.
- Log allowlisted, non-sensitive cache-relevant response headers so a
future log capture localises a stale read (client cache vs proxy).
Fixes#7144
The app uses only local notifications — no @capacitor/push-notifications,
no FCM, no registerForRemoteNotifications anywhere. Declaring
remote-notification in UIBackgroundModes claims a push capability the
app never uses, which Apple may flag at App Store review.
The app targets ES2022+/Safari 18+ but the iOS deployment target was 14.0.
On older iOS versions (e.g., 15), the JavaScript fails to parse, leaving
users stuck on a white splash screen with no feedback.
- Add an inline ES5-compatible browser check in index.html that detects
unsupported browsers and shows a helpful update message
- Raise iOS deployment target from 14.0 to 16.0 in both project.pbxproj
and Podfile
- Guard productivity tips script against null elements when compat check
replaces splash screen content
Install capacitor-plugin-safe-area to read native safe area insets and
inject them as CSS variables, working around Capacitor 7's broken
adjustMarginsForEdgeToEdge and Android WebView's unreliable
env(safe-area-inset-*) values.
- Add _initSafeAreaInsets() to GlobalThemeService
- Remove broken adjustMarginsForEdgeToEdge: 'auto' from capacitor config
- Clamp context menu trigger Y to safe area top
- Fix light mode status bar color to match theme (#f8f8f7)
The plugin was compiled but never registered on iOS, causing
"WebDavHttp plugin not implemented" errors. Add a custom
CAPBridgeViewController subclass that registers the plugin,
mirroring the Android registration in CapacitorMainActivity.
- fix(tasks): preserve selectAllTasks memoization by only filtering when
undefined entities exist, avoiding new array allocation on every call
- fix(sync): replace mutable ARGON2_PARAMS export with getter/setter to
prevent test pollution across spec files
- fix(sync): convert tag-task-page from async pipe to toSignal pattern
to fix OnPush change detection after bulk sync state updates
- fix(sync): remove E2E navigation workarounds that masked the rendering
bug now fixed by the toSignal conversion
- fix(sync): add clarifying comments for archive-wins sibling conflict
resolution and waitForSyncWindow switchMap concurrency semantics
- fix(ios): add hex preview of first 16 bytes to WebDAV UTF-8 decode
error for easier debugging
- docs: add JSDoc to getDiffInWeeks noting negative diff behavior
https://claude.ai/code/session_01Y51QDFEdvrJ9VVgp9XfWLJ
Co-authored-by: Claude <noreply@anthropic.com>
Reject with DECODE_ERROR instead of silently returning empty string
when UTF-8 decoding fails on non-empty response data, preventing
the sync layer from misinterpreting decode failures as empty files.
iOS WebDAV sync could write but not read data due to Capacitor's native
bridge auto-parsing responses as JSON and replacing data with error strings.
Add a native URLSession-based plugin (matching the existing Android one)
that bypasses CapacitorHttp entirely.
Fixes#6317
- Add redirect_uri parameter to OAuth flow for mobile platforms
- Create OAuthCallbackHandlerService to handle deep link callbacks
- Register custom URI scheme (com.super-productivity.app://) in Android/iOS
- Add platform-specific UI for OAuth flow (automatic vs manual)
- Implement proper error handling for OAuth callback errors
- Add comprehensive unit tests for callback handler
- Fix memory leak by properly cleaning up event listeners
- Use IS_NATIVE_PLATFORM constant for consistent platform detection
Web/Electron continue using manual code entry (no regression).
Mobile (iOS/Android) now use automatic redirect with deep linking.
Fixes Dropbox OAuth authentication on iOS and Android platforms.
iOS renders transparent pixels as white, causing a white frame around the
app icon. This fix generates a 1024x1024 RGB PNG (no alpha channel) from
the existing build/icons/sq2160x2160.png source.
Changes:
- Add tools/generate-ios-icon.js script using Sharp to resize and remove alpha
- Add npm script 'generate:ios-icon' for reproducible icon generation
- Update AppIcon-512@2x.png to RGB format (was RGBA)
- Install Sharp as dev dependency for image processing
Icon is now fully opaque with correct brand blue color and white checkmark.