Skip to content

Comments

Finalize remote notification#390

Merged
dewabisma merged 19 commits intomainfrom
beast/finalize-notification
Feb 25, 2026
Merged

Finalize remote notification#390
dewabisma merged 19 commits intomainfrom
beast/finalize-notification

Conversation

@dewabisma
Copy link
Collaborator

Summary

Update the remote message handling to properly mapped into notification data known in the mobile app. Also updated the endpoint to production one.

@dewabisma dewabisma requested a review from n13 February 18, 2026 07:15
@dewabisma dewabisma marked this pull request as ready for review February 23, 2026 05:47
@n13
Copy link
Collaborator

n13 commented Feb 23, 2026

The PR looks solid overall. It correctly implements the remote notification handling, updates the backend endpoint to production, and handles device registration/unregistration lifecycles.

However, I have a few observations and questions:

1. pubspec.lock Downgrades

The pubspec.lock file shows a downgrade in the Flutter and Dart SDK constraints:

-  dart: ">=3.10.0 <4.0.0"
-  flutter: ">=3.38.0"
+  dart: ">=3.9.0 <4.0.0"
+  flutter: ">=3.35.0"

Was this intentional? It looks like it might have been generated with an older version of Flutter.

2. SenotiService Account Assumption

In quantus_sdk/lib/src/services/senoti_service.dart, the _getAccount1Credentials method relies on _settingsService.getMnemonic(0):

    final mnemonic = await _settingsService.getMnemonic(0);
    if (mnemonic == null) {
      throw Exception('Mnemonic not found.');
    }

This assumes the user always has a mnemonic-based wallet at index 0. If the app supports importing accounts via JSON or private key (where no mnemonic exists), or if the primary account isn't at index 0, this will throw an exception and registration will fail.

3. Error Handling in insertNewAddress

In FirebaseMessagingService, insertNewAddress catches errors and debug prints them:

    } catch (e) {
      debugPrint('Failed to insert new address: $e');
    }

This is likely fine for a background/fire-and-forget task, but just noting that failures here will be silent to the user.

4. Production Endpoint

You've updated senotiEndpoint to https://snt.quantus.com/api. Just confirming this is the intended production endpoint and we are ready to point to it.

Summary

The logic changes in FirebaseMessagingService to use TransactionService for parsing are excellent and much cleaner. The lifecycle management (register/unregister) is also a great addition. If the SDK version change is fine and the mnemonic assumption holds true for the current app architecture, this is good to go.

Copy link
Collaborator

@n13 n13 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't send pub key
Avoid accessing mnemonic if not needed

@dewabisma
Copy link
Collaborator Author

I have removed auth from registering remote notifications

@n13
Copy link
Collaborator

n13 commented Feb 24, 2026

The PR looks like a solid implementation of the remote notification features, especially the simplification of the SenotiService to remove the complex auth challenge-response flow. However, there are a couple of critical issues that should be addressed before merging.

1. Critical: Wallet Reset is Blocked by Network Failures

In mobile-app/lib/v2/screens/settings/settings_screen.dart, the _resetAndClearData method attempts to unregister the device before clearing local data.

// mobile-app/lib/v2/screens/settings/settings_screen.dart

Future<void> _resetAndClearData() async {
  if (FeatureFlags.enableRemoteNotifications) {
    try {
      await ref.read(firebaseMessagingServiceProvider).unregisterDevice();
    } catch (e) {
      if (mounted) {
        context.showErrorToaster(message: 'Failed to unregister device: $e');
      }
      return; // <--- This return aborts the entire reset process!
    }
  }
  // ... clearing data ...
}

If the user is offline or the snt.quantus.com server is down, unregisterDevice will throw (as it rethrows errors), and the catch block will return early. This means the user will be unable to reset their wallet if the network call fails.

Recommendation:
The unregistration should be "best effort". If it fails, log the error but continue with the local data reset. The user's primary intent here is to wipe the app locally.

    try {
      await ref.read(firebaseMessagingServiceProvider).unregisterDevice();
    } catch (e) {
      debugPrint('Failed to unregister device (non-fatal): $e');
      // Continue with reset even if this fails
    }

2. pubspec.lock Downgrades

The mobile-app/pubspec.lock file shows downgrades for the Flutter/Dart SDKs and image_picker_ios.

-  dart: ">=3.10.0 <4.0.0"
-  flutter: ">=3.38.0"
+  dart: ">=3.9.0 <4.0.0"
+  flutter: ">=3.35.0"

This likely happened because your local environment is on an older Flutter version than main.
Recommendation: Please revert the changes to pubspec.lock (or re-generate it with the latest Flutter version) to avoid downgrading the project dependencies for everyone else.

3. Minor: UI Latency in AccountsSheet

In mobile-app/lib/v2/screens/home/accounts_sheet.dart, insertNewAddress is awaited:

await ref.read(firebaseMessagingServiceProvider).insertNewAddress(accountToSave.accountId);

While insertNewAddress catches errors internally, awaiting the HTTP request here adds a small delay to the UI closing after adding an account. You might consider running this unawaited or using Future.microtask to keep the UI snappy, as it's a background operation.

Summary

  • Logic: The move to AccountsService (fixing the "index 0" assumption) and the simplified auth flow are great.
  • ⚠️ Action Required: Fix the blocking reset behavior in SettingsScreenV2.
  • ⚠️ Action Required: Revert pubspec.lock changes.

@n13
Copy link
Collaborator

n13 commented Feb 24, 2026

I agree with these ^^^^

Copy link
Collaborator

@n13 n13 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reset should not wait definitely not fail.

The rest also are good

@n13
Copy link
Collaborator

n13 commented Feb 25, 2026

The PR looks solid and addresses the critical feedback from the previous review. Here is my analysis of the changes:

Key Improvements & Fixes

  1. Blocking Reset Fixed: In SettingsScreenV2 (_resetAndClearData), the unregisterDevice call is now wrapped in a try-catch block. This ensures that if the network call fails (e.g., offline or server down), the user can still reset their wallet and wipe local data.
  2. Auth Removed: SenotiService has been simplified to remove the authentication requirement for registering devices, as requested.
  3. Dependencies: pubspec.lock has been updated. The Flutter SDK constraint is now >=3.38.0, which is an upgrade rather than the accidental downgrade seen previously.
  4. iOS Configuration: The changes to Info.plist (migrating to UIScene, setting FirebaseAppDelegateProxyEnabled to false) and AppDelegate.swift are correct for modern Flutter Firebase Messaging setups that require background fetch and custom handling.

Minor Suggestion (Nit)

In mobile-app/lib/v2/screens/home/accounts_sheet.dart:

792:       await ref.read(firebaseMessagingServiceProvider).insertNewAddress(accountToSave.accountId);

The insertNewAddress method in FirebaseMessagingService already catches and swallows all exceptions (printing them to debug console). Awaiting it here forces the UI to wait for the network request before closing the "Create Account" sheet, making the app feel slower.

Since the UI doesn't handle errors from this call (they are swallowed inside the service), you can safely run this unawaited to keep the UI snappy:

// Recommended: Run unawaited so the sheet closes immediately
ref.read(firebaseMessagingServiceProvider).insertNewAddress(accountToSave.accountId);

Conclusion

Approve. The critical blockers are resolved. The minor suggestion about insertNewAddress can be addressed in a follow-up or before merge if desired, but it's not a blocker.

Copy link
Collaborator

@n13 n13 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am OK with this but please check all uses of the new token functions and make sure none of them block the UI

The UI does not have to wait for this, it's useless to do so

We have in the past often managed to add unnecessary wait loops without user interface, so let's avoid this in the future.

Copy link
Collaborator

@n13 n13 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually this is a change request after all - none of these call can block the UI

@n13
Copy link
Collaborator

n13 commented Feb 25, 2026

The PR has been updated to address the feedback regarding blocking UI calls. I have reviewed the latest changes in beast/finalize-notification branch (specifically commits 26de2dc and 5a145f8).

Summary

The author has successfully removed the await keyword from firebaseMessagingServiceProvider calls in the following files, ensuring they do not block the UI:

  • mobile-app/lib/v2/screens/home/accounts_sheet.dart (insertNewAddress)
  • mobile-app/lib/v2/screens/create/wallet_ready_screen.dart (registerDeviceIfPossible)
  • mobile-app/lib/v2/screens/import/import_wallet_screen.dart (registerDeviceIfPossible)
  • mobile-app/lib/v2/screens/settings/settings_screen.dart (unregisterDevice)

Issue Found: Unhandled Future Error in SettingsScreenV2

There is one remaining issue in mobile-app/lib/v2/screens/settings/settings_screen.dart.

The code currently looks like this:

// mobile-app/lib/v2/screens/settings/settings_screen.dart
if (FeatureFlags.enableRemoteNotifications) {
  try {
    // This call is now unawaited (fire-and-forget)
    ref.read(firebaseMessagingServiceProvider).unregisterDevice();
  } catch (e) {
    // This catch block only catches synchronous errors.
    // It will NOT catch errors thrown asynchronously by the Future returned above.
    debugPrint('Failed to unregister device (non-fatal): $e');
  }
}

However, in mobile-app/lib/services/firebase_messaging_service.dart, the unregisterDevice method explicitly rethrows errors:

// mobile-app/lib/services/firebase_messaging_service.dart
Future<void> unregisterDevice() async {
  // ...
  try {
    await _senotiService.unregisterDevice(token, _platform);
  } catch (e) {
    debugPrint('Failed to unregister device: $e');
    rethrow; // <--- This will cause the unawaited Future in SettingsScreenV2 to complete with an error
  }
}

Because unregisterDevice rethrows the error and SettingsScreenV2 does not await or handle the future's error, any failure during unregistration (e.g., offline) will result in an unhandled exception (Unawaited Future Error). While this might not crash the app depending on the environment configuration, it is unsafe.

Recommendation

Since the requirement is for this operation to be "best effort" and non-blocking, you should remove the rethrow from unregisterDevice in firebase_messaging_service.dart. This would make it consistent with insertNewAddress and registerDeviceIfPossible, which already swallow errors.

Proposed Change:

In mobile-app/lib/services/firebase_messaging_service.dart:

  Future<void> unregisterDevice() async {
    final token = await getDeviceToken();
    if (token == null) {
      debugPrint('No FCM token available — skipping unregister');
      return;
    }
    try {
      await _senotiService.unregisterDevice(token, _platform);
    } catch (e) {
      debugPrint('Failed to unregister device: $e');
      // Remove 'rethrow' here to make it truly best-effort
    }
  }

Once this is fixed (or if you choose to handle the error in SettingsScreenV2 using .catchError(...)), the PR looks good to go. The pubspec.lock versions are also correct now (flutter: ">=3.38.0").

@n13
Copy link
Collaborator

n13 commented Feb 25, 2026

One more tiny change request

@dewabisma dewabisma merged commit 9d1da81 into main Feb 25, 2026
1 check passed
@dewabisma dewabisma deleted the beast/finalize-notification branch February 25, 2026 08:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants