mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
Add toast for image fetch failures
This commit is contained in:
@@ -5,22 +5,22 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.00023">
|
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000253">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="1: increment_build_number" time="0.387742">
|
<testcase classname="fastlane.lanes" name="1: increment_build_number" time="0.428509">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="2: build_app" time="112.078035">
|
<testcase classname="fastlane.lanes" name="2: build_app" time="102.185635">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="3: upload_to_app_store" time="215.877264">
|
<testcase classname="fastlane.lanes" name="3: upload_to_app_store" time="3.215591">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|||||||
157
lib/utils/transmission_target_resolver.dart
Normal file
157
lib/utils/transmission_target_resolver.dart
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import '../models/contact.dart';
|
||||||
|
import '../providers/contacts_provider.dart';
|
||||||
|
|
||||||
|
enum TransmissionTargetFailure { unknownContact, unknownRoute, tooFar }
|
||||||
|
|
||||||
|
class TransmissionTargetResolution {
|
||||||
|
final Contact? target;
|
||||||
|
final TransmissionTargetFailure? failure;
|
||||||
|
final int maxHops;
|
||||||
|
|
||||||
|
const TransmissionTargetResolution({
|
||||||
|
required this.target,
|
||||||
|
required this.failure,
|
||||||
|
required this.maxHops,
|
||||||
|
});
|
||||||
|
|
||||||
|
int get hops => target?.outPathLen ?? -1;
|
||||||
|
bool get isValid => target != null && failure == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TransmissionTargetResolver {
|
||||||
|
const TransmissionTargetResolver._();
|
||||||
|
|
||||||
|
static Contact? resolveLocalTarget({
|
||||||
|
required ContactsProvider contactsProvider,
|
||||||
|
required bool isSentByMe,
|
||||||
|
Uint8List? recipientPublicKey,
|
||||||
|
Uint8List? senderPublicKeyPrefix,
|
||||||
|
String? senderKey6FromEnvelope,
|
||||||
|
String? senderName,
|
||||||
|
}) {
|
||||||
|
if (isSentByMe) {
|
||||||
|
final recipient = _findByRecipientKey(contactsProvider, recipientPublicKey);
|
||||||
|
if (recipient != null) return recipient;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byEnvelope = _findByEnvelopeKey6(contactsProvider, senderKey6FromEnvelope);
|
||||||
|
if (byEnvelope != null) return byEnvelope;
|
||||||
|
|
||||||
|
final byPrefix = _findByPrefix(contactsProvider, senderPublicKeyPrefix);
|
||||||
|
if (byPrefix != null) return byPrefix;
|
||||||
|
|
||||||
|
return _findByName(contactsProvider, senderName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<TransmissionTargetResolution> resolveFetchTarget({
|
||||||
|
required ContactsProvider contactsProvider,
|
||||||
|
required Future<void> Function() refreshContacts,
|
||||||
|
required bool isSentByMe,
|
||||||
|
Uint8List? recipientPublicKey,
|
||||||
|
Uint8List? senderPublicKeyPrefix,
|
||||||
|
String? senderKey6FromEnvelope,
|
||||||
|
String? senderName,
|
||||||
|
required int maxFetchHops,
|
||||||
|
}) async {
|
||||||
|
var target = resolveLocalTarget(
|
||||||
|
contactsProvider: contactsProvider,
|
||||||
|
isSentByMe: isSentByMe,
|
||||||
|
recipientPublicKey: recipientPublicKey,
|
||||||
|
senderPublicKeyPrefix: senderPublicKeyPrefix,
|
||||||
|
senderKey6FromEnvelope: senderKey6FromEnvelope,
|
||||||
|
senderName: senderName,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (target == null || target.outPathLen < 0 || target.outPathLen > maxFetchHops) {
|
||||||
|
await refreshContacts();
|
||||||
|
target = resolveLocalTarget(
|
||||||
|
contactsProvider: contactsProvider,
|
||||||
|
isSentByMe: isSentByMe,
|
||||||
|
recipientPublicKey: recipientPublicKey,
|
||||||
|
senderPublicKeyPrefix: senderPublicKeyPrefix,
|
||||||
|
senderKey6FromEnvelope: senderKey6FromEnvelope,
|
||||||
|
senderName: senderName,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target == null) {
|
||||||
|
return TransmissionTargetResolution(
|
||||||
|
target: null,
|
||||||
|
failure: TransmissionTargetFailure.unknownContact,
|
||||||
|
maxHops: maxFetchHops,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (target.outPathLen < 0) {
|
||||||
|
return TransmissionTargetResolution(
|
||||||
|
target: target,
|
||||||
|
failure: TransmissionTargetFailure.unknownRoute,
|
||||||
|
maxHops: maxFetchHops,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (target.outPathLen > maxFetchHops) {
|
||||||
|
return TransmissionTargetResolution(
|
||||||
|
target: target,
|
||||||
|
failure: TransmissionTargetFailure.tooFar,
|
||||||
|
maxHops: maxFetchHops,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return TransmissionTargetResolution(
|
||||||
|
target: target,
|
||||||
|
failure: null,
|
||||||
|
maxHops: maxFetchHops,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Contact? _findByRecipientKey(
|
||||||
|
ContactsProvider contactsProvider,
|
||||||
|
Uint8List? recipientKey,
|
||||||
|
) {
|
||||||
|
if (recipientKey == null || recipientKey.isEmpty) return null;
|
||||||
|
final byKey = contactsProvider.findContactByKey(recipientKey);
|
||||||
|
if (byKey != null) return byKey;
|
||||||
|
if (recipientKey.length >= 6) {
|
||||||
|
return contactsProvider.findContactByPrefix(
|
||||||
|
Uint8List.fromList(recipientKey.sublist(0, 6)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Contact? _findByEnvelopeKey6(
|
||||||
|
ContactsProvider contactsProvider,
|
||||||
|
String? senderKey6FromEnvelope,
|
||||||
|
) {
|
||||||
|
if (senderKey6FromEnvelope == null || senderKey6FromEnvelope.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return contactsProvider.findContactByPrefixHex(senderKey6FromEnvelope);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Contact? _findByPrefix(
|
||||||
|
ContactsProvider contactsProvider,
|
||||||
|
Uint8List? senderPublicKeyPrefix,
|
||||||
|
) {
|
||||||
|
if (senderPublicKeyPrefix == null || senderPublicKeyPrefix.length < 6) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return contactsProvider.findContactByPrefix(
|
||||||
|
Uint8List.fromList(senderPublicKeyPrefix.sublist(0, 6)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Contact? _findByName(
|
||||||
|
ContactsProvider contactsProvider,
|
||||||
|
String? senderName,
|
||||||
|
) {
|
||||||
|
final normalized = senderName?.trim();
|
||||||
|
if (normalized == null || normalized.isEmpty) return null;
|
||||||
|
for (final contact in contactsProvider.contacts) {
|
||||||
|
if (contact.advName.trim().toLowerCase() == normalized.toLowerCase()) {
|
||||||
|
return contact;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,12 +3,12 @@ import 'dart:typed_data';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_avif/flutter_avif.dart';
|
import 'package:flutter_avif/flutter_avif.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../../models/contact.dart';
|
|
||||||
import '../../models/message.dart';
|
import '../../models/message.dart';
|
||||||
import '../../providers/connection_provider.dart';
|
import '../../providers/connection_provider.dart';
|
||||||
import '../../providers/contacts_provider.dart';
|
import '../../providers/contacts_provider.dart';
|
||||||
import '../../providers/image_provider.dart' as ip;
|
import '../../providers/image_provider.dart' as ip;
|
||||||
import '../../utils/image_message_parser.dart';
|
import '../../utils/image_message_parser.dart';
|
||||||
|
import '../../utils/transmission_target_resolver.dart';
|
||||||
import 'transfer_timeout.dart';
|
import 'transfer_timeout.dart';
|
||||||
|
|
||||||
/// A message bubble that shows a received or sent image.
|
/// A message bubble that shows a received or sent image.
|
||||||
@@ -58,8 +58,16 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
|
|||||||
|
|
||||||
return Consumer<ip.ImageProvider>(
|
return Consumer<ip.ImageProvider>(
|
||||||
builder: (context, imageProvider, _) {
|
builder: (context, imageProvider, _) {
|
||||||
|
final contactsProvider = context.read<ContactsProvider>();
|
||||||
final session = imageProvider.session(envelope.sessionId);
|
final session = imageProvider.session(envelope.sessionId);
|
||||||
final sender = _resolveSender(envelope);
|
final sender = TransmissionTargetResolver.resolveLocalTarget(
|
||||||
|
contactsProvider: contactsProvider,
|
||||||
|
isSentByMe: widget.isSentByMe,
|
||||||
|
recipientPublicKey: widget.message.recipientPublicKey,
|
||||||
|
senderPublicKeyPrefix: widget.message.senderPublicKeyPrefix,
|
||||||
|
senderKey6FromEnvelope: envelope.senderKey6,
|
||||||
|
senderName: widget.message.senderName,
|
||||||
|
);
|
||||||
final effectivePathLen =
|
final effectivePathLen =
|
||||||
sender != null && sender.outPathLen >= 0
|
sender != null && sender.outPathLen >= 0
|
||||||
? sender.outPathLen
|
? sender.outPathLen
|
||||||
@@ -213,35 +221,42 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
|
|||||||
}) async {
|
}) async {
|
||||||
if (_isRequesting) return;
|
if (_isRequesting) return;
|
||||||
final conn = context.read<ConnectionProvider>();
|
final conn = context.read<ConnectionProvider>();
|
||||||
var sender = _resolveSender(envelope);
|
final contactsProvider = context.read<ContactsProvider>();
|
||||||
if (sender == null ||
|
final resolution = await TransmissionTargetResolver.resolveFetchTarget(
|
||||||
sender.outPathLen < 0 ||
|
contactsProvider: contactsProvider,
|
||||||
sender.outPathLen > _maxFetchHops) {
|
refreshContacts: conn.getContacts,
|
||||||
await conn.getContacts();
|
isSentByMe: widget.isSentByMe,
|
||||||
|
recipientPublicKey: widget.message.recipientPublicKey,
|
||||||
|
senderPublicKeyPrefix: widget.message.senderPublicKeyPrefix,
|
||||||
|
senderKey6FromEnvelope: envelope.senderKey6,
|
||||||
|
senderName: widget.message.senderName,
|
||||||
|
maxFetchHops: _maxFetchHops,
|
||||||
|
);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
sender = _resolveSender(envelope);
|
|
||||||
}
|
if (resolution.failure == TransmissionTargetFailure.unknownContact) {
|
||||||
if (sender == null) {
|
|
||||||
await _showBlockingAlert(
|
await _showBlockingAlert(
|
||||||
'Cannot fetch image',
|
'Cannot fetch image',
|
||||||
'Sender contact is unknown. Sync contacts first.',
|
'Sender contact is unknown. Sync contacts first.',
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sender.outPathLen < 0) {
|
if (resolution.failure == TransmissionTargetFailure.unknownRoute) {
|
||||||
await _showBlockingAlert(
|
await _showBlockingAlert(
|
||||||
'Cannot fetch image',
|
'Cannot fetch image',
|
||||||
'Sender route is unknown. Sync contacts/path first.',
|
'Sender route is unknown. Sync contacts/path first.',
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sender.outPathLen > _maxFetchHops) {
|
if (resolution.failure == TransmissionTargetFailure.tooFar) {
|
||||||
await _showBlockingAlert(
|
await _showBlockingAlert(
|
||||||
'Cannot fetch image',
|
'Cannot fetch image',
|
||||||
'Message is too far (${sender.outPathLen} hops, max $_maxFetchHops).',
|
'Message is too far (${resolution.hops} hops, max ${resolution.maxHops}).',
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final sender = resolution.target!;
|
||||||
if (sender.outPathLen >= 2) {
|
if (sender.outPathLen >= 2) {
|
||||||
_showToast(
|
_showToast(
|
||||||
'Image fetch over ${sender.outPathLen} hops may take a while.',
|
'Image fetch over ${sender.outPathLen} hops may take a while.',
|
||||||
@@ -335,47 +350,6 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Contact? _resolveSender(ImageEnvelope envelope) {
|
|
||||||
final contactsProvider = context.read<ContactsProvider>();
|
|
||||||
|
|
||||||
// For sent direct messages, fetch must target the recipient peer first.
|
|
||||||
final recipientKey = widget.message.recipientPublicKey;
|
|
||||||
if (widget.isSentByMe && recipientKey != null && recipientKey.isNotEmpty) {
|
|
||||||
final byKey = contactsProvider.findContactByKey(recipientKey);
|
|
||||||
if (byKey != null) return byKey;
|
|
||||||
if (recipientKey.length >= 6) {
|
|
||||||
final byPrefix = contactsProvider.findContactByPrefix(
|
|
||||||
Uint8List.fromList(recipientKey.sublist(0, 6)),
|
|
||||||
);
|
|
||||||
if (byPrefix != null) return byPrefix;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final contact = contactsProvider.findContactByPrefixHex(
|
|
||||||
envelope.senderKey6,
|
|
||||||
);
|
|
||||||
if (contact != null) return contact;
|
|
||||||
|
|
||||||
final senderPrefix = widget.message.senderPublicKeyPrefix;
|
|
||||||
if (senderPrefix != null && senderPrefix.length >= 6) {
|
|
||||||
final c = contactsProvider.findContactByPrefix(
|
|
||||||
Uint8List.fromList(senderPrefix.sublist(0, 6)),
|
|
||||||
);
|
|
||||||
if (c != null) return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
final senderName = widget.message.senderName?.trim();
|
|
||||||
if (senderName != null && senderName.isNotEmpty) {
|
|
||||||
for (final c in contactsProvider.contacts) {
|
|
||||||
if (c.advName.trim().toLowerCase() == senderName.toLowerCase()) {
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showToast(String message) {
|
void _showToast(String message) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../../l10n/app_localizations.dart';
|
import '../../l10n/app_localizations.dart';
|
||||||
import '../../models/contact.dart';
|
|
||||||
import '../../models/message.dart';
|
import '../../models/message.dart';
|
||||||
import '../../providers/connection_provider.dart';
|
import '../../providers/connection_provider.dart';
|
||||||
import '../../providers/contacts_provider.dart';
|
import '../../providers/contacts_provider.dart';
|
||||||
import '../../providers/voice_provider.dart';
|
import '../../providers/voice_provider.dart';
|
||||||
|
import '../../utils/transmission_target_resolver.dart';
|
||||||
import '../../utils/voice_message_parser.dart';
|
import '../../utils/voice_message_parser.dart';
|
||||||
import 'transfer_timeout.dart';
|
import 'transfer_timeout.dart';
|
||||||
|
|
||||||
@@ -55,9 +54,17 @@ class _VoiceMessageBubbleState extends State<VoiceMessageBubble> {
|
|||||||
|
|
||||||
return Consumer<VoiceProvider>(
|
return Consumer<VoiceProvider>(
|
||||||
builder: (context, voiceProvider, _) {
|
builder: (context, voiceProvider, _) {
|
||||||
|
final contactsProvider = context.read<ContactsProvider>();
|
||||||
final session = voiceProvider.session(voiceId);
|
final session = voiceProvider.session(voiceId);
|
||||||
final envelope = VoiceEnvelope.tryParseText(widget.message.text);
|
final envelope = VoiceEnvelope.tryParseText(widget.message.text);
|
||||||
final sender = _resolveSenderContact();
|
final sender = TransmissionTargetResolver.resolveLocalTarget(
|
||||||
|
contactsProvider: contactsProvider,
|
||||||
|
isSentByMe: widget.isSentByMe,
|
||||||
|
recipientPublicKey: widget.message.recipientPublicKey,
|
||||||
|
senderPublicKeyPrefix: widget.message.senderPublicKeyPrefix,
|
||||||
|
senderKey6FromEnvelope: envelope?.senderKey6,
|
||||||
|
senderName: widget.message.senderName,
|
||||||
|
);
|
||||||
final effectivePathLen =
|
final effectivePathLen =
|
||||||
sender != null && sender.outPathLen >= 0
|
sender != null && sender.outPathLen >= 0
|
||||||
? sender.outPathLen
|
? sender.outPathLen
|
||||||
@@ -208,35 +215,42 @@ class _VoiceMessageBubbleState extends State<VoiceMessageBubble> {
|
|||||||
}) async {
|
}) async {
|
||||||
if (_isRequesting) return;
|
if (_isRequesting) return;
|
||||||
final connectionProvider = context.read<ConnectionProvider>();
|
final connectionProvider = context.read<ConnectionProvider>();
|
||||||
var sender = _resolveSenderContact();
|
final contactsProvider = context.read<ContactsProvider>();
|
||||||
if (sender == null ||
|
final resolution = await TransmissionTargetResolver.resolveFetchTarget(
|
||||||
sender.outPathLen < 0 ||
|
contactsProvider: contactsProvider,
|
||||||
sender.outPathLen > _maxFetchHops) {
|
refreshContacts: connectionProvider.getContacts,
|
||||||
await connectionProvider.getContacts();
|
isSentByMe: widget.isSentByMe,
|
||||||
|
recipientPublicKey: widget.message.recipientPublicKey,
|
||||||
|
senderPublicKeyPrefix: widget.message.senderPublicKeyPrefix,
|
||||||
|
senderKey6FromEnvelope: envelope?.senderKey6,
|
||||||
|
senderName: widget.message.senderName,
|
||||||
|
maxFetchHops: _maxFetchHops,
|
||||||
|
);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
sender = _resolveSenderContact();
|
|
||||||
}
|
if (resolution.failure == TransmissionTargetFailure.unknownContact) {
|
||||||
if (sender == null) {
|
|
||||||
await _showBlockingAlert(
|
await _showBlockingAlert(
|
||||||
'Cannot fetch voice',
|
'Cannot fetch voice',
|
||||||
'Sender contact is unknown. Sync contacts first.',
|
'Sender contact is unknown. Sync contacts first.',
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sender.outPathLen < 0) {
|
if (resolution.failure == TransmissionTargetFailure.unknownRoute) {
|
||||||
await _showBlockingAlert(
|
await _showBlockingAlert(
|
||||||
'Cannot fetch voice',
|
'Cannot fetch voice',
|
||||||
'Sender route is unknown. Sync contacts/path first.',
|
'Sender route is unknown. Sync contacts/path first.',
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sender.outPathLen > _maxFetchHops) {
|
if (resolution.failure == TransmissionTargetFailure.tooFar) {
|
||||||
await _showBlockingAlert(
|
await _showBlockingAlert(
|
||||||
'Cannot fetch voice',
|
'Cannot fetch voice',
|
||||||
'Message is too far (${sender.outPathLen} hops, max $_maxFetchHops).',
|
'Message is too far (${resolution.hops} hops, max ${resolution.maxHops}).',
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final sender = resolution.target!;
|
||||||
if (sender.outPathLen >= 2) {
|
if (sender.outPathLen >= 2) {
|
||||||
_showToast(
|
_showToast(
|
||||||
'Voice fetch over ${sender.outPathLen} hops may take a while.',
|
'Voice fetch over ${sender.outPathLen} hops may take a while.',
|
||||||
@@ -319,50 +333,6 @@ class _VoiceMessageBubbleState extends State<VoiceMessageBubble> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Contact? _resolveSenderContact() {
|
|
||||||
final contactsProvider = context.read<ContactsProvider>();
|
|
||||||
|
|
||||||
// For sent direct messages, fetch must target the recipient peer first.
|
|
||||||
final recipientKey = widget.message.recipientPublicKey;
|
|
||||||
if (widget.isSentByMe && recipientKey != null && recipientKey.isNotEmpty) {
|
|
||||||
final byKey = contactsProvider.findContactByKey(recipientKey);
|
|
||||||
if (byKey != null) return byKey;
|
|
||||||
if (recipientKey.length >= 6) {
|
|
||||||
final byPrefix = contactsProvider.findContactByPrefix(
|
|
||||||
Uint8List.fromList(recipientKey.sublist(0, 6)),
|
|
||||||
);
|
|
||||||
if (byPrefix != null) return byPrefix;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final senderPrefix = widget.message.senderPublicKeyPrefix;
|
|
||||||
if (senderPrefix != null && senderPrefix.length >= 6) {
|
|
||||||
final contact = contactsProvider.findContactByPrefix(
|
|
||||||
Uint8List.fromList(senderPrefix.sublist(0, 6)),
|
|
||||||
);
|
|
||||||
if (contact != null) return contact;
|
|
||||||
}
|
|
||||||
|
|
||||||
final envelope = VoiceEnvelope.tryParseText(widget.message.text);
|
|
||||||
if (envelope != null) {
|
|
||||||
final contact = contactsProvider.findContactByPrefixHex(
|
|
||||||
envelope.senderKey6,
|
|
||||||
);
|
|
||||||
if (contact != null) return contact;
|
|
||||||
}
|
|
||||||
|
|
||||||
final senderName = widget.message.senderName?.trim();
|
|
||||||
if (senderName != null && senderName.isNotEmpty) {
|
|
||||||
for (final contact in contactsProvider.contacts) {
|
|
||||||
if (contact.advName.trim().toLowerCase() == senderName.toLowerCase()) {
|
|
||||||
return contact;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showToast(String message) {
|
void _showToast(String message) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
|||||||
Reference in New Issue
Block a user