diff --git a/.fvmrc b/.fvmrc
new file mode 100644
index 00000000..e8b41515
--- /dev/null
+++ b/.fvmrc
@@ -0,0 +1,3 @@
+{
+ "flutter": "3.35.7"
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 10cd2174..20dd50a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -77,3 +77,6 @@ chrome/.packages
.pdm
bfg-1.14.0.jar
lib/generated/
+
+# FVM Version Cache
+.fvm/
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 6ccf0946..49f162e7 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -52,6 +52,10 @@
+
+
diff --git a/lib/background/background.dart b/lib/background/background.dart
index fef485dd..f9f6552e 100644
--- a/lib/background/background.dart
+++ b/lib/background/background.dart
@@ -69,7 +69,9 @@ Future serviceMain(ServiceInstance service) async {
subscription.listen((event) async {
try {
- if (await eventStore.hasItem(event.id!)) return;
+ if (await eventStore.hasItem(event.id!)) {
+ return;
+ }
await notification_service.retryNotification(event);
} catch (e) {
Logger().e('Error processing event', error: e);
diff --git a/lib/features/notifications/services/background_notification_service.dart b/lib/features/notifications/services/background_notification_service.dart
index 0cc2316f..a7879404 100644
--- a/lib/features/notifications/services/background_notification_service.dart
+++ b/lib/features/notifications/services/background_notification_service.dart
@@ -53,16 +53,18 @@ Future showLocalNotification(NostrEvent event) async {
try {
final mostroMessage = await _decryptAndProcessEvent(event);
if (mostroMessage == null) return;
-
final sessions = await _loadSessionsFromDatabase();
final matchingSession = sessions.cast().firstWhere(
(session) => session?.orderId == mostroMessage.id,
orElse: () => null,
);
-
+
final notificationData = await NotificationDataExtractor.extractFromMostroMessage(mostroMessage, null, session: matchingSession);
- if (notificationData == null || notificationData.isTemporary) return;
+
+ if (notificationData == null || notificationData.isTemporary) {
+ return;
+ }
final notificationText = await _getLocalizedNotificationText(notificationData.action, notificationData.values);
final expandedText = _getExpandedText(notificationData.values);
@@ -79,7 +81,7 @@ Future showLocalNotification(NostrEvent event) async {
enableVibration: true,
ticker: notificationText.title,
icon: '@drawable/ic_notification',
- styleInformation: expandedText != null
+ styleInformation: expandedText != null
? BigTextStyleInformation(expandedText, contentTitle: notificationText.title)
: null,
category: AndroidNotificationCategory.message,
@@ -111,25 +113,34 @@ Future showLocalNotification(NostrEvent event) async {
Future _decryptAndProcessEvent(NostrEvent event) async {
try {
- if (event.kind != 4 && event.kind != 1059) return null;
+ if (event.kind != 4 && event.kind != 1059) {
+ return null;
+ }
final sessions = await _loadSessionsFromDatabase();
+
final matchingSession = sessions.cast().firstWhere(
(s) => s?.tradeKey.public == event.recipient,
orElse: () => null,
);
- if (matchingSession == null) return null;
+ if (matchingSession == null) {
+ return null;
+ }
final decryptedEvent = await event.unWrap(matchingSession.tradeKey.private);
- if (decryptedEvent.content == null) return null;
+ if (decryptedEvent.content == null) {
+ return null;
+ }
final result = jsonDecode(decryptedEvent.content!);
- if (result is! List || result.isEmpty) return null;
+ if (result is! List || result.isEmpty) {
+ return null;
+ }
final mostroMessage = MostroMessage.fromJson(result[0]);
mostroMessage.timestamp = event.createdAt?.millisecondsSinceEpoch;
-
+
return mostroMessage;
} catch (e) {
Logger().e('Decrypt error: $e');
@@ -258,25 +269,25 @@ String? _getExpandedText(Map values) {
}
-Future retryNotification(NostrEvent event, {int maxAttempts = 3}) async {
- int attempt = 0;
- bool success = false;
-
- while (!success && attempt < maxAttempts) {
- try {
- await showLocalNotification(event);
- success = true;
- } catch (e) {
- attempt++;
- if (attempt >= maxAttempts) {
- Logger().e('Failed to show notification after $maxAttempts attempts: $e');
- break;
- }
-
- // Exponential backoff: 1s, 2s, 4s, etc.
- final backoffSeconds = pow(2, attempt - 1).toInt();
- Logger().e('Notification attempt $attempt failed: $e. Retrying in ${backoffSeconds}s');
- await Future.delayed(Duration(seconds: backoffSeconds));
- }
- }
+Future retryNotification(NostrEvent event, {int maxAttempts = 3}) async {
+ int attempt = 0;
+ bool success = false;
+
+ while (!success && attempt < maxAttempts) {
+ try {
+ await showLocalNotification(event);
+ success = true;
+ } catch (e) {
+ attempt++;
+ if (attempt >= maxAttempts) {
+ Logger().e('Failed to show notification after $maxAttempts attempts: $e');
+ break;
+ }
+
+ // Exponential backoff: 1s, 2s, 4s, etc.
+ final backoffSeconds = pow(2, attempt - 1).toInt();
+ Logger().e('Notification attempt $attempt failed: $e. Retrying in ${backoffSeconds}s');
+ await Future.delayed(Duration(seconds: backoffSeconds));
+ }
+ }
}
\ No newline at end of file
diff --git a/lib/features/notifications/utils/notification_data_extractor.dart b/lib/features/notifications/utils/notification_data_extractor.dart
index 02b277cd..35e04964 100644
--- a/lib/features/notifications/utils/notification_data_extractor.dart
+++ b/lib/features/notifications/utils/notification_data_extractor.dart
@@ -108,10 +108,22 @@ class NotificationDataExtractor {
}
break;
+ case Action.fiatSent:
+ // Notification for seller when buyer marks fiat as sent
+ // This requires seller to release the funds
+ final order = event.getPayload();
+ if (order != null) {
+ values = {
+ 'fiat_code': order.fiatCode,
+ 'fiat_amount': order.fiatAmount,
+ };
+ }
+ break;
+
case Action.fiatSentOk:
// Only sellers should receive fiat confirmed notifications
if (session?.role != Role.seller) return null;
-
+
final peer = event.getPayload();
if (peer?.publicKey != null) {
final buyerNym = ref != null