mirror of
https://github.com/we-promise/sure.git
synced 2026-04-07 14:31:25 +00:00
98ae6782dccab5f89dda569b7bd2d22cfc5893ee
24 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c09362b880 |
Check for pending invitations before creating new Family during SSO log in/sign up (#1171)
* Check for pending invitations before creating new Family during SSO account creation When a user signs in via Google SSO and doesn't have an account yet, the system now checks for pending invitations before creating a new Family. If an invitation exists, the user joins the invited family instead. - OidcAccountsController: check Invitation.pending in link/create_user - API AuthController: check pending invitations in sso_create_account - SessionsController: pass has_pending_invitation to mobile SSO callback - Web view: show "Accept Invitation" button when invitation exists - Flutter: show "Accept Invitation" tab/button when invitation pending https://claude.ai/code/session_019Tr6edJa496V1ErGmsbqFU * Fix external assistant tests: clear Settings cache to prevent test pollution The tests relied solely on with_env_overrides to clear configuration, but rails-settings-cached may retain stale Setting values across tests when the cache isn't explicitly invalidated. Ensure both ENV vars AND Setting values are cleared with Setting.clear_cache before assertions. https://claude.ai/code/session_019Tr6edJa496V1ErGmsbqFU --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
f6e7234ead |
Enable Google SSO account creation in Flutter app (#1164)
* Add Google SSO onboarding flow for Flutter mobile app Previously, mobile users attempting Google SSO without a linked OIDC identity received an error telling them to link from the web app first. This adds the same account linking/creation flow that exists on the PWA. Backend changes: - sessions_controller: Cache pending OIDC auth with a linking code and redirect back to the app instead of returning an error - api/v1/auth_controller: Add sso_link endpoint to link Google identity to an existing account via email/password, and sso_create_account endpoint to create a new SSO-only account (respects JIT config) - routes: Add POST auth/sso_link and auth/sso_create_account Flutter changes: - auth_service: Detect account_not_linked callback status, add ssoLink and ssoCreateAccount API methods - auth_provider: Track SSO onboarding state, expose linking/creation methods and cancelSsoOnboarding - sso_onboarding_screen: New screen with tabs to link existing account or create new account, pre-filled with Google profile data - main.dart: Show SsoOnboardingScreen when ssoOnboardingPending is true https://claude.ai/code/session_011ag1qSfriUg6j7TqFgbS5c * Fix broken SSO tests: use MemoryStore cache and correct redirect param - Sessions test: check `status` param instead of `error` since handle_mobile_sso_onboarding sends linking info with status key - API auth tests: swap null_store for MemoryStore so cache-based linking code validation works in test environment https://claude.ai/code/session_011ag1qSfriUg6j7TqFgbS5c * Delay linking-code consumption until SSO link/create succeeds Split validate_and_consume_linking_code into validate_linking_code (read-only) and consume_linking_code! (delete). The code is now only consumed after password verification (sso_link) or successful user save (sso_create_account), so recoverable errors no longer burn the one-time code and force a full Google SSO roundtrip. https://claude.ai/code/session_011ag1qSfriUg6j7TqFgbS5c * Make linking-code consumption atomic to prevent race conditions Move consume_linking_code! (backed by Rails.cache.delete) to after recoverable checks (bad password, policy rejection) but before side-effecting operations (identity/user creation). Only the first caller to delete the cache key gets true, so concurrent requests with the same code cannot both succeed. - sso_link: consume after password auth, before OidcIdentity creation - sso_create_account: consume after allow_account_creation check, before User creation - Bad password still preserves the code for retry - Add single-use regression tests for both endpoints https://claude.ai/code/session_011ag1qSfriUg6j7TqFgbS5c * Add missing sso_create_account test coverage for blank code and validation failure - Test blank linking_code returns 400 (bad_request) with proper error - Test duplicate email triggers user.save failure → 422 with validation errors https://claude.ai/code/session_011ag1qSfriUg6j7TqFgbS5c * Verify cache payload in mobile SSO onboarding test with MemoryStore The test environment uses :null_store which silently discards cache writes, so handle_mobile_sso_onboarding's Rails.cache.write was never verified. Swap in a MemoryStore for this test and assert the full cached payload (provider, uid, email, name, device_info, allow_account_creation) at the linking_code key from the redirect URL. https://claude.ai/code/session_011ag1qSfriUg6j7TqFgbS5c * Add rswag/OpenAPI specs for sso_link and sso_create_account endpoints POST /api/v1/auth/sso_link: documents linking_code + email/password params, 200 (tokens), 400 (missing code), 401 (invalid creds/expired). POST /api/v1/auth/sso_create_account: documents linking_code + optional first_name/last_name params, 200 (tokens), 400 (missing code), 401 (expired code), 403 (creation disabled), 422 (validation errors). Note: RAILS_ENV=test bundle exec rake rswag:specs:swaggerize should be run to regenerate docs/api/openapi.yaml once the runtime environment matches the Gemfile Ruby version. https://claude.ai/code/session_011ag1qSfriUg6j7TqFgbS5c * Preserve OIDC issuer through mobile SSO onboarding flow handle_mobile_sso_onboarding now caches the issuer from auth.extra.raw_info.iss so it survives the linking-code round trip. build_omniauth_hash populates extra.raw_info.iss from the cached issuer so OidcIdentity.create_from_omniauth stores it correctly. Previously the issuer was always nil for mobile SSO-created identities because build_omniauth_hash passed an empty raw_info OpenStruct. https://claude.ai/code/session_011ag1qSfriUg6j7TqFgbS5c * Block MFA users from bypassing second factor via sso_link sso_link authenticated with email/password but never checked user.otp_required?, allowing MFA users to obtain tokens without a second factor. The mobile SSO callback already rejects MFA users with "mfa_not_supported"; apply the same guard in sso_link before consuming the linking code or creating an identity. Returns 401 with mfa_required: true, consistent with the login action's MFA response shape. https://claude.ai/code/session_011ag1qSfriUg6j7TqFgbS5c * Fix NoMethodError in SSO link MFA test Replace non-existent User.generate_otp_secret class method with ROTP::Base32.random(32), matching the pattern used in User#setup_mfa!. https://claude.ai/code/session_011ag1qSfriUg6j7TqFgbS5c * Assert linking code survives rejected SSO create account Add cache persistence assertion to "should reject SSO create account when not allowed" test, verifying the linking code is not consumed on the 403 path. This mirrors the pattern used in the invalid-password sso_link test. The other rejection tests (expired/missing linking code) don't have a valid cached code to check, so no assertion is needed there. https://claude.ai/code/session_011ag1qSfriUg6j7TqFgbS5c --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
1f9a934c59 | Add build ID to Flutter app version display | ||
|
|
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> |
||
|
|
a16e1f8482 | Default tap on URL opens settings | ||
|
|
2c50bd1d9a | Add demo account to Flutter client also | ||
|
|
42724335ab | Default login for Flutter client beta | ||
|
|
da754b8d05 | Version number to bundle files | ||
|
|
1995c62ddf | Flutter title and icon alignment fixes | ||
|
|
2dcb4b4f67 | Small Flutter UI tweaks | ||
|
|
eb0d05a7fb | Intro mode in Flutter client fixes | ||
|
|
bf0be85859 |
Expose ui_layout and ai_enabled to mobile clients and add enable_ai endpoint (#983)
* Wire ui layout and AI flags into mobile auth Include ui_layout and ai_enabled in mobile login/signup/SSO payloads, add an authenticated endpoint to enable AI from Flutter, and gate mobile navigation based on intro layout and AI consent flow. * Linter * Ensure write scope on enable_ai * Make sure AI is available before enabling it * Test improvements * PR comment * Fix review issues: test assertion bug, missing coverage, and Dart defaults (#985) - Fix login test to use ai_enabled? (method) instead of ai_enabled (column) to match what mobile_user_payload actually serializes - Add test for enable_ai when ai_available? returns false (403 path) - Default aiEnabled to false when user is null in AuthProvider to avoid showing AI as available before authentication completes - Remove extra blank lines in auth_provider.dart and auth_service.dart https://claude.ai/code/session_01LEYYmtsDBoqizyihFtkye4 Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
32b01165c9 |
Flutter login polish (#973)
* Skip config screen by setting demo site default * Small copy edits * Login page polish |
||
|
|
ca3abd5d8b |
Add Google Sign-In (SSO) support to Flutter mobile app (#860)
* Add mobile SSO support to sessions controller
Add /auth/mobile/:provider route and mobile_sso_start action that
captures device params in session and renders an auto-submitting POST
form to OmniAuth (required by omniauth-rails_csrf_protection).
Modify openid_connect callback to detect mobile_sso session, issue
Doorkeeper tokens via MobileDevice, and redirect to sureapp://oauth/callback
with tokens. Handles MFA users and unlinked accounts with error redirects.
Validates provider name against configured SSO providers and device info
before proceeding.
* Add SSO auth flow to Flutter service and provider
Add buildSsoUrl() and handleSsoCallback() to AuthService for
constructing the mobile SSO URL and parsing tokens from the deep
link callback.
Add startSsoLogin() and handleSsoCallback() to AuthProvider for
launching browser-based SSO and processing the redirect.
* Register deep link listener for SSO callback
Listen for sureapp://oauth/* deep links via app_links package,
handling both cold start (getInitialLink) and warm (uriLinkStream)
scenarios. Routes callbacks to AuthProvider.handleSsoCallback().
* Add Google Sign-In button to Flutter login screen
Add "or" divider and outlined Google Sign-In button that triggers
browser-based SSO via startSsoLogin('google_oauth2').
Add app_links and url_launcher dependencies to pubspec.yaml.
* Fix mobile SSO failure handling to redirect back to app
When OmniAuth fails during mobile SSO flow, redirect to
sureapp://oauth/callback with the error instead of the web login page.
Cleans up mobile_sso session data on failure.
* Address PR review feedback for mobile SSO flow
- Use strong params for device info in mobile_sso_start
- Guard against nil session data in handle_mobile_sso_callback
- Add error handling for AppLinks initialization and stream
- Handle launchUrl false return value in SSO login
- Use user-friendly error messages instead of exposing exceptions
- Reject empty token strings in SSO callback validation
* Consolidate mobile device token logic into MobileDevice model
Extract duplicated device upsert and token issuance code from
AuthController and SessionsController into MobileDevice. Add
CALLBACK_URL constant and URL builder helpers to eliminate repeated
deep-link strings. Add mobile SSO integration tests covering the
full flow, MFA rejection, unlinked accounts, and failure handling.
* Fix CI: resolve Brakeman redirect warnings and rubocop empty line
Move mobile SSO redirect into a private controller method with an
inline string literal so Brakeman can statically verify the target.
Remove unused URL builder helpers from MobileDevice. Fix extra empty
line at end of AuthController class body.
* Use authorization code exchange for mobile SSO and add signup error handling
Replace passing plaintext tokens in mobile SSO redirect URLs with a
one-time authorization code pattern. Tokens are now stored server-side
in Rails.cache (5min TTL) and exchanged via a secure POST to
/api/v1/auth/sso_exchange. Also wraps device/token creation in the
signup action with error handling and sanitizes device error messages.
* Add error handling for login device registration and blank SSO code guard
* Address PR #860 review: fix SSO race condition, add OpenAPI spec, and cleanup
- Fix race condition in sso_exchange by checking Rails.cache.delete return
value to ensure only one request can consume an authorization code
- Use strong parameters (params.require) for sso_exchange code param
- Move inline HTML from mobile_sso_start to a proper view template
- Clear stale session[:mobile_sso] flag on web login paths to prevent
abandoned mobile flows from hijacking subsequent web SSO logins
- Add OpenAPI/rswag spec for all auth API endpoints
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix mobile SSO test to match authorization code exchange pattern
The test was asserting tokens directly in the callback URL, but the code
uses an authorization code exchange pattern. Updated to exchange the code
via the sso_exchange API endpoint. Also swaps in a MemoryStore for this
test since the test environment uses null_store which discards writes.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Refactor mobile OAuth to use single shared application
Replace per-device Doorkeeper::Application creation with a shared
"Sure Mobile" OAuth app. Device tracking uses mobile_device_id on
access tokens instead of oauth_application_id on mobile_devices.
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
|
||
|
|
4be1c39e1f |
Fix Flutter iOS build and add web support with web-safe storage (#878)
* Add Flutter web support and web-safe storage * Update mobile/web/index.html Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Juan José Mata <jjmata@jjmata.com> * Product name instead of placeholder --------- Signed-off-by: Juan José Mata <jjmata@jjmata.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> |
||
|
|
2ac3f3dff0 |
Add account filtering and net worth card to dashboard (#818)
* 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 * 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(mobile): reload dashboard preferences on any tab switch to Home Previously only reloaded when navigating directly from Settings to Home. Now reloads whenever the Home tab is selected, covering paths like Settings -> More -> Home. https://claude.ai/code/session_01W8cQSCzmgTmTqwRJ8Ycpx3 * chore(mobile): simplify net worth placeholder to single line Replace the two-line Net Worth / -- placeholder with a compact "Net Worth — coming soon" label while the API endpoint is pending. https://claude.ai/code/session_01W8cQSCzmgTmTqwRJ8Ycpx3 --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
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 |
||
|
|
77269fa60a |
feat(mobile): Add transaction display on calendar date tap (#817)
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 Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
38938fe971 |
Add API key authentication support to mobile app (#850)
* 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 --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
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>
|
||
|
|
19da11ed4e |
This commit adds two new features to the More tab in the mobile app: (#612)
Calendar View: Monthly calendar displaying daily balance changes for selected account Account selection dropdown to switch between different accounts Month navigation with previous/next buttons Visual indicators (green for income, red for expenses) Monthly profit/loss summary at the top Auto-calculation of daily transaction totals Recent Transactions: View recent N transactions across all accounts Configurable display limit (10/20/50/100 transactions) Pull-to-refresh functionality Transaction details including account name, date, amount, and notes Visual distinction between income and expenses Sorted by date (most recent first) Implementation details: Created MoreScreen as menu hub for new features Replaced PlaceholderScreen with functional MoreScreen Leverages existing Provider pattern for state management Uses offline-first approach with existing data providers Full Chinese localization Material Design 3 compliant UI Files added: mobile/lib/screens/more_screen.dart mobile/lib/screens/calendar_screen.dart mobile/lib/screens/recent_transactions_screen.dart Files modified: mobile/lib/screens/main_navigation_screen.dart Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> Co-authored-by: dwvwdv <dwvwdv@protonmail.com> |
||
|
|
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> |
||
|
|
3dfa569bed |
Mobile offline features (#580)
* feat: mobile support. Basic functionality development includes adding and deleting transactions,viewing balances, * Fix mobile support issues in PR #426 This commit addresses the critical issues identified in the mobile-support PR: 1. **GitHub Actions Workflow Path Issues (Critical)** - Add mobile/ prefix to all path filters in flutter-build.yml - Add working-directory to all Flutter commands - Fix Android keystore and iOS CocoaPods paths - Fix artifact upload paths 2. **Error Handling Improvements** - Add try-catch blocks to all HTTP requests in services - Wrap all JSON parsing operations in error handling - Add proper error messages for network failures 3. **HTTP Request Timeout Configuration** - Add 30-second timeout to all HTTP requests - Prevents hanging on network failures 4. **Defensive Null Checks in Providers** - Add containsKey() checks before accessing result maps - Add proper type casting with null safety - Add fallback error messages These changes ensure the workflow triggers correctly on mobile/ directory changes and improves overall code robustness. * Fix transactions exposure and error handling issues - Add UnmodifiableListView to transactions getter to prevent external mutation - Call notifyListeners() immediately after setting _isLoading = false - Move jsonDecode to run only after successful statusCode verification - Replace string concatenation with Uri.replace() for proper URL encoding - Add try/catch for jsonDecode on non-2xx responses to handle non-JSON errors * Fix exception handling and duplicate parsing in auth_service.dart - Replace broad catch-all exception handlers with targeted exception handling - Add specific catches for SocketException, TimeoutException, HttpException, FormatException, and TypeError - Return safe, user-friendly error messages instead of exposing internal details - Log full exception details and stack traces using debugPrint for debugging - Fix duplicate User.fromJson calls in login and signup methods by parsing once and reusing the instance - Improve code efficiency and security by preventing information leakage * Fix 2FA login crash and improve UX Fixed the crash that occurred when logging in with 2FA-enabled accounts and improved the user experience by not showing error messages when MFA is required (it's a normal flow, not an error). Changes: - Added mounted check before setState() in login screen - Modified AuthProvider to not set error message when MFA is required - Ensures smooth transition from password entry to OTP entry - Prevents "setState() called after dispose()" error The flow now works correctly: 1. User enters email/password → clicks Sign In 2. Backend responds with mfa_required 3. OTP input field appears with friendly blue prompt (no red error) 4. User enters 6-digit code → clicks Sign In again 5. Login succeeds * Add debug logs to trace 2FA login flow Added comprehensive debug logging to understand why OTP field is not showing when MFA is required: - Log backend response status and body - Log login result in AuthProvider - Log MFA required state - Log when OTP field should be shown This will help identify if the issue is: 1. Backend not returning mfa_required flag 2. Response parsing issue 3. State management issue 4. UI rendering issue * Fix 2FA login flow by moving MFA state to AuthProvider PROBLEM: The LoginScreen was being recreated when AuthProvider called notifyListeners(), causing all internal state (_showOtpField) to be lost. This resulted in the OTP input field never appearing, making 2FA login impossible. ROOT CAUSE: The AppWrapper uses a Consumer<AuthProvider> that rebuilds the entire widget tree when auth state changes. When login() sets isLoading=false and calls notifyListeners(), a brand new LoginScreen instance is created, resetting all internal state. SOLUTION: - Moved _showMfaInput state from LoginScreen to AuthProvider - AuthProvider now manages when to show the MFA input field - LoginScreen uses Consumer to read this state reactively - State survives widget rebuilds FLOW: 1. User enters email/password → clicks Sign In 2. Backend responds with mfa_required: true 3. AuthProvider sets _showMfaInput = true 4. Consumer rebuilds, showing OTP field (state preserved) 5. User enters code → clicks Sign In 6. Backend validates → returns tokens → login succeeds Backend is confirmed working via tests (auth_controller_test.rb). * Fix mobile 2FA login requiring double password entry Problem: When 2FA is required during mobile login, the LoginScreen was being destroyed and recreated, causing text controllers to reset and forcing users to re-enter their credentials. Root cause: AppWrapper was checking authProvider.isLoading and showing a full-screen loading indicator during login attempts. This caused LoginScreen to be unmounted when isLoading=true, destroying the State and text controllers. When the backend returned mfa_required, isLoading=false triggered recreation of LoginScreen with empty fields. Solution: - Add isInitializing state to AuthProvider to distinguish initial auth check from active login attempts - Update AppWrapper to only show loading spinner during isInitializing, not during login flow - LoginScreen now persists across login attempts, preserving entered credentials Flow after fix: 1. User enters email/password 2. LoginScreen stays mounted (shows loading in button only) 3. Backend returns mfa_required 4. MFA field appears, email/password fields retain values 5. User enters OTP and submits (email/password automatically included) Files changed: - mobile/lib/providers/auth_provider.dart: Add isInitializing state - mobile/lib/main.dart: Use isInitializing instead of isLoading in AppWrapper * Add OTP error feedback for mobile 2FA login When users enter an incorrect OTP code during 2FA login, the app now: - Displays an error message indicating the code was invalid - Keeps the MFA input field visible for retry - Automatically clears the OTP field for easy re-entry Changes: - mobile/lib/providers/auth_provider.dart: * Distinguish between first MFA request vs invalid OTP error * Show error message when OTP code was submitted but invalid * Keep MFA input visible when in MFA flow with errors - mobile/lib/screens/login_screen.dart: * Clear OTP field after failed login attempt * Improve UX by allowing easy retry without re-entering credentials User flow after fix: 1. User enters email/password 2. MFA required - OTP field appears 3. User enters wrong OTP 4. Error message shows "Two-factor authentication required" 5. OTP field clears, ready for new code 6. User can immediately retry without re-entering email/password * Improve OTP error message clarity When user enters an invalid OTP code, show clearer error message "Invalid authentication code. Please try again." instead of the confusing "Two-factor authentication required" from backend. This makes it clear that the OTP was wrong, not that they need to start the 2FA process. * chore: delete generation ai create test flow md. * Update mobile/lib/screens/login_screen.dart Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * feat: add pubspec.lock file. * Linter * Update mobile/android/app/build.gradle Co-authored-by: Pedro Piñera Buendía <663605+pepicrft@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Update mobile/android/app/build.gradle com.sure.mobile -> am.sure.mobile Co-authored-by: Pedro Piñera Buendía <663605+pepicrft@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Update mobile/ios/Runner.xcodeproj/project.pbxproj Co-authored-by: Pedro Piñera Buendía <663605+pepicrft@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Update mobile/ios/Runner.xcodeproj/project.pbxproj Co-authored-by: Pedro Piñera Buendía <663605+pepicrft@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Update mobile/ios/Runner.xcodeproj/project.pbxproj Co-authored-by: Pedro Piñera Buendía <663605+pepicrft@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Fix iOS deployment target and update documentation - Update iOS minimum deployment target from 12.0 to 13.0 in Podfile for Flutter compatibility - Translate SIGNING_SETUP.md from Chinese to English for better accessibility - Remove TECHNICAL_GUIDE.md as requested * Restore TECHNICAL_GUIDE.md with partial content removal - Restore mobile/docs/TECHNICAL_GUIDE.md (previously deleted) - Remove only License, Contributing, and Related Links sections (from line 445 onwards) - Keep all technical documentation content (lines 1-444) * Fix setState after dispose errors across mobile app This commit fixes 5 critical setState/dispose errors identified by Cursor: 1. backend_config_screen.dart: Add mounted checks in _testConnection() and _saveAndContinue() methods to prevent setState calls after async operations (http.get, SharedPreferences) when widget is disposed. 2. transaction_form_screen.dart: Add mounted check in _selectDate() after showDatePicker to prevent setState when modal is dismissed while date picker is open. 3. main.dart: Add mounted check in _checkBackendConfig() after ApiConfig.initialize() to handle disposal during async initialization. 4. transactions_list_screen.dart: Add mounted check in the .then() callback of _showAddTransactionForm() to prevent calling _loadTransactions() on a disposed widget when modal is closed. 5. transactions_provider.dart: Fix premature notifyListeners() by removing intermediate notification after _isLoading = false, ensuring listeners only get notified once with complete state updates to prevent momentary stale UI state. All setState calls after async operations now properly check mounted status to prevent "setState() called after dispose()" errors. * Fix Android build: Remove package attribute from AndroidManifest.xml Remove deprecated package attribute from AndroidManifest.xml. The namespace is now correctly defined only in build.gradle as required by newer versions of Android Gradle Plugin. This fixes the build error: "Incorrect package="com.sure.mobile" found in source AndroidManifest.xml. Setting the namespace via the package attribute in the source AndroidManifest.xml is no longer supported." * Update issue templates * Change package name from com.sure.mobile to am.sure.mobile Updated Android package name across all files: - build.gradle: namespace and applicationId - MainActivity.kt: package declaration and file path - Moved MainActivity.kt from com/sure/mobile to am/sure/mobile This aligns with the package name change made in the mobile-support branch and fixes app crashes caused by package name mismatch. * Fix mobile app code quality issues - Add mounted check in backend_config_screen.dart to prevent setState after dispose - Translate Chinese comments to English in transactions_list_screen.dart for better maintainability - Replace brittle string-split date conversion with DateFormat in transaction_form_screen.dart for safer date handling These changes address code review feedback and improve code robustness. * Remove feature request template Delete unused feature request issue template file. * Fix mobile app code quality issues - Fix URL construction in backend_config_screen.dart to prevent double slashes by normalizing base URL (removing trailing slashes) before appending paths - Update pubspec.yaml to require Flutter 3.27.0+ for withValues API compatibility - Improve amount parsing robustness in transactions_list_screen.dart with proper locale handling, sign detection, and fallback error handling - Fix dismissible delete handler to prevent UI/backend inconsistency by moving deletion to confirmDismiss and only allowing dismissal on success * Fix mobile app performance and security issues - Eliminate duplicate _getAmountDisplayInfo calls in transactions list by computing display info once per transaction item - Upgrade flutter_secure_storage from 9.0.0 to 10.0.0 for AES-GCM encryption - Update dev dependencies: flutter_lints to 6.0.0 and flutter_launcher_icons to 0.14.4 * Update Android SDK requirements for flutter_secure_storage v10 - Increase compileSdk from 35 to 36 - Increase minSdkVersion from 21 to 24 This is required by flutter_secure_storage v10+ which uses newer Android APIs for AES-GCM encryption. * Fix transaction deletion message not displaying properly The success message was being shown in the onDismissed callback, which executes after the dismissal animation completes. By that time, the context may have become invalid due to widget tree rebuilds, causing the SnackBar to not display. Moved the success message to the confirmDismiss callback where we already have a captured scaffoldMessenger reference, ensuring the message displays reliably before the dismissal animation begins. * Add mounted check before showing SnackBar after async operation * Update mobile/android/app/build.gradle Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Fix empty state refresh and auth error feedback in mobile transactions screen - Wrap empty state in RefreshIndicator with CustomScrollView to enable pull-to-refresh when no transactions exist - Wrap error state in RefreshIndicator as well for consistency - Add SnackBar feedback when auth token is null in _loadTransactions instead of silent failure - Ensure mounted check before showing SnackBar to prevent errors after widget disposal * Fix flash of 'No accounts yet' page on app startup Added initialization state tracking to AccountsProvider to prevent the empty state from briefly showing while accounts are being loaded for the first time. Changes: - Add _isInitializing flag to AccountsProvider (starts as true) - Set to false after first fetchAccounts() completes - Reset to true when clearAccounts() is called - Update DashboardScreen to show loading during initialization This ensures a smooth user experience without visual flashing on app launch. * Refactor: Extract transaction deletion logic into dedicated method Improved code readability by extracting the 67-line confirmDismiss callback into a separate _confirmAndDeleteTransaction method. Changes: - Add Transaction model import - Create _confirmAndDeleteTransaction method that handles: - Confirmation dialog - Token retrieval - Deletion API call - Success/failure feedback - Simplify confirmDismiss to single line calling new method This separation of concerns makes the code more maintainable and the Dismissible widget configuration more concise. * Add offline-first architecture for mobile app Implement comprehensive offline functionality for the Sure mobile app: - SQLite local storage for transactions and accounts - Network connectivity detection and monitoring - Offline transaction creation with pending sync status - Automatic synchronization when network is restored - Visual indicators for offline status and pending syncs New Services: - ConnectivityService: Real-time network status monitoring - DatabaseHelper: SQLite database management with schema - OfflineStorageService: High-level API for local data operations - SyncService: Bidirectional sync between local DB and server New Models: - OfflineTransaction: Extended transaction model with sync status - SyncStatus enum: Track transaction sync state (synced/pending/failed) New UI Components: - ConnectivityBanner: Shows offline status and sync actions - SyncStatusBadge: Displays sync status on transactions Updated Components: - TransactionsProvider: Offline-first data fetching and caching - Main app: Wire up connectivity and sync services - Dashboard: Display connectivity banner Dependencies Added: - sqflite: SQLite database - connectivity_plus: Network status detection - uuid: Generate local transaction IDs - path: Database path utilities The app now works seamlessly offline, saving all transactions locally and automatically syncing when connectivity is restored. * Fix lint errors and warnings in offline features - Fix connectivity_service to handle ConnectivityResult (not List) - Remove unused imports in database_helper - Replace deprecated withOpacity with withValues in sync_status_badge - Use super parameters in OfflineTransaction constructor * Add offline support for AccountsProvider - Implement offline-first architecture for accounts - Load cached accounts from SQLite first for instant display - Sync with server when online and save to cache - Wire up ConnectivityService to AccountsProvider - Show cached data even when offline This fixes the issue where accounts couldn't be loaded offline. * Add debug logging to track offline transaction sync issues - Add detailed logging in TransactionsProvider.fetchTransactions - Add logging in SyncService.syncFromServer - Track transaction counts at each step - Log server responses and database operations This will help identify why transaction lists are empty. * Fix critical offline sync issues and add auto-sync Major fixes: 1. Fix accountId missing issue when syncing from server - Server may not return account_id in transaction JSON - Now use provided accountId parameter as fallback - Ensures transactions are saved with correct account_id 2. Add automatic sync when connectivity restored - Store last access token for auto-sync - Automatically upload pending transactions when back online - Prevent duplicate auto-sync attempts 3. Enhanced debug logging - Track accountId from server vs provided parameter - Log transaction upsert operations - Better error tracking This fixes: - Empty transaction lists when viewing account details - Offline transactions not auto-syncing when online - Data not being properly saved to local database * Add comprehensive debug logging for transaction storage Added detailed logging to track: - Account ID values from server vs provided parameter - Effective account ID being used for storage - Database insert/update operations - Number of transactions retrieved from database - Sample account IDs from stored transactions This will help identify: 1. Whether server returns account_id in transaction JSON 2. If fallback accountId parameter is being used correctly 3. What values are actually stored in the database 4. Why queries might not be finding transactions * Add detailed database query logging Added SQL query logging to DatabaseHelper: - Log all INSERT operations with account_id values - Log SELECT queries with WHERE conditions - Log result counts from queries This will show: - Exact account_id values being stored in database - SQL WHERE clauses being used for queries - Whether queries are finding matching records Combined with OfflineStorageService logs, this gives complete visibility into the data flow from server to database to UI. * Add debug log viewer screen - Create LogService for centralized log management - Add LogViewerScreen with filtering and export capabilities - Add debug logs button to dashboard - Support log levels: ERROR, WARNING, INFO, DEBUG - Features: filter by level, auto-scroll, copy to clipboard, clear logs - Keep last 1000 log entries * Add visual indicators for offline transactions and sync success Implemented the following features: 1. Pending transactions are now displayed with 50% opacity (gray) 2. Sync status badges (orange sync icon) show next to pending transaction amounts 3. Green cloud icon appears in dashboard AppBar when sync completes successfully 4. Auto-hide sync success indicator after 3 seconds with fade animation 5. Transaction list now uses offlineTransactions to access sync status Changes: - transactions_list_screen.dart: Display pending status with opacity and badges - dashboard_screen.dart: Add sync success indicator with listener - transactions_provider.dart: Already had offline-first architecture from previous commit * Fix offline transaction creation and add comprehensive logging This commit fixes all three reported issues: 1. Log viewer now showing logs - Added test log when log viewer opens - Added LogService to all critical services - Comprehensive logging throughout the stack 2. Offline transaction creation now working - TransactionFormScreen now uses TransactionsProvider (offline-first) - Previously was calling HTTP API directly, bypassing offline storage - Transactions now save to SQLite immediately, then sync in background 3. Sync and cloud icon improvements - Dashboard already has sync success indicator from previous commit - Added connectivity logging to track online/offline state changes - Sync will trigger automatically when connectivity restored Changes: - transaction_form_screen.dart: Use TransactionsProvider instead of TransactionsService - offline_storage_service.dart: Add comprehensive logging with LogService - connectivity_service.dart: Add logging for connectivity state changes - log_viewer_screen.dart: Add test log on screen open - main.dart: Add app startup log All operations now properly logged and can be viewed in the debug log viewer. * Fix transaction disappearing after sync and add manual sync Critical fixes: 1. Fixed syncTransactionsFromServer deleting uploaded transactions - Changed clearTransactions() to clearSyncedTransactions() - Now only clears synced transactions, preserves pending/failed ones - Added comprehensive logging to track sync process 2. Converted dashboard refresh button to manual sync - Now triggers full sync: upload pending, download from server - Shows progress indicator during sync - Displays success/error messages Technical details: - database_helper.dart: Added clearSyncedTransactions() method - offline_storage_service.dart: Use clearSyncedTransactions() in syncTransactionsFromServer() - sync_service.dart: Added detailed logging to all sync operations - dashboard_screen.dart: Refresh button now calls performManualSync() Previous issue: When connectivity restored, pending transactions were uploaded successfully, but then syncTransactionsFromServer() would call clearTransactions() which deleted ALL transactions including the just-uploaded ones, causing them to disappear from the UI even though they were on the server. Now fixed by only clearing synced transactions, keeping pending ones safe. * Change sync to use upsert logic instead of clear+insert Critical fix: syncTransactionsFromServer now uses upsert for each transaction instead of clearing synced transactions first. This prevents the race condition where: 1. Pending transaction uploaded and marked as synced 2. syncTransactionsFromServer clears all synced transactions (including just uploaded) 3. Server response doesn't include the new transaction yet 4. Transaction disappears from local database Changes: - offline_storage_service.dart: syncTransactionsFromServer now calls upsertTransactionFromServer for each transaction - Added logic to preserve existing accountId if server response has empty accountId - Replaced debugPrint with LogService for better tracking - Added detailed logging to track upsert operations This ensures uploaded transactions are never deleted, only updated with server data when available. * Fix flutter analyze warnings - Remove unused import 'package:flutter/foundation.dart' from offline_storage_service.dart - Fix use_build_context_synchronously warnings in transactions_list_screen.dart by capturing providers before async gap (showDialog) * Fix offline transaction sync not uploading pending transactions Critical bug: performFullSync was calling syncPendingTransactions which immediately returned 'Sync already in progress' error because performFullSync had already set _isSyncing = true. This caused pending transactions to never be uploaded to the server. Solution: - Extract core upload logic into _syncPendingTransactionsInternal() method - This internal method doesn't check _isSyncing flag - performFullSync calls the internal method directly - Public syncPendingTransactions() still checks _isSyncing for standalone calls Now pending transactions will properly upload during full sync. * Initial plan * Fix lifecycle, logging, error handling, and accessibility issues - Fixed provider lifecycle in dashboard_screen.dart to prevent accessing disposed providers - Fixed connectivity listener tracking in transactions_provider.dart with proper cleanup - Fixed async callback race conditions by checking mounted state - Replaced all debugPrint with LogService for consistent production logging - Added comprehensive error handling for database initialization with FlutterError reporting - Added missing index on server_id column for improved query performance - Optimized LogService memory usage (reduced from 1000 to 500 max logs) - Added debounced notifications in LogService (only when log viewer is active) - Added semantic labels to sync status badge icons for accessibility - Added semantic label to debug logs button for screen readers - Fixed misleading success message in transaction form (conditional based on sync status) - Fixed fire-and-forget async error handling to properly surface errors to UI - Improved error messages in accounts_provider with specific error types Co-authored-by: dwvwdv <89256478+dwvwdv@users.noreply.github.com> * Fix semantic labels and connectivity check issues from code review - Added semantic label to non-compact sync status badge for consistency - Fixed connectivity check in transaction form to use actual ConnectivityService.isOnline instead of lastSyncTime Co-authored-by: dwvwdv <89256478+dwvwdv@users.noreply.github.com> * Fix mounted check to properly track provider disposal state - Added _isDisposed flag to track actual provider disposal - Updated mounted getter to use _isDisposed instead of connectivity service availability - Ensures disposed providers don't continue processing connectivity changes Co-authored-by: dwvwdv <89256478+dwvwdv@users.noreply.github.com> * Address code review nitpicks for better code quality - Improved exception type checking in accounts_provider using instanceof instead of string contains - Improved ternary operator formatting for better readability - Added clarifying comment for fire-and-forget async pattern in connectivity change handler Co-authored-by: dwvwdv <89256478+dwvwdv@users.noreply.github.com> * Fix exception handling, error messages, and database index - Removed HandshakeException instanceof check, use string-based check for SSL/certificate errors - Improved error message clarity: 'Transaction upload failed' instead of 'Background sync failed' - Optimized server_id index to exclude NULL values for better performance and reduced size Co-authored-by: dwvwdv <89256478+dwvwdv@users.noreply.github.com> * Remove partial index syntax for better SQLite compatibility - Changed server_id index to standard syntax without WHERE clause - SQLite handles NULL values efficiently without explicit exclusion - Ensures compatibility across all SQLite versions Co-authored-by: dwvwdv <89256478+dwvwdv@users.noreply.github.com> * Fix Android release build to work without keystore - Made signing config optional when key.properties is missing - Prevents null pointer exception during release build - APK will be unsigned if no keystore is configured (debug signing will be used) Co-authored-by: dwvwdv <89256478+dwvwdv@users.noreply.github.com> * Improve keystore validation in build.gradle Enhanced keystore validation by checking keyAlias, keyPassword, and storePassword in addition to storeFile existence. Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Enhance Flutter build workflow with keystore checks Added checks for keystore secrets and adjusted APK build process based on their presence. Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Revert "Improve keystore validation in build.gradle" * Update mobile/lib/services/connectivity_service.dart Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Update mobile/lib/services/sync_service.dart Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Fix mobile provider null handling and connectivity issues - Fix null handling in ChangeNotifierProxyProvider callbacks for AccountsProvider and TransactionsProvider - Change connectivity service fallback from online to offline to prevent unwanted network calls - Replace raw error messages with user-friendly messages in TransactionsProvider - Add VPN and Bluetooth to online connectivity check - Fix database close() method to properly reset static cache - Add loading state and error handling to connectivity banner sync button - Update dependencies to latest stable versions (sqflite 2.4.2, path 1.9.1, connectivity_plus 7.0.0, uuid 4.5.2) * Replace all debugPrint with LogService in mobile app - Replace debugPrint with LogService in transactions_list_screen.dart - Replace debugPrint with LogService in auth_provider.dart - Replace debugPrint with LogService in auth_service.dart (login, signup, refreshToken methods) - Add LogService imports to all affected files - Use appropriate log levels: debug for informational logs, error for exceptions - Keep debugPrint only in LogService internal implementation All debugPrint calls now properly use the centralized LogService for consistent logging. * Fix Flutter analyze errors and warnings - Remove unused flutter/foundation.dart import in auth_service.dart - Update connectivity_service.dart to handle List<ConnectivityResult> for connectivity_plus 7.0.0 - Change StreamSubscription type from ConnectivityResult to List<ConnectivityResult> - Update _updateConnectionStatus to handle list of results using .any() - Check if any connectivity result indicates online status (mobile, wifi, ethernet, vpn, bluetooth) - Fix BuildContext usage across async gaps in connectivity_banner.dart - Use immediate mounted check before context usage (if (!mounted) return) - Properly guard all ScaffoldMessenger calls after async operations All flutter analyze errors and info warnings resolved. * Update mobile/lib/providers/accounts_provider.dart Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Update mobile/lib/providers/transactions_provider.dart Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Update mobile/lib/screens/dashboard_screen.dart Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Update mobile/lib/screens/dashboard_screen.dart Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Add error logging and implement pending delete tracking for offline transactions This commit adds comprehensive error logging and implements a robust pending delete system for offline transactions, ensuring data integrity and proper synchronization. Changes: - Add LogService error logging in AccountsProvider and DashboardScreen - Add SyncStatus.pendingDelete enum for tracking offline deletions - Implement markTransactionForDeletion in OfflineStorageService - Update offline delete logic to mark transactions instead of immediate deletion - Add _syncPendingDeletesInternal to SyncService for processing pending deletes - Update performFullSync to process pending deletes before uploads - Update hasPendingTransactions and pendingCount to include pending deletes The pending delete system ensures that: - Offline deletions are tracked and synced when connectivity is restored - Transactions with server IDs are deleted from both local and server - Transactions without server IDs (never synced) are deleted locally only - Failed delete attempts are marked and retried on next sync * Fix Flutter analyzer issues for pending delete feature - Add SyncStatus.pendingDelete case to sync_status_badge.dart switch - Use const constructor for SnackBar in dashboard_screen.dart - Display "Deleting" badge with delete icon for pendingDelete status * Add undo functionality for pending transactions This commit adds the ability to undo pending operations (both creates and deletes) while offline, providing users with better control over their pending transactions. Features: - Gray out pending delete transactions (50% opacity) similar to pending creates - Add "Undo" button on all pending transactions (both pending and pendingDelete) - Implement undoPendingTransaction in OfflineStorageService: - Pending creates: delete the transaction from local storage - Pending deletes: restore transaction to synced status - Add undoPendingTransaction method to TransactionsProvider - Show appropriate confirmation dialogs for undo operations - Display success/error messages after undo attempts User experience: - Pending transactions are visually distinct (grayed out with status badge) - Users can easily revert offline actions before they sync - Clear feedback on undo success or failure --------- Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> Co-authored-by: dwvwdv <dwvwdv@protonmail.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Juan José Mata <juanjo.mata@gmail.com> Co-authored-by: Pedro Piñera Buendía <663605+pepicrft@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> |
||
|
|
7866598057 |
Mobile native client via Flutter (#426)
* feat: mobile support. Basic functionality development includes adding and deleting transactions,viewing balances, * Fix mobile support issues in PR #426 This commit addresses the critical issues identified in the mobile-support PR: 1. **GitHub Actions Workflow Path Issues (Critical)** - Add mobile/ prefix to all path filters in flutter-build.yml - Add working-directory to all Flutter commands - Fix Android keystore and iOS CocoaPods paths - Fix artifact upload paths 2. **Error Handling Improvements** - Add try-catch blocks to all HTTP requests in services - Wrap all JSON parsing operations in error handling - Add proper error messages for network failures 3. **HTTP Request Timeout Configuration** - Add 30-second timeout to all HTTP requests - Prevents hanging on network failures 4. **Defensive Null Checks in Providers** - Add containsKey() checks before accessing result maps - Add proper type casting with null safety - Add fallback error messages These changes ensure the workflow triggers correctly on mobile/ directory changes and improves overall code robustness. * Fix transactions exposure and error handling issues - Add UnmodifiableListView to transactions getter to prevent external mutation - Call notifyListeners() immediately after setting _isLoading = false - Move jsonDecode to run only after successful statusCode verification - Replace string concatenation with Uri.replace() for proper URL encoding - Add try/catch for jsonDecode on non-2xx responses to handle non-JSON errors * Fix exception handling and duplicate parsing in auth_service.dart - Replace broad catch-all exception handlers with targeted exception handling - Add specific catches for SocketException, TimeoutException, HttpException, FormatException, and TypeError - Return safe, user-friendly error messages instead of exposing internal details - Log full exception details and stack traces using debugPrint for debugging - Fix duplicate User.fromJson calls in login and signup methods by parsing once and reusing the instance - Improve code efficiency and security by preventing information leakage * Fix 2FA login crash and improve UX Fixed the crash that occurred when logging in with 2FA-enabled accounts and improved the user experience by not showing error messages when MFA is required (it's a normal flow, not an error). Changes: - Added mounted check before setState() in login screen - Modified AuthProvider to not set error message when MFA is required - Ensures smooth transition from password entry to OTP entry - Prevents "setState() called after dispose()" error The flow now works correctly: 1. User enters email/password → clicks Sign In 2. Backend responds with mfa_required 3. OTP input field appears with friendly blue prompt (no red error) 4. User enters 6-digit code → clicks Sign In again 5. Login succeeds * Add debug logs to trace 2FA login flow Added comprehensive debug logging to understand why OTP field is not showing when MFA is required: - Log backend response status and body - Log login result in AuthProvider - Log MFA required state - Log when OTP field should be shown This will help identify if the issue is: 1. Backend not returning mfa_required flag 2. Response parsing issue 3. State management issue 4. UI rendering issue * Fix 2FA login flow by moving MFA state to AuthProvider PROBLEM: The LoginScreen was being recreated when AuthProvider called notifyListeners(), causing all internal state (_showOtpField) to be lost. This resulted in the OTP input field never appearing, making 2FA login impossible. ROOT CAUSE: The AppWrapper uses a Consumer<AuthProvider> that rebuilds the entire widget tree when auth state changes. When login() sets isLoading=false and calls notifyListeners(), a brand new LoginScreen instance is created, resetting all internal state. SOLUTION: - Moved _showMfaInput state from LoginScreen to AuthProvider - AuthProvider now manages when to show the MFA input field - LoginScreen uses Consumer to read this state reactively - State survives widget rebuilds FLOW: 1. User enters email/password → clicks Sign In 2. Backend responds with mfa_required: true 3. AuthProvider sets _showMfaInput = true 4. Consumer rebuilds, showing OTP field (state preserved) 5. User enters code → clicks Sign In 6. Backend validates → returns tokens → login succeeds Backend is confirmed working via tests (auth_controller_test.rb). * Fix mobile 2FA login requiring double password entry Problem: When 2FA is required during mobile login, the LoginScreen was being destroyed and recreated, causing text controllers to reset and forcing users to re-enter their credentials. Root cause: AppWrapper was checking authProvider.isLoading and showing a full-screen loading indicator during login attempts. This caused LoginScreen to be unmounted when isLoading=true, destroying the State and text controllers. When the backend returned mfa_required, isLoading=false triggered recreation of LoginScreen with empty fields. Solution: - Add isInitializing state to AuthProvider to distinguish initial auth check from active login attempts - Update AppWrapper to only show loading spinner during isInitializing, not during login flow - LoginScreen now persists across login attempts, preserving entered credentials Flow after fix: 1. User enters email/password 2. LoginScreen stays mounted (shows loading in button only) 3. Backend returns mfa_required 4. MFA field appears, email/password fields retain values 5. User enters OTP and submits (email/password automatically included) Files changed: - mobile/lib/providers/auth_provider.dart: Add isInitializing state - mobile/lib/main.dart: Use isInitializing instead of isLoading in AppWrapper * Add OTP error feedback for mobile 2FA login When users enter an incorrect OTP code during 2FA login, the app now: - Displays an error message indicating the code was invalid - Keeps the MFA input field visible for retry - Automatically clears the OTP field for easy re-entry Changes: - mobile/lib/providers/auth_provider.dart: * Distinguish between first MFA request vs invalid OTP error * Show error message when OTP code was submitted but invalid * Keep MFA input visible when in MFA flow with errors - mobile/lib/screens/login_screen.dart: * Clear OTP field after failed login attempt * Improve UX by allowing easy retry without re-entering credentials User flow after fix: 1. User enters email/password 2. MFA required - OTP field appears 3. User enters wrong OTP 4. Error message shows "Two-factor authentication required" 5. OTP field clears, ready for new code 6. User can immediately retry without re-entering email/password * Improve OTP error message clarity When user enters an invalid OTP code, show clearer error message "Invalid authentication code. Please try again." instead of the confusing "Two-factor authentication required" from backend. This makes it clear that the OTP was wrong, not that they need to start the 2FA process. * chore: delete generation ai create test flow md. * Update mobile/lib/screens/login_screen.dart Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * feat: add pubspec.lock file. * Linter * Update mobile/android/app/build.gradle Co-authored-by: Pedro Piñera Buendía <663605+pepicrft@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Update mobile/android/app/build.gradle com.sure.mobile -> am.sure.mobile Co-authored-by: Pedro Piñera Buendía <663605+pepicrft@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Update mobile/ios/Runner.xcodeproj/project.pbxproj Co-authored-by: Pedro Piñera Buendía <663605+pepicrft@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Update mobile/ios/Runner.xcodeproj/project.pbxproj Co-authored-by: Pedro Piñera Buendía <663605+pepicrft@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Update mobile/ios/Runner.xcodeproj/project.pbxproj Co-authored-by: Pedro Piñera Buendía <663605+pepicrft@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Fix iOS deployment target and update documentation - Update iOS minimum deployment target from 12.0 to 13.0 in Podfile for Flutter compatibility - Translate SIGNING_SETUP.md from Chinese to English for better accessibility - Remove TECHNICAL_GUIDE.md as requested * Restore TECHNICAL_GUIDE.md with partial content removal - Restore mobile/docs/TECHNICAL_GUIDE.md (previously deleted) - Remove only License, Contributing, and Related Links sections (from line 445 onwards) - Keep all technical documentation content (lines 1-444) * Fix setState after dispose errors across mobile app This commit fixes 5 critical setState/dispose errors identified by Cursor: 1. backend_config_screen.dart: Add mounted checks in _testConnection() and _saveAndContinue() methods to prevent setState calls after async operations (http.get, SharedPreferences) when widget is disposed. 2. transaction_form_screen.dart: Add mounted check in _selectDate() after showDatePicker to prevent setState when modal is dismissed while date picker is open. 3. main.dart: Add mounted check in _checkBackendConfig() after ApiConfig.initialize() to handle disposal during async initialization. 4. transactions_list_screen.dart: Add mounted check in the .then() callback of _showAddTransactionForm() to prevent calling _loadTransactions() on a disposed widget when modal is closed. 5. transactions_provider.dart: Fix premature notifyListeners() by removing intermediate notification after _isLoading = false, ensuring listeners only get notified once with complete state updates to prevent momentary stale UI state. All setState calls after async operations now properly check mounted status to prevent "setState() called after dispose()" errors. * Fix Android build: Remove package attribute from AndroidManifest.xml Remove deprecated package attribute from AndroidManifest.xml. The namespace is now correctly defined only in build.gradle as required by newer versions of Android Gradle Plugin. This fixes the build error: "Incorrect package="com.sure.mobile" found in source AndroidManifest.xml. Setting the namespace via the package attribute in the source AndroidManifest.xml is no longer supported." * Update issue templates * Change package name from com.sure.mobile to am.sure.mobile Updated Android package name across all files: - build.gradle: namespace and applicationId - MainActivity.kt: package declaration and file path - Moved MainActivity.kt from com/sure/mobile to am/sure/mobile This aligns with the package name change made in the mobile-support branch and fixes app crashes caused by package name mismatch. * Fix mobile app code quality issues - Add mounted check in backend_config_screen.dart to prevent setState after dispose - Translate Chinese comments to English in transactions_list_screen.dart for better maintainability - Replace brittle string-split date conversion with DateFormat in transaction_form_screen.dart for safer date handling These changes address code review feedback and improve code robustness. * Remove feature request template Delete unused feature request issue template file. * Fix mobile app code quality issues - Fix URL construction in backend_config_screen.dart to prevent double slashes by normalizing base URL (removing trailing slashes) before appending paths - Update pubspec.yaml to require Flutter 3.27.0+ for withValues API compatibility - Improve amount parsing robustness in transactions_list_screen.dart with proper locale handling, sign detection, and fallback error handling - Fix dismissible delete handler to prevent UI/backend inconsistency by moving deletion to confirmDismiss and only allowing dismissal on success * Fix mobile app performance and security issues - Eliminate duplicate _getAmountDisplayInfo calls in transactions list by computing display info once per transaction item - Upgrade flutter_secure_storage from 9.0.0 to 10.0.0 for AES-GCM encryption - Update dev dependencies: flutter_lints to 6.0.0 and flutter_launcher_icons to 0.14.4 * Update Android SDK requirements for flutter_secure_storage v10 - Increase compileSdk from 35 to 36 - Increase minSdkVersion from 21 to 24 This is required by flutter_secure_storage v10+ which uses newer Android APIs for AES-GCM encryption. * Fix transaction deletion message not displaying properly The success message was being shown in the onDismissed callback, which executes after the dismissal animation completes. By that time, the context may have become invalid due to widget tree rebuilds, causing the SnackBar to not display. Moved the success message to the confirmDismiss callback where we already have a captured scaffoldMessenger reference, ensuring the message displays reliably before the dismissal animation begins. * Add mounted check before showing SnackBar after async operation * Update mobile/android/app/build.gradle Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Fix empty state refresh and auth error feedback in mobile transactions screen - Wrap empty state in RefreshIndicator with CustomScrollView to enable pull-to-refresh when no transactions exist - Wrap error state in RefreshIndicator as well for consistency - Add SnackBar feedback when auth token is null in _loadTransactions instead of silent failure - Ensure mounted check before showing SnackBar to prevent errors after widget disposal * Fix flash of 'No accounts yet' page on app startup Added initialization state tracking to AccountsProvider to prevent the empty state from briefly showing while accounts are being loaded for the first time. Changes: - Add _isInitializing flag to AccountsProvider (starts as true) - Set to false after first fetchAccounts() completes - Reset to true when clearAccounts() is called - Update DashboardScreen to show loading during initialization This ensures a smooth user experience without visual flashing on app launch. * Refactor: Extract transaction deletion logic into dedicated method Improved code readability by extracting the 67-line confirmDismiss callback into a separate _confirmAndDeleteTransaction method. Changes: - Add Transaction model import - Create _confirmAndDeleteTransaction method that handles: - Confirmation dialog - Token retrieval - Deletion API call - Success/failure feedback - Simplify confirmDismiss to single line calling new method This separation of concerns makes the code more maintainable and the Dismissible widget configuration more concise. * Enhance Flutter build workflow with keystore checks Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> * Implement conditional signing configuration Added a check for keystore properties before configuring signing. Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> --------- Signed-off-by: Lazy Bone <89256478+dwvwdv@users.noreply.github.com> Co-authored-by: dwvwdv <dwvwdv@protonmail.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Juan José Mata <juanjo.mata@gmail.com> Co-authored-by: Pedro Piñera Buendía <663605+pepicrft@users.noreply.github.com> |