The login page used the web view's own origin (capacitor://localhost) as
the OpenID return URL and then navigated the web view to the provider, so
the callback had nowhere to land. Register an `actualbudget://` URL scheme
in both native shells, use it as the return URL inside the native apps,
and route the `appUrlOpen` callback from @capacitor/app to the existing
/openid-cb page, which writes the token and finishes the login. Navigating
the web view to the provider is what hands the sign-in to the system
browser: Capacitor opens any top-level navigation to an outside host with
the OS and keeps the web view on the login page.
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Add isNativeMobileApp platform flag and gate web-only UI behind it
The native Capacitor builds were detected ad hoc with 'Capacitor' in window.
Expose a real isNativeMobileApp flag from shared/platform (false on electron
and the API) based on Capacitor.isNativePlatform(), which stays false on the
plain web even once @capacitor/core is bundled there.
Use it to skip what has no meaning in an App Store build: service worker
registration (assets ship in the bundle and update via the store), the
"new version of Actual is available" notification, the "learn how to set
up a server" hint on the server config screen, and the "Use the current
domain" shortcut, whose origin is the capacitor:// scheme.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Gate the GitHub release check itself in the native mobile apps
Skipping the fetch in getLatestVersion covers the update notification, the
Settings "Updates" row and the GitHub rate limit in one place, so the
per-consumer gate in FinancesApp goes away. Also inline the one-use type
alias in platform.ts and tighten the service worker comment.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Unregister any service worker left by earlier native mobile builds
Skipping registration alone leaves a worker installed by a previous native
build in control of page loads, serving the old precached bundle after a
store update. Drop existing registrations on native startup only.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Revert "Unregister any service worker left by earlier native mobile builds"
The only native builds that ever registered a service worker were TestFlight
test builds, so there are no store users with a stale worker to clean up.
This reverts commit 2eb95f623f3bc0ba7a8dd7b0e2b94b6a4ebc0c0e.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Sync and re-check the server when the mobile app resumes
The Capacitor apps only synced on an interval or a manual pull, so returning
to the app after a while showed stale data. `useOnVisible` now also listens to
`@capacitor/app` `appStateChange` on native platforms, so the existing
visibility-driven sync and server-version check run on resume too. User data
is refreshed first so the online/offline status reflects the server's
reachability.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Track server reachability from sync events everywhere
The online/offline reconciliation lived in LoggedInUser, which the narrow
(mobile) layout never renders, so the status never updated there. Move it
into the global sync-event listener so every layout keeps the status in step
with sync results, and drop the extra user-data fetch from the visibility
callback. Also fold the native app-state listener into the existing
visibility effect and trim the test mocks.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Use the isNativeMobileApp platform flag for the resume listener
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Apply CodeRabbit review feedback
Use a single foreground event source per platform in useOnVisible so a
resume in the native apps does not run the callback twice.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Coalesce visibility events that arrive during a running callback
A resume event that lands while the previous callback is still running was
dropped, so if that run failed the resume sync was lost. Queue one follow-up
run instead.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Drop a queued visibility run once the hook is cleaned up
An event coalesced while the callback was still running would start one more
run after the hook unmounted or was disabled. Clear the pending flag in both
cleanup paths.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Ignore native app-state events that land after cleanup
Capacitor removes listeners asynchronously, so an appStateChange arriving
between cleanup and removal could still run the callback. Flag the listener
as removed in cleanup and skip events after that.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Simplify the resume listener and guard the reachability refresh
Collapse useOnVisible into one effect with a shared active flag and a loop
for the coalesced follow-up run, so both platforms share the same cleanup.
Refresh user data one request at a time when sync errors arrive in a burst.
Trim the test scaffolding.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Apply CodeRabbit review feedback
Keep a queued visibility run when the callback rejects, and drop the
in-flight guard on the reachability refresh so a sync error that lands
mid-validation is not lost and a stalled request cannot block later ones.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Return useOnVisible to the minimal native listener
Drop the per-platform event source, the coalesced follow-up run and the
cleanup guards added during review. The hook keeps its original logic and
only adds the native app-state listener next to it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Restore LoggedInUser to its original sync handling
Keep the title bar's own user-data refresh so it stays in "Connecting..."
until validation completes. The global sync-events listener still covers the
mobile layout, which never renders this component.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Leave reachability reconciliation to the global sync listener
LoggedInUser only tracks sync start/stop for its loading state; the
re-fetch on a contradicting sync result lives in sync-events alone. The
status text may lag by one validate request until the store updates.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Resolve follow-redirects to 1.16.2 as on master
The merge conflict resolution kept the lockfile entry from the branch.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Add native haptic feedback to the Capacitor mobile apps
The calculator keypad relied on navigator.vibrate, which iOS does not
support. Route keypad presses, swipe-action and pull-to-refresh threshold
crossings through a shared helper that uses @capacitor/haptics inside the
native apps and falls back to the Vibration API elsewhere.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q8X9eMnLyeJnMJFhQHDcCG
* [AI] Extract pull-to-refresh haptic into a named hook and drop helper test
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q8X9eMnLyeJnMJFhQHDcCG
* [AI] Keep swipe-action revealed state in sync when the spring start is a no-op
When a swipe overshoots the full reveal (or close), the spring is already
at its target on release, so react-spring treats the final start as a
no-op and never calls onRest. isRevealed then stayed stale, which made the
next drag jump instead of follow the finger and skipped the threshold
haptic. Set the state at release time instead.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Ignore the haptics plugin dependency in knip
The native app's package.json declares @capacitor/haptics so Capacitor
links the plugin, but no JS in that package imports it, same as the
splash-screen plugin.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Start swipe drags from the spring's live position
Read x.get() when a drag starts instead of tracking an isRevealed flag.
A swipe that interrupts a snap animation now continues from where the
item actually is, and the flag (which could go stale) is no longer needed.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Rename haptics helpers for clarity
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* [AI] Submit App Store releases automatically and move TestFlight uploads to nightly
On a published release both the iOS and Mac App Store workflows now wait for
Apple to process the uploaded build, attach it to the App Store version for
the release, fill in the release notes and submit it for review with
automatic release after approval. The TestFlight uploads run on a nightly
schedule instead of on every master push.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Publish App Store builds with the Codemagic App Store Connect CLI
Replace the hand-written submission script and the altool upload with
`app-store-connect publish`, which uploads the build, waits for processing
and submits it for review in one step. The iOS export now writes the .ipa
locally so the CLI can upload it, matching the Mac flow.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Run the App Store Connect CLI through pipx instead of a virtualenv
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Share the App Store Connect publish step between the iOS and Mac workflows
Move the upload and review submission into one composite action so the CLI
version and submission flags live in a single place, drop step outputs that
nothing read, clean up signing material right after signing instead of after
the processing wait, and remove export options that were already defaults.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Do not submit prereleases for App Store review
Prereleases still upload to TestFlight; only full releases are submitted.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Send TestFlight builds to the external beta testers group
Every uploaded build is now added to the "Beta testers" TestFlight group and
submitted for Beta App Review, so external testers get the nightly builds
without a manual step. The FAQ links to the public TestFlight invite.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Wait for the uploaded build to appear before the processing wait
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Capacitor copied the whole web build into the native bundle, including
`*.map` files that are ~70% of it. Stage the build into `www/` without
them before `cap sync`, cutting the web payload from 55 MB to 18 MB.
Claude-Session: https://claude.ai/code/session_012QFXyMC7tVoYP9pebbQzCh
Co-authored-by: Claude <noreply@anthropic.com>
The Capacitor iOS app was stuck on the splash screen: the splash was
configured to never auto-hide and nothing hid it, and underneath the web
app failed because the capacitor:// scheme is never cross-origin isolated
so SharedArrayBuffer is unavailable. Drop the splash setting and run the
native app in the existing no-SharedArrayBuffer fallback mode.
Pad the mobile page header by the top safe-area inset so it is not hidden
behind the notch (a no-op in the PWA and browsers).
iOS project: bundle ID com.actualbudget.actual, arm64 device capability,
export-compliance flag, light status bar text. `release:ios` now archives
with the team from APPLE_TEAM_ID, derives the build number from a UTC
timestamp and the version from the package version, uploads to App Store
Connect, and accepts optional xcodebuild auth flags for CI.
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* 🔖 (26.8.0)
* Generate release notes for v26.8.0
* Add release notes for version 26.8.0, highlighting new stable features, mobile banking improvements, and day-level date range selection in reports.
* Update authorship in release notes for version 26.8.0 to reflect MatissJanis as the author.
* [AI] Apply review feedback to 26.8.0 release notes: clarify onboarding/mobile bank sync wording and note per-file credentials are Pluggy.ai only
* [AI] Copy review-feedback wording fixes into releases.md for 26.8.0
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <matiss@mja.lv>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>