From ec1bcf583e4fb55a0483762391e79d64570a0770 Mon Sep 17 00:00:00 2001 From: Andrea Diaz Correia Date: Thu, 4 Dec 2025 21:09:15 -0300 Subject: [PATCH 1/5] Add FVM configuration for Flutter version management --- .fvmrc | 3 +++ .gitignore | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 .fvmrc 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/ From 616d1245567e0049236360b117b66d77da24f167 Mon Sep 17 00:00:00 2001 From: Andrea Diaz Correia Date: Wed, 3 Dec 2025 21:10:05 -0300 Subject: [PATCH 2/5] Update background notification icon --- .../background_notification_service.dart | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/features/notifications/services/background_notification_service.dart b/lib/features/notifications/services/background_notification_service.dart index 0cc2316f..4b2080c2 100644 --- a/lib/features/notifications/services/background_notification_service.dart +++ b/lib/features/notifications/services/background_notification_service.dart @@ -53,14 +53,14 @@ 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; @@ -78,7 +78,7 @@ Future showLocalNotification(NostrEvent event) async { playSound: true, enableVibration: true, ticker: notificationText.title, - icon: '@drawable/ic_notification', + icon: '@drawable/ic_bg_service_small', styleInformation: expandedText != null ? BigTextStyleInformation(expandedText, contentTitle: notificationText.title) : null, @@ -258,25 +258,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 From 4d9678fe39e50d110807e71a832400a4ea1fc45b Mon Sep 17 00:00:00 2001 From: Andrea Diaz Correia Date: Fri, 5 Dec 2025 21:48:00 -0300 Subject: [PATCH 3/5] Update local notification icon to use dedicated drawable Changes the local notification icon from `ic_bg_service_small` to `ic_notification` and adds metadata to preserve the dedicated notification icon in AndroidManifest.xml --- android/app/src/main/AndroidManifest.xml | 4 ++++ .../services/background_notification_service.dart | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) 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/features/notifications/services/background_notification_service.dart b/lib/features/notifications/services/background_notification_service.dart index 4b2080c2..963f30a7 100644 --- a/lib/features/notifications/services/background_notification_service.dart +++ b/lib/features/notifications/services/background_notification_service.dart @@ -78,7 +78,7 @@ Future showLocalNotification(NostrEvent event) async { playSound: true, enableVibration: true, ticker: notificationText.title, - icon: '@drawable/ic_bg_service_small', + icon: '@drawable/ic_notification', styleInformation: expandedText != null ? BigTextStyleInformation(expandedText, contentTitle: notificationText.title) : null, From d93efbedf182b55a40c0190649bad8efa82b4128 Mon Sep 17 00:00:00 2001 From: Andrea Diaz Correia Date: Fri, 5 Dec 2025 22:12:13 -0300 Subject: [PATCH 4/5] Update notification data extractor to handle fiatSent action --- .../utils/notification_data_extractor.dart | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) 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 From cc190f6353ed88b274520090dee0956eb496da6f Mon Sep 17 00:00:00 2001 From: Andrea Diaz Correia Date: Tue, 9 Dec 2025 17:10:57 -0300 Subject: [PATCH 5/5] Format code with consistent early return style --- lib/background/background.dart | 4 ++- .../background_notification_service.dart | 27 +++++++++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) 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 963f30a7..a7879404 100644 --- a/lib/features/notifications/services/background_notification_service.dart +++ b/lib/features/notifications/services/background_notification_service.dart @@ -54,7 +54,6 @@ Future showLocalNotification(NostrEvent event) async { final mostroMessage = await _decryptAndProcessEvent(event); if (mostroMessage == null) return; - final sessions = await _loadSessionsFromDatabase(); final matchingSession = sessions.cast().firstWhere( (session) => session?.orderId == mostroMessage.id, @@ -62,7 +61,10 @@ Future showLocalNotification(NostrEvent event) async { ); 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');