Commit Graph

20 Commits

Author SHA1 Message Date
ghost
bd740f112c fix(mobile): in-progress feedback for the Clear local data action (#2439)
* fix(mobile): in-progress feedback for the Clear local data action

The reset- and delete-account tiles already disable and show a spinner
while their network calls are in flight, but "Clear local data" (an async
op that wipes offline storage and clears the category/merchant/tag
providers) had no in-progress feedback. The settings list stayed fully
interactive during the wipe, so the row could be tapped again.

Mirror the existing reset/delete pattern: add an _isClearingData flag
(set before the work, cleared in a finally with a mounted guard) and give
the tile a trailing CircularProgressIndicator plus enabled/onTap guards
while it runs. Completes the in-progress feedback across all destructive
async actions in Settings.

flutter analyze: no new issues; full suite (166) green.

* refactor(mobile): unified destructive action guard in settings

Replace three independent boolean flags (_isClearingData, _isResettingAccount,
_isDeletingAccount) with a single _activeDestructiveAction string. All three
danger-zone tiles now disable together whenever any destructive operation is
running, preventing concurrent destructive actions.

* refactor(mobile): name the destructive-action discriminator constants

Address review feedback (jjmata): replace the scattered 'clear' / 'reset' /
'delete' string literals used for _activeDestructiveAction with private
constants (_clearAction / _resetAction / _deleteAction), defined once. The
discriminator can no longer drift via a mistyped literal across the handlers and
tiles — a typo'd constant name fails to compile. No behavior change.
2026-06-30 07:37:34 +02:00
ghost
401cd6ab08 feat(mobile): privacy mode to mask money values (#2386)
* feat(mobile): add privacy mode to mask money values

Adds an app-wide "privacy mode" so users can hide monetary amounts from
over-the-shoulder view.

- PrivacyProvider (ChangeNotifier) backed by PreferencesService, so the
  choice persists across launches and every money widget rebuilds on
  toggle.
- MoneyMasker.mask() collapses an amount's numeric portion into a short
  fixed run of bullets while keeping the currency symbol and sign
  (e.g. CA$1,234.56 -> CA$••••). A fixed run avoids leaking the value's
  magnitude and reads cleanly without stray separators. Currency- and
  locale-agnostic — it operates on already-formatted strings.
- Masking applied at every money render site: net worth + per-currency
  totals + breakdown sheet (NetWorthCard), account balances (AccountCard,
  AccountDetailHeader, transaction form account selector), and transaction
  amounts (transactions list, recent transactions, calendar).
- Two entry points: a "Hide amounts" switch in Settings -> Security, and a
  quick eye toggle in the top bar (visible on every tab).

Tests: MoneyMasker unit tests (fixed-run mask, magnitude hidden, symbol/
sign kept, passthrough, idempotent) + a widget test asserting the net
worth masks/unmasks as the provider flips; account_card_test updated to
provide the new provider. flutter analyze: no new issues; full suite
(123) green.

* fix(mobile): address privacy-mode review feedback

- Startup masking (Codex P1): read the privacy preference in main() before
  runApp and seed PrivacyProvider with it, so the first frame already has
  the correct value — money is never briefly rendered unmasked for a user
  who enabled "Hide amounts". Provider stays fail-closed otherwise: starts
  masked, SureApp's no-arg default is masked, and a failed read keeps it
  masked. A late-completing initial load no longer clobbers an explicit
  user toggle.
- setHidden() reverts the in-memory state (and logs) if persistence fails,
  keeping the UI consistent with what's actually stored.
- Mask the cash-balance detail chip in AccountDetailHeader (was leaking
  the cash position in privacy mode).
- Privacy top-bar toggle gets a "Toggle privacy" tooltip + icon semantic
  label for accessibility (kept as an InkWell to match the adjacent
  settings control).
- Tests: assert fail-closed initial state; assert the exact masked count;
  test the persistence round-trip (set -> reload); add
  PreferencesService.resetForTest() and reset between tests so the cached
  singleton can't leak state.

125 tests pass; flutter analyze: no new issues.

* refactor(mobile): thread hideAmounts through calendar tiles

Per review: the calendar tile builders read PrivacyProvider via
context.read, relying implicitly on the parent build()'s context.watch to
rebuild them — fragile if a tile is later extracted or wrapped in a
RepaintBoundary. Pass hideAmounts down explicitly instead, matching the
recent_transactions_screen pattern:

- build() (context.watch) -> _buildCalendar -> _buildDayCell
- _showTransactionsDialog reads once when the modal opens ->
  _buildTransactionTile

No more context.read inside tile methods. 125 tests pass; analyze clean.

* fix(mobile): watch PrivacyProvider inside calendar dialog builder

Moving the hideAmounts read inside the showDialog builder and switching
from context.read to context.watch ensures the dialog re-masks transaction
amounts if the user toggles privacy mode while the dialog is open.
2026-06-30 06:49:05 +02:00
ghost
9ea8261919 feat(mobile): migrate all hardcoded UI strings to gen_l10n ARB (Phase 1 + 2) (#2384)
* feat(mobile): add Flutter i18n foundation (gen_l10n Phase 1)

- Wire flutter_localizations + gen_l10n (pubspec.yaml + l10n.yaml)
- Bump intl ^0.18.1 → ^0.20.2 for Flutter 3.32 pin
- Add lib/l10n/app_en.arb with 9 seed keys (appTitle, 3 common*, 4 chatSuggestion*)
- Generate AppLocalizations via flutter gen-l10n
- Replace MaterialApp title: with onGenerateTitle + localizationsDelegates + supportedLocales
- Migrate suggested_questions.dart from const list to BuildContext function
- Update chat_conversation_screen.dart call site (one-line change at pre-designed seam)

* feat(mobile): migrate all hardcoded UI strings to gen_l10n ARB (Phase 2)

Extends the i18n foundation from Phase 1 to cover every remaining
hardcoded user-facing string across the mobile app.

Files changed (21 source files + ARB/generated):
- lib/l10n/app_en.arb: 226 keys (+~80 new keys covering all screens)
- lib/screens/dashboard_screen.dart
- lib/screens/calendar_screen.dart
- lib/screens/transactions_list_screen.dart
- lib/screens/backend_config_screen.dart
- lib/screens/recent_transactions_screen.dart
- lib/screens/biometric_lock_screen.dart
- lib/screens/chat_conversation_screen.dart
- lib/screens/chat_list_screen.dart
- lib/screens/log_viewer_screen.dart
- lib/screens/login_screen.dart
- lib/screens/main_navigation_screen.dart
- lib/screens/more_screen.dart
- lib/screens/settings_screen.dart
- lib/screens/sso_onboarding_screen.dart
- lib/screens/transaction_edit_screen.dart
- lib/screens/transaction_form_screen.dart
- lib/widgets/account_detail_header.dart
- lib/widgets/category_filter.dart
- lib/widgets/connectivity_banner.dart
- lib/widgets/currency_filter.dart
- lib/widgets/custom_proxy_headers_editor.dart

Notable patterns:
- ICU plural for connectivity pending-sync count
- Parameterised keys for greeting (firstName), update dialog
  (version), cash chip (amount), delete confirmation (name),
  show-N menu (count), proxy-headers count, multi-delete count
- AppLocalizations captured before async gaps; dialog builders
  re-obtain from their own BuildContext
- const removed only from Text/InputDecoration/SnackBar/Row
  widgets that directly reference localisation getters; sibling
  const widgets preserved

116/116 unit tests pass; flutter analyze --no-fatal-infos: 0 errors.

* feat(mobile): migrate the remaining secondary UI strings to gen_l10n ARB

Completes the i18n migration by covering the lower-traffic strings the
earlier passes left behind — secondary dialogs, snackbars, settings
rows, validators, helper texts, and tooltips across 11 files.

- settings_screen.dart: reset/delete-account dialogs, clear-data and
  proxy-header snackbars, list-tile titles/subtitles, theme-segment
  tooltips, biometric prompt reason, debug-logs semantics label
- transaction_form_screen.dart: amount validators, session/success/
  failure/generic-error snackbars, More/Less toggle, date/name/category
  fields + helper texts, create button
- transaction_edit_screen.dart: session/update snackbars, validators,
  category/merchant/tag fallbacks, synced-only notice
- login_screen.dart: API-key dialog, demo/sign-up TextSpan, MFA info +
  validator, divider, server-URL heading, API-key + backend tooltip
- chat_list_screen.dart: multi-delete plural, result snackbars, error
  heading, relative-time fallback
- chat_conversation_screen.dart: edit-title dialog, refresh tooltip,
  load-error heading
- connectivity_banner.dart: sign-in / sync-success / sync-failure /
  auth-failure snackbars (also resolves the const removals cleanly)
- main_navigation_screen.dart: enable-AI dialog + failure snackbar
- log_viewer_screen.dart: clear-logs confirmation dialog
- biometric_lock_screen.dart, transactions_list_screen.dart: snackbar
  and edit tooltip

Adds 98 ARB keys (now 324 total), reusing common* and existing
screen keys where text matched. Interpolations use String/int
placeholders; multi-delete uses an ICU plural. AppLocalizations is
captured before async gaps and re-obtained inside dialog builders;
const removed only where a localisation getter is introduced, with
const restored on still-const siblings.

116/116 unit tests pass; flutter analyze --no-fatal-infos: 0 errors
(6 pre-existing infos unchanged). No remaining hardcoded user-facing
strings (verified by grep; only a doc-comment example remains).

* fix(mobile): address i18n review feedback and prune dead ARB keys

Resolves the still-valid CodeRabbit/Codex review findings on the i18n
migration; the majority were already handled by earlier commits.

- pubspec: switch intl to `any` so flutter_localizations selects the
  SDK-pinned version (0.19.x on <3.32, 0.20.x on 3.32+). Pinning
  ^0.20.2 conflicted with the declared `flutter: '>=3.27.0'` floor and
  would break `flutter pub get` on pre-3.32 SDKs.
- sso_onboarding: localize "Signed in as {email}" / "Google account
  verified" / the link- and create-form notes; fix the first/last-name
  validators that wrongly returned the "Email is required" message.
- backend_config: localize _testConnection / _saveAndContinue /
  _validateUrl messages and the headers helper text (validator now
  takes AppLocalizations; async methods capture it before awaits).
- recent_transactions: localize "Unknown Account"; locale-aware
  timestamp.
- account_detail_header: localize the unavailable-details message via a
  render-time flag (avoids touching inherited widgets in the
  initState-driven load path); treat empty/whitespace securityName as
  missing for the holding-name fallback.
- dashboard: wrap syncing/refreshing snackbar text in Expanded to avoid
  overflow with long translations; use the distinct dashboardSyncError
  key on the exception path.
- chat_list: localize relative-time labels (plural keys) and use a
  locale-aware fallback date; calendar: locale-aware displayed dates and
  weekday letters (map-key formats left fixed; Sunday-anchored grid
  preserved).
- connectivity_banner: capture ScaffoldMessenger before the async gap
  (removes use_build_context_synchronously); log_viewer: const Duration.
- Prune 55 unused ARB keys (never-wired scaffolding + superseded
  duplicates). ARB now 298 keys, 0 unused.

116/116 tests pass; flutter analyze --no-fatal-infos: 0 errors, only 3
pre-existing infos in the untouched intro_screen_web.dart.

* fix(mobile): localize the log viewer empty-state string

The 'No logs yet' empty state was the last hardcoded user-facing string
(the logViewerEmpty key had been pruned before this string was migrated).
Re-add the key and route the empty state through AppLocalizations.

Closes the final CodeRabbit i18n-coverage thread. 0 analyze errors,
116 tests pass, ARB at 299 keys with 0 unused.

* fix(mobile): address i18n review feedback

- sso_onboarding: the "Accept Invitation" tab and submit buttons showed
  the Terms-of-Service string (ssoOnboardingAcceptTerms) when a household
  invitation was pending. Add ssoOnboardingAcceptInvitation and use it in
  both spots; drop the now-unused ToS key (no ToS checkbox exists).
- transaction_form: restore the required-field indicator on the amount
  label ("Amount *") that the migration dropped.
- settings: localize the "a newer version" fallback used in the update
  dialog when the store version is unknown.
- ARB plurals: use the CLDR `one{}` category instead of the exact-match
  `=1{}` so future non-English locales pluralize correctly. English output
  is unchanged.

ARB at 300 keys, 0 unused. 116 tests pass; flutter analyze: no new issues.

* fix(mobile): reconcile i18n with design-system primitives after merge

The merge of main into this branch pulled in the design-system primitive
migrations (SureCard/SureChip/SureSegmentedControl/SureTextField/
SureListGroup), which collided with the i18n string migration and left
several files broken (compile errors / un-localized text).

Re-apply the i18n migration on top of the design-system versions:
- more_screen: SureListGroup/SureListRow rows now use l.more* keys
  (was a mangled mix of _buildMenuItem + SureListGroup).
- transaction_form_screen: SureSegmentedControl segments + amount/date/
  name/category labels, helpers, validators, and snackbars localized;
  add transactionFormAmountHelper ("Required") for the amount helper text.
- custom_proxy_headers_editor: SureTextField label/hint params localized.
- currency_filter: the "All" chip is a SureChip again (the merge had left
  it as a Material FilterChip) and uses l.commonAll.
- currency_filter_test / custom_proxy_headers_editor_test: add the
  localization delegates the widgets now require.

ARB at 301 keys, 0 unused. 165 tests pass; flutter analyze: 0 errors.

---------

Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
2026-06-21 11:53:12 +02:00
Tristan the Katana
ee89aec850 Add Mobile version check for update notifications (#2367)
* feat(mobile): in-app update prompt via upgrader package

- Add upgrader ^13.5.0 dependency for automatic update checks
- Show UpgradeAlert after authentication, with a 5-minute delay so
  users can settle in before being prompted
- Dialog shows at most once every 7 days per store version
- Only "Later" and "Update now" buttons — ignore button removed via showIgnore: false
- Custom _SureUpgraderMessages with mustache body template (v13 API)
- Add "Check for updates" tile in Settings for manual checks:
  calls updateVersionInfo() and shows an AlertDialog with store URL
  if an update is available, or a snackbar if already up to date
- Timer resets on logout so the delay restarts on next sign-in

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(mobile): address upgrader review comments

- Mount UpgradeAlert unconditionally so MainNavigationScreen is never
  remounted when the timer fires, preserving navigation state
- Dispose _manualUpgrader in SettingsScreen to remove its lifecycle
  observer when the screen is torn down
- Show update dialog even when store version metadata is incomplete
  (falls back to 'a newer version'); disable Update now button rather
  than silently no-oping when store URL is unavailable

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(mobile): show snackbar when store URL cannot be opened

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(mobile): dispose manual upgrader if unmounted after initialize

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 16:49:52 +02:00
ghost
32c3b92d22 feat(mobile): add privacy-safe Sentry instrumentation (#2201)
* feat(mobile): add privacy-safe Sentry instrumentation

* fix(mobile): harden auth telemetry handling

* fix(mobile): harden Sentry privacy filters

* fix(mobile): make auth session persistence atomic
2026-06-08 21:54:46 +02:00
ghost
f7be206c55 fix(mobile): redact sensitive diagnostic logs (#2199)
* fix(mobile): redact sensitive diagnostic logs

* fix(mobile): tighten diagnostic log redaction

* fix(mobile): harden sanitized diagnostics

* fix(mobile): clarify offline fallback diagnostics

* fix(mobile): refine diagnostic log redaction

* fix(mobile): tighten diagnostic log redaction

* fix(mobile): harden auth diagnostic failures
2026-06-06 16:47:30 +02:00
ghost
5372a08788 feat(mobile): add transaction metadata editing (#2131)
* feat(mobile): add transaction metadata editing

* fix(mobile): preserve explicit metadata clears

* fix(mobile): derive persisted tag metadata state

* fix(mobile): avoid logging transaction details

* fix(mobile): harden transaction edit sync

Pass the edited transaction context through provider updates, refresh or fall back after empty update responses, surface field-level API errors, and avoid forced metadata refetches on every edit screen open.

* fix(mobile): keep transaction edit selects ci-compatible

Use the DropdownButtonFormField API supported by the Flutter version pinned in upstream mobile CI.
2026-06-04 23:02:57 +02:00
Michal Tajchert
96c893ec18 Mobile: custom proxy headers + small login UX fixes (#1748)
* Add mobile custom proxy headers

* Clear login placeholders on focus

Email/password fields ship with example values pre-filled. Tapping the
field now clears the placeholder so users don't have to delete it
manually. Skips clearing if the user has already edited the value.

* Push Configuration as a route from Sign in

Opening Configuration from the Sign in screen now uses Navigator.push
instead of toggling a state flag, so Android back returns to Sign in
instead of quitting the app. Saving the URL auto-pops the route.

* Address PR review on custom proxy headers

- Test Connection no longer leaves global ApiConfig headers mutated;
  unsaved edits are restored in a finally block after the probe.
- _loadSavedUrl / _loadCustomHeaders wrap storage reads in try/catch and
  always finish initialization with sensible defaults.
- Sanitization is now a single CustomProxyHeader.sanitize() reused by
  ApiConfig.setCustomProxyHeaders and CustomProxyHeadersService.
- Brief comment on redactedValue explaining the length-obscuring design.

* Harden custom proxy header validation and load path

- validateValue now rejects ASCII control characters (CR/LF/tab/etc.)
  to prevent header-injection via crafted values.
- loadHeaders moves the secure-storage read inside the try block so
  platform exceptions are caught the same way JSON parse errors are.
2026-05-11 23:09:21 +02:00
Tristan Katana
9c199a6dcd feat(mobile): Add biometric lock for app resume (#1474)
* Feature: Biometric lock for app resume

User enables "Biometric Lock" in Settings → prompted to verify fingerprint/face first.
User backgrounds the app → _isLocked = true.
User returns → lock screen appears, auto-triggers biometric prompt.
Success → app unlocks, state preserved underneath.
Can retry or log out as fallback.
Add USE_BIOMETRIC permission to AndroidManifest.

* Fix: Remove duplicate local_auth entry in pubspec.lock and add NSFaceIDUsageDescription to iOS Info.plist

* fix(mobile) : Remove duplicate local auth files first

* fix(mobile): keep MainNavigationScreen in the Stack, let the lock screen float above, no unmounting

* Updtae: Swap out Flutter Activity for FlutterFragmentActivity that extends the lock scan feature

* fix(mobile): address biometric lock PR review feedback

* fix(mobile): only require biometric auth when enabling lock, not disabling

Prevents users from getting locked out if biometrics start failing —
they can now disable the lock without needing to pass biometric auth.

* fix(mobile): add missing closing brace in setBiometricEnabled

---------

Signed-off-by: Tristan Katana <50181095+felixmuinde@users.noreply.github.com>
2026-04-15 19:48:13 +02:00
Lazy Bone
fdc2ce1feb Add category support to transactions (#1251)
* Move debug logs button from Home to Settings page, remove refresh/logout from Home AppBar

- Remove Debug Logs, Refresh, and Sign Out buttons from DashboardScreen AppBar
- Add Debug Logs ListTile entry in SettingsScreen under app info section
- Remove unused _handleLogout method from DashboardScreen
- Remove unused log_viewer_screen.dart import from DashboardScreen

https://claude.ai/code/session_017XQZdaEwUuRS75tJMcHzB9

* Add category picker to Android transaction form

Implements category selection when creating transactions in the mobile app.
Uses the existing /api/v1/categories endpoint to fetch categories and sends
category_id when creating transactions via the API.

New files:
- Category model, CategoriesService, CategoriesProvider
Updated:
- Transaction/OfflineTransaction models with categoryId/categoryName
- TransactionsService/Provider to pass category_id
- DB schema v2 migration for category columns
- TransactionFormScreen with category dropdown in "More" section

Closes #78

https://claude.ai/code/session_01Dgj8tYrCkoUaLW2WrQ3vMJ

* Fix ambiguous Category import in CategoriesProvider

Hide Flutter's built-in Category annotation from foundation.dart
to resolve name collision with our Category model.

https://claude.ai/code/session_01Dgj8tYrCkoUaLW2WrQ3vMJ

* Add category filter on Dashboard, clear categories on data reset, fix ambiguous imports

- Add CategoryFilter widget (horizontal chip row like CurrencyFilter)
- Show category filter on Dashboard below currency filter (2nd row)
- Add "Show Category Filter" toggle in Settings > Display section
- Clear CategoriesProvider on "Clear Local Data" and "Reset Account"
- Fix Category name collision: hide Flutter's Category from material.dart
- Add getShowCategoryFilter/setShowCategoryFilter to PreferencesService

https://claude.ai/code/session_01Dgj8tYrCkoUaLW2WrQ3vMJ

* Fix Category name collision using prefixed imports

Use 'import as models' instead of 'hide Category' to avoid
undefined_hidden_name warnings with flutter/material.dart.

https://claude.ai/code/session_01Dgj8tYrCkoUaLW2WrQ3vMJ

* Fix duplicate column error in SQLite migration

Check if category_id/category_name columns exist before running
ALTER TABLE, preventing crashes when the DB was already at v2
or the migration had partially succeeded.

https://claude.ai/code/session_01Dgj8tYrCkoUaLW2WrQ3vMJ

* Move CategoryFilter from dashboard to transaction list screen

CategoryFilter was filtering accounts on the dashboard but accounts
are already grouped by type. Moved it to TransactionsListScreen where
it filters transactions by category, which is the correct placement.

https://claude.ai/code/session_01Dgj8tYrCkoUaLW2WrQ3vMJ

* Add category tag badge next to transaction name

Shows an oval-bordered category label after each transaction's
name for quick visual identification of transaction types.

https://claude.ai/code/session_01Dgj8tYrCkoUaLW2WrQ3vMJ

* Address review findings for category feature

1. Category.fromJson now recursively parses parent chain;
   displayName walks all ancestors (e.g. "Grandparent > Parent > Child")
2. CategoriesProvider.fetchCategories guards against concurrent/duplicate
   calls by checking _isLoading and _hasFetched early
3. CategoryFilter chips use displayName to distinguish subcategories
4. Transaction badge resolves full displayName from CategoriesProvider
   with overflow ellipsis for long paths
5. Offline storage preserves local category values when server response
   omits them (coalesce with ??)

https://claude.ai/code/session_01Dgj8tYrCkoUaLW2WrQ3vMJ

* Fix missing closing brace in PreferencesService causing theme_provider analyze errors

https://claude.ai/code/session_01Dgj8tYrCkoUaLW2WrQ3vMJ

* Fix sync category upload, empty-state refresh, badge reactivity, and preferences syntax

- Add categoryId to SyncService pending transaction upload payload
- Replace non-scrollable Center with ListView for empty filter state so
  RefreshIndicator works when no transactions match
- Use listen:true for CategoriesProvider in badge display so badges
  rebuild when categories finish loading
- Fix missing closing brace in PreferencesService.setShowCategoryFilter

https://claude.ai/code/session_01Dgj8tYrCkoUaLW2WrQ3vMJ

---------

Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
2026-04-13 20:01:08 +02:00
Tristan Katana
b1fd8bbc99 Mobile: Add theme selection (light/dark/system) to settings (#1213)
* Feature: Add Theme selection in Settings page

* Fix: Theme provider exception handling.

* feat(mobile): Show theme selection option in settings screen.

* BuildID version 9

---------

Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
2026-03-26 22:47:31 +01:00
Lazy Bone
ac1b2e621e Move debug logs button from Home to Settings page, remove refresh/logout from Home AppBar (#1146)
- Remove Debug Logs, Refresh, and Sign Out buttons from DashboardScreen AppBar
- Add Debug Logs ListTile entry in SettingsScreen under app info section
- Remove unused _handleLogout method from DashboardScreen
- Remove unused log_viewer_screen.dart import from DashboardScreen

https://claude.ai/code/session_017XQZdaEwUuRS75tJMcHzB9

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-22 16:08:35 +01:00
Juan José Mata
1f9a934c59 Add build ID to Flutter app version display 2026-02-23 14:22:54 +00:00
Juan José Mata
ad3087f1dd Improvements to Flutter client (#1042)
* Chat improvements

* Delete/reset account via API for Flutter app

* Fix tests.

* Add "contact us" to settings

* Update mobile/lib/screens/chat_conversation_screen.dart

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Juan José Mata <jjmata@jjmata.com>

* Improve LLM special token detection

* Deactivated user shouldn't have API working

* Fix tests

* API-Key usage

* Flutter app launch failure on no network

* Handle deletion/reset delays

* Local cached data may become stale

* Use X-Api-Key correctly!

---------

Signed-off-by: Juan José Mata <jjmata@jjmata.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-02-22 21:22:32 -05:00
Juan José Mata
da754b8d05 Version number to bundle files 2026-02-18 00:23:02 +01:00
Juan José Mata
1995c62ddf Flutter title and icon alignment fixes 2026-02-16 04:37:18 +01:00
Juan José Mata
eb0d05a7fb Intro mode in Flutter client fixes 2026-02-16 02:17:03 +01:00
Lazy Bone
81cf473862 fix: Use getValidAccessToken() in connectivity banner sync button (#851)
* feat(mobile): Add transaction display on calendar date tap

Implement two-tap interaction for calendar dates:
- First tap selects a date (highlighted with thicker primary color border)
- Second tap on same date shows AlertDialog with transactions for that day

Each transaction displays with:
- Color-coded icon (red minus for expenses, green plus for income)
- Transaction name as title
- Notes as subtitle (if present)
- Amount with color matching expense/income

Selection is cleared when changing account, account type, or month.

https://claude.ai/code/session_019m7ZrCakU6h9xLwD1NTx9i

* feat(mobile): optimize asset/liability display with filters

- Add NetWorthCard widget with placeholder for future net worth API
- Add side-by-side Assets/Liabilities display with tap-to-filter
- Implement CurrencyFilter widget for multi-select currency filtering
- Replace old _SummaryCard with new unified design
- Remove _CollapsibleSectionHeader in favor of filter-based navigation

The net worth section shows a placeholder as the API endpoint is not yet available.
Users can now filter accounts by type (assets/liabilities) and by currency.

https://claude.ai/code/session_01W8cQSCzmgTmTqwRJ8Ycpx3

* fix(mobile): remove unused variables and add const

- Remove unused _totalAssets, _totalLiabilities, _getPrimaryCurrency
- Add const to Text('All') widget

https://claude.ai/code/session_01W8cQSCzmgTmTqwRJ8Ycpx3

* feat(mobile): enhance dashboard with icons, long-press breakdown, and grouped view

- NetWorthCard: replace text labels with trending icons, add colored
  bottom borders for asset (green) and liability (red) sections
- Add long-press gesture on asset/liability areas to show full currency
  breakdown in a bottom sheet popup
- Add collapsible account type grouping (Crypto, Bank, Investment, etc.)
  with type-specific icons and expand/collapse headers
- Add PreferencesService for persisting display settings
- Add "Group by Account Type" toggle in Settings screen
- Wire settings change to dashboard via GlobalKey for live updates

https://claude.ai/code/session_01W8cQSCzmgTmTqwRJ8Ycpx3

* refactor(mobile): remove welcome header from dashboard

Strip the Welcome greeting and subtitle to let the financial
overview take immediate focus.

https://claude.ai/code/session_01W8cQSCzmgTmTqwRJ8Ycpx3

* feat(mobile): compact filter buttons with scroll-wheel currency switcher

- Remove trending icons from asset/liability filter buttons
- Increase amount font size to titleMedium bold
- Reduce Net Worth section and filter button padding
- Show single currency at a time with ListWheelScrollView for
  scrolling between currencies (wheel-picker style)
- Absorb scroll events via NotificationListener to prevent
  triggering pull-to-refresh
- Keep icons in the long-press currency breakdown popup

https://claude.ai/code/session_01W8cQSCzmgTmTqwRJ8Ycpx3

* feat: Add API key login option to mobile app

Add a "Via API Key Login" button on the login screen that opens a
dialog for entering an API key. The API key is validated by making a
test request to /api/v1/accounts with the X-Api-Key header, and on
success is persisted in secure storage. All HTTP services now use a
centralized ApiConfig.getAuthHeaders() helper that returns the correct
auth header (X-Api-Key or Bearer) based on the current auth mode.

https://claude.ai/code/session_01DnyCzdMjVpSsbBZK3XbzUH

* fix: Improve API key dialog context handling and controller disposal

- Use outer context for SnackBar so it displays on the main screen
  instead of behind the dialog
- Explicitly dispose TextEditingController to prevent memory leaks
- Close dialog on failure before showing error SnackBar for better UX
- Avoid StatefulBuilder context parameter shadowing

https://claude.ai/code/session_01DnyCzdMjVpSsbBZK3XbzUH

* fix: Use user-friendly error message in API key login catch block

Log the technical exception details via LogService.instance.error and
show a generic "Unable to connect" message to the user instead of
exposing the raw exception string.

https://claude.ai/code/session_01DnyCzdMjVpSsbBZK3XbzUH

* fix: Use getValidAccessToken() in connectivity banner sync button

Replace direct authProvider.tokens?.accessToken access with
getValidAccessToken() so the Sync Now button works in API-key
auth mode where _tokens is null.

https://claude.ai/code/session_01DnyCzdMjVpSsbBZK3XbzUH

* Revert "fix: Use getValidAccessToken() in connectivity banner sync button"

This reverts commit 7015c160f0.

* Reapply "fix: Use getValidAccessToken() in connectivity banner sync button"

This reverts commit b29e010de3.

* fix: Use getValidAccessToken() in connectivity banner sync button

Replace direct authProvider.tokens?.accessToken access with
getValidAccessToken() so the Sync Now button works in API-key
auth mode where _tokens is null.

https://claude.ai/code/session_01DnyCzdMjVpSsbBZK3XbzUH

* fix(mobile): prevent bottom sheet overflow with ConstrainedBox

Use ConstrainedBox + ListView.separated with shrinkWrap for the
currency breakdown popup. Few currencies: sheet sizes to content.
Many currencies: caps at 50% screen height and scrolls.

Also add isScrollControlled and useSafeArea to showModalBottomSheet.

https://claude.ai/code/session_01W8cQSCzmgTmTqwRJ8Ycpx3

* fix: Prevent multiple syncs and handle auth errors in connectivity banner

Set _isSyncing immediately on tap to disable the button during token
refresh, wrap getValidAccessToken() in try/catch with user-facing error
snackbar, and await _handleSync so errors propagate correctly.

https://claude.ai/code/session_01GgVgjqwyXhWMZN3eWfaMCk

---------

Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-01 23:16:02 +01:00
Lazy Bone
62dabb6971 Fix: Transaction Sync Issues & Enhanced Debugging (#632)
* Fix mobile app to fetch all transactions with pagination

The mobile app was only fetching 25 transactions per account because:
1. TransactionsService didn't pass pagination parameters to the API
2. The backend defaults to 25 records per page when no per_page is specified
3. SyncService didn't implement pagination to fetch all pages

Changes:
- Updated TransactionsService.getTransactions() to accept page and perPage parameters
- Modified the method to extract and return pagination metadata from API response
- Updated SyncService.syncFromServer() to fetch all pages (up to 100 per page)
- Added pagination loop to continue fetching until all pages are retrieved
- Enhanced logging to show pagination progress

This ensures users see all their transactions in the mobile app, not just the first 25.

* Add clear local data feature and enhanced sync logging

Added features:
1. Clear Local Data button in Settings
   - Allows users to clear all cached transactions and accounts
   - Shows confirmation dialog before clearing
   - Displays success/error feedback

2. Enhanced sync logging for debugging
   - Added detailed logs in syncFromServer to track pagination
   - Shows page-by-page progress with transaction counts
   - Logs pagination metadata (total pages, total count, etc.)
   - Tracks upsert progress every 50 transactions
   - Added clear section markers for easier log reading

3. Simplified upsertTransactionFromServer logging
   - Removed verbose debug logs to reduce noise
   - Keeps only essential error/warning logs

This will help users troubleshoot sync issues by:
- Clearing stale data and forcing a fresh sync
- Providing detailed logs to identify where sync might fail

* Fix transaction accountId parsing from API response

The mobile app was only showing 25 transactions per account because:
- The backend API returns account info in nested format: {"account": {"id": "xxx"}}
- The mobile Transaction model expected flat format: {"account_id": "xxx"}
- When parsing, accountId was always empty, so database queries by account_id returned incomplete results

Changes:
1. Updated Transaction.fromJson to handle both formats:
   - New format: {"account": {"id": "xxx", "name": "..."}}
   - Old format: {"account_id": "xxx"} (for backward compatibility)

2. Fixed classification/nature field parsing:
   - Backend sends "classification" field (income/expense)
   - Mobile uses "nature" field
   - Now handles both fields correctly

3. Added debug logging to identify empty accountId issues:
   - Logs first transaction's accountId when syncing
   - Counts and warns about transactions with empty accountId
   - Shows critical errors when trying to save with empty accountId

This ensures all transactions from the server are correctly associated with their accounts in the local database.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-13 09:27:39 +01:00
Lazy Bone
f52b3fceb6 feat: implement mobile AI chat feature and fix duplicate response issue (#610)
Backend fixes:
- Fix duplicate AssistantResponseJob triggering causing duplicate AI responses
- UserMessage model already handles job triggering via after_create_commit callback
- Remove redundant job enqueue in chats_controller and messages_controller

Mobile app features:
- Implement complete AI chat interface and conversation management
- Add Chat, Message, and ToolCall data models
- Add ChatProvider for state management with polling mechanism
- Add ChatService to handle all chat-related API requests
- Add chat list screen (ChatListScreen)
- Add conversation detail screen (ChatConversationScreen)
- Refactor navigation structure with bottom navigation bar (MainNavigationScreen)
- Add settings screen (SettingsScreen)
- Optimize TransactionsProvider to support account filtering

Technical details:
- Implement message polling mechanism for real-time AI responses
- Support chat creation, deletion, retry and other operations
- Integrate Material Design 3 design language
- Improve user experience and error handling

Co-authored-by: dwvwdv <dwvwdv@protonmail.com>
2026-01-11 12:45:33 +01:00