@@ -1106,19 +1106,31 @@ class MeshCoreConnector extends ChangeNotifier {
11061106 }
11071107 }
11081108
1109- Future <void > _translateIncomingContactMessage (
1109+ Future <TranslationResult ?> translateContactMessage (
11101110 String contactKeyHex,
1111- Message message,
1112- ) async {
1111+ Message message, {
1112+ bool manualTranslation = false ,
1113+ }) async {
11131114 try {
1115+ if (message.translatedText? .trim ().isNotEmpty == true ||
1116+ (! manualTranslation &&
1117+ message.translationStatus != MessageTranslationStatus .none)) {
1118+ return null ;
1119+ }
11141120 final service = _translationService;
11151121 if (service == null ||
1116- ! service.shouldTranslateIncoming (
1117- text: message.text,
1118- isCli: message.isCli,
1119- isOutgoing: message.isOutgoing,
1120- )) {
1121- return ;
1122+ ! (manualTranslation
1123+ ? service.canTranslateIncoming (
1124+ text: message.text,
1125+ isCli: message.isCli,
1126+ isOutgoing: message.isOutgoing,
1127+ )
1128+ : service.shouldAutoTranslateIncoming (
1129+ text: message.text,
1130+ isCli: message.isCli,
1131+ isOutgoing: message.isOutgoing,
1132+ ))) {
1133+ return null ;
11221134 }
11231135 final targetLanguageCode = service.resolvedIncomingLanguageCode (
11241136 _appSettingsService? .settings.languageOverride,
@@ -1128,7 +1140,7 @@ class MeshCoreConnector extends ChangeNotifier {
11281140 targetLanguageCode: targetLanguageCode,
11291141 );
11301142 if (result == null ) {
1131- return ;
1143+ return null ;
11321144 }
11331145 final translated = result.status == MessageTranslationStatus .completed
11341146 ? result.translatedText
@@ -1143,24 +1155,38 @@ class MeshCoreConnector extends ChangeNotifier {
11431155 translationModelId: result.modelId,
11441156 ),
11451157 );
1158+ return result;
11461159 } catch (error) {
11471160 appLogger.warn ('Translation failed for contact message: $error ' );
1161+ return null ;
11481162 }
11491163 }
11501164
1151- Future <void > _translateIncomingChannelMessage (
1165+ Future <TranslationResult ?> translateChannelMessage (
11521166 int channelIndex,
1153- ChannelMessage message,
1154- ) async {
1167+ ChannelMessage message, {
1168+ bool manualTranslation = false ,
1169+ }) async {
11551170 try {
1171+ if (message.translatedText? .trim ().isNotEmpty == true ||
1172+ (! manualTranslation &&
1173+ message.translationStatus != MessageTranslationStatus .none)) {
1174+ return null ;
1175+ }
11561176 final service = _translationService;
11571177 if (service == null ||
1158- ! service.shouldTranslateIncoming (
1159- text: message.text,
1160- isCli: false ,
1161- isOutgoing: message.isOutgoing,
1162- )) {
1163- return ;
1178+ ! (manualTranslation
1179+ ? service.canTranslateIncoming (
1180+ text: message.text,
1181+ isCli: false ,
1182+ isOutgoing: message.isOutgoing,
1183+ )
1184+ : service.shouldAutoTranslateIncoming (
1185+ text: message.text,
1186+ isCli: false ,
1187+ isOutgoing: message.isOutgoing,
1188+ ))) {
1189+ return null ;
11641190 }
11651191 final targetLanguageCode = service.resolvedIncomingLanguageCode (
11661192 _appSettingsService? .settings.languageOverride,
@@ -1170,11 +1196,16 @@ class MeshCoreConnector extends ChangeNotifier {
11701196 targetLanguageCode: targetLanguageCode,
11711197 );
11721198 if (result == null ) {
1173- return ;
1199+ return null ;
11741200 }
1175- final translated = result.status == MessageTranslationStatus .completed
1201+ var translated = result.status == MessageTranslationStatus .completed
11761202 ? result.translatedText
11771203 : null ;
1204+ // Strip replyInfo prefix from translated text to match stored message.text
1205+ if (translated != null ) {
1206+ final regex = RegExp (r'^@\[[^\]]+\]\s+' , dotAll: true );
1207+ translated = translated.replaceFirst (regex, '' );
1208+ }
11781209 _updateStoredChannelMessage (
11791210 channelIndex,
11801211 message.messageId,
@@ -1185,8 +1216,10 @@ class MeshCoreConnector extends ChangeNotifier {
11851216 translationModelId: result.modelId,
11861217 ),
11871218 );
1219+ return result;
11881220 } catch (error) {
11891221 appLogger.warn ('Translation failed for channel message: $error ' );
1222+ return null ;
11901223 }
11911224 }
11921225
@@ -4373,6 +4406,12 @@ class MeshCoreConnector extends ChangeNotifier {
43734406 void _handleIncomingMessage (Uint8List frame) async {
43744407 if (_selfPublicKey == null ) return ;
43754408
4409+ // If we're syncing the queued messages, advance the queue immediately
4410+ // before any potentially long async work (like translation/notifications).
4411+ if (_isSyncingQueuedMessages) {
4412+ _handleQueuedMessageReceived ();
4413+ }
4414+
43764415 var message = _parseContactMessage (frame);
43774416
43784417 // If message parsing failed due to unknown contact, refresh contacts and retry
@@ -4438,35 +4477,52 @@ class MeshCoreConnector extends ChangeNotifier {
44384477 }
44394478 }
44404479 _addMessage (message.senderKeyHex, message);
4441- if (! message.isOutgoing) {
4442- unawaited (
4443- _translateIncomingContactMessage (message.senderKeyHex, message),
4444- );
4445- }
44464480 _maybeIncrementContactUnread (message);
44474481 notifyListeners ();
44484482
4449- // Show notification for new incoming message
4483+ // Show notification for new incoming message (run async with translation)
44504484 if (! message.isOutgoing &&
44514485 ! message.isCli &&
44524486 _appSettingsService != null ) {
44534487 final settings = _appSettingsService! .settings;
44544488 if (settings.notificationsEnabled && settings.notifyOnNewMessage) {
4455- if (contact? .type == advTypeChat) {
4456- _notificationService.showMessageNotification (
4457- contactName: contact? .name ?? 'Unknown' ,
4458- message: message.text,
4459- contactId: message.senderKeyHex,
4460- badgeCount: getTotalUnreadCount (),
4461- );
4462- } else if (contact? .type == advTypeRoom) {
4463- _notificationService.showMessageNotification (
4464- contactName: contact? .name ?? 'Unknown Room' ,
4465- message: message.text,
4466- contactId: message.senderKeyHex,
4467- badgeCount: getTotalUnreadCount (),
4489+ final msg = message; // capture for closure
4490+ final c = contact; // capture contact reference
4491+ unawaited (() async {
4492+ final translationResult = await translateContactMessage (
4493+ msg.senderKeyHex,
4494+ msg,
44684495 );
4469- }
4496+ if (c? .type == advTypeChat) {
4497+ final resolvedText =
4498+ (translationResult != null &&
4499+ translationResult.status ==
4500+ MessageTranslationStatus .completed &&
4501+ translationResult.translatedText.trim ().isNotEmpty)
4502+ ? translationResult.translatedText.trim ()
4503+ : msg.text.trim ();
4504+ await _notificationService.showMessageNotification (
4505+ contactName: c? .name ?? 'Unknown' ,
4506+ message: resolvedText,
4507+ contactId: msg.senderKeyHex,
4508+ badgeCount: getTotalUnreadCount (),
4509+ );
4510+ } else if (c? .type == advTypeRoom) {
4511+ final resolvedText =
4512+ (translationResult != null &&
4513+ translationResult.status ==
4514+ MessageTranslationStatus .completed &&
4515+ translationResult.translatedText.trim ().isNotEmpty)
4516+ ? translationResult.translatedText.trim ()
4517+ : msg.text.trim ();
4518+ await _notificationService.showMessageNotification (
4519+ contactName: c? .name ?? 'Unknown Room' ,
4520+ message: resolvedText,
4521+ contactId: msg.senderKeyHex,
4522+ badgeCount: getTotalUnreadCount (),
4523+ );
4524+ }
4525+ }());
44704526 }
44714527 }
44724528 _handleQueuedMessageReceived ();
@@ -4740,6 +4796,7 @@ class MeshCoreConnector extends ChangeNotifier {
47404796 void _maybeNotifyChannelMessage (
47414797 ChannelMessage message, {
47424798 String ? channelName,
4799+ TranslationResult ? translationResult,
47434800 }) {
47444801 if (message.isOutgoing || _appSettingsService == null ) return ;
47454802 final channelIndex = message.channelIndex;
@@ -4753,16 +4810,30 @@ class MeshCoreConnector extends ChangeNotifier {
47534810 final label = channelName ?? _channelDisplayName (channelIndex);
47544811 if (_appSettingsService! .isChannelMuted (label)) return ;
47554812
4756- _notificationService.showChannelMessageNotification (
4757- channelName: label,
4758- senderName: message.senderName,
4759- message: message.text,
4760- channelIndex: channelIndex,
4761- badgeCount: getTotalUnreadCount (),
4762- );
4813+ // Reuse translation result only if completed and non-empty; else use original text
4814+ final resolvedText =
4815+ (translationResult != null &&
4816+ translationResult.status == MessageTranslationStatus .completed &&
4817+ translationResult.translatedText.trim ().isNotEmpty)
4818+ ? translationResult.translatedText.trim ()
4819+ : message.text.trim ();
4820+ unawaited (() async {
4821+ await _notificationService.showChannelMessageNotification (
4822+ channelName: label,
4823+ senderName: message.senderName,
4824+ message: resolvedText,
4825+ channelIndex: message.channelIndex,
4826+ badgeCount: getTotalUnreadCount (),
4827+ );
4828+ }());
47634829 }
47644830
4765- void _handleIncomingChannelMessage (Uint8List frame) {
4831+ void _handleIncomingChannelMessage (Uint8List frame) async {
4832+ // If we're syncing the queued messages, advance the queue immediately
4833+ // before any potentially long async work (like translation/notifications).
4834+ if (_isSyncingQueuedMessages) {
4835+ _handleQueuedMessageReceived ();
4836+ }
47664837 final parsed = ChannelMessage .fromFrame (frame);
47674838 if (parsed != null && parsed.channelIndex != null ) {
47684839 if (_shouldDropSelfChannelMessage (parsed.senderName, parsed.pathBytes)) {
@@ -4781,23 +4852,25 @@ class MeshCoreConnector extends ChangeNotifier {
47814852 pathBytes: message.pathBytes,
47824853 );
47834854 final isNew = _addChannelMessage (message.channelIndex! , message);
4784- if (isNew && ! message.isOutgoing) {
4785- unawaited (
4786- _translateIncomingChannelMessage (message.channelIndex! , message),
4787- );
4788- }
47894855 _maybeIncrementChannelUnread (message, isNew: isNew);
47904856 notifyListeners ();
4791- if (isNew) {
4792- _maybeNotifyChannelMessage (message);
4857+ if (isNew && ! message.isOutgoing) {
4858+ final msg = message; // capture for closure
4859+ unawaited (() async {
4860+ final translationResult = await translateChannelMessage (
4861+ msg.channelIndex! ,
4862+ msg,
4863+ );
4864+ _maybeNotifyChannelMessage (msg, translationResult: translationResult);
4865+ }());
47934866 }
47944867 _handleQueuedMessageReceived ();
47954868 } else if (_isSyncingQueuedMessages) {
47964869 _handleQueuedMessageReceived ();
47974870 }
47984871 }
47994872
4800- void _handleLogRxData (Uint8List frame) {
4873+ void _handleLogRxData (Uint8List frame) async {
48014874 if (frame.length < 4 ) return ;
48024875 try {
48034876 final reader = BufferReader (frame);
@@ -4865,16 +4938,24 @@ class MeshCoreConnector extends ChangeNotifier {
48654938 pathBytes: message.pathBytes,
48664939 );
48674940 final isNew = _addChannelMessage (channel.index, message);
4868- if (isNew && ! message.isOutgoing) {
4869- unawaited (_translateIncomingChannelMessage (channel.index, message));
4870- }
48714941 _maybeIncrementChannelUnread (message, isNew: isNew);
48724942 notifyListeners ();
48734943 if (isNew) {
4874- final label = channel.name.isEmpty
4875- ? 'Channel ${channel .index }'
4876- : channel.name;
4877- _maybeNotifyChannelMessage (message, channelName: label);
4944+ // Run translation + notification asynchronously to avoid blocking
4945+ unawaited (() async {
4946+ final translationResult = await translateChannelMessage (
4947+ channel.index,
4948+ message,
4949+ );
4950+ final label = channel.name.isEmpty
4951+ ? 'Channel ${channel .index }'
4952+ : channel.name;
4953+ _maybeNotifyChannelMessage (
4954+ message,
4955+ channelName: label,
4956+ translationResult: translationResult,
4957+ );
4958+ }());
48784959 }
48794960 return ;
48804961 } catch (e) {
0 commit comments