mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
fix: Clarify shared location labels and onboarding copy #123
This commit is contained in:
@@ -3267,7 +3267,7 @@ abstract class AppLocalizations {
|
|||||||
/// Updated onboarding overview feature 2
|
/// Updated onboarding overview feature 2
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Share SAR markers, map drawings, voice clips, and images over the mesh.'**
|
/// **'Share live location to a private channel, plus SAR markers, map drawings, voice clips, and images over the mesh.'**
|
||||||
String get wizardOverviewFeature2;
|
String get wizardOverviewFeature2;
|
||||||
|
|
||||||
/// Updated onboarding overview feature 3
|
/// Updated onboarding overview feature 3
|
||||||
@@ -3459,7 +3459,7 @@ abstract class AppLocalizations {
|
|||||||
/// Updated onboarding map feature 1
|
/// Updated onboarding map feature 1
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Track your own position, teammate locations, and movement trails on the map.'**
|
/// **'Track your own position, teammate locations, and share live location to a private channel from the channel menu.'**
|
||||||
String get wizardMapOpsFeature1;
|
String get wizardMapOpsFeature1;
|
||||||
|
|
||||||
/// Updated onboarding map feature 2
|
/// Updated onboarding map feature 2
|
||||||
|
|||||||
@@ -15,10 +15,14 @@ class MessageContactLocation {
|
|||||||
|
|
||||||
String get technicalSourceLabel {
|
String get technicalSourceLabel {
|
||||||
switch (source) {
|
switch (source) {
|
||||||
|
case 'shared':
|
||||||
|
return 'shared location';
|
||||||
|
case 'gps':
|
||||||
|
return 'shared location';
|
||||||
case 'telemetry':
|
case 'telemetry':
|
||||||
return 'telemetry';
|
return 'live telemetry';
|
||||||
case 'advert':
|
case 'advert':
|
||||||
return 'advert';
|
return 'shared advert';
|
||||||
default:
|
default:
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ import '../../providers/image_provider.dart' as ip;
|
|||||||
import '../../providers/app_provider.dart';
|
import '../../providers/app_provider.dart';
|
||||||
import '../drawing_minimap_preview.dart';
|
import '../drawing_minimap_preview.dart';
|
||||||
import '../../models/ble_packet_log.dart';
|
import '../../models/ble_packet_log.dart';
|
||||||
|
import '../../models/message_contact_location.dart';
|
||||||
import '../../services/sar_template_service.dart';
|
import '../../services/sar_template_service.dart';
|
||||||
|
import '../../services/location_tracking_service.dart';
|
||||||
import '../../utils/toast_logger.dart';
|
import '../../utils/toast_logger.dart';
|
||||||
import '../../utils/sar_message_parser.dart';
|
import '../../utils/sar_message_parser.dart';
|
||||||
import '../../utils/key_comparison.dart';
|
import '../../utils/key_comparison.dart';
|
||||||
@@ -421,6 +423,8 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showMessageOptions(BuildContext context) {
|
void _showMessageOptions(BuildContext context) {
|
||||||
|
final parentContext = context;
|
||||||
|
final l10n = AppLocalizations.of(context)!;
|
||||||
final connectionProvider = context.read<ConnectionProvider>();
|
final connectionProvider = context.read<ConnectionProvider>();
|
||||||
final selfPublicKey = connectionProvider.deviceInfo.publicKey;
|
final selfPublicKey = connectionProvider.deviceInfo.publicKey;
|
||||||
final isOwnMessage =
|
final isOwnMessage =
|
||||||
@@ -433,7 +437,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
||||||
),
|
),
|
||||||
builder: (context) => Padding(
|
builder: (sheetContext) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -441,29 +445,29 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
if (!isOwnMessage && widget.onReply != null)
|
if (!isOwnMessage && widget.onReply != null)
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Icon(Icons.reply),
|
leading: Icon(Icons.reply),
|
||||||
title: Text(AppLocalizations.of(context)!.reply),
|
title: Text(l10n.reply),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(sheetContext);
|
||||||
widget.onReply?.call();
|
widget.onReply?.call();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// Copy text option
|
// Copy text option
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Icon(Icons.copy),
|
leading: Icon(Icons.copy),
|
||||||
title: Text(AppLocalizations.of(context)!.copyText),
|
title: Text(l10n.copyText),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Clipboard.setData(ClipboardData(text: widget.message.text));
|
Clipboard.setData(ClipboardData(text: widget.message.text));
|
||||||
Navigator.pop(context);
|
Navigator.pop(sheetContext);
|
||||||
ToastLogger.success(
|
ToastLogger.success(
|
||||||
context,
|
parentContext,
|
||||||
AppLocalizations.of(context)!.textCopiedToClipboard,
|
l10n.textCopiedToClipboard,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// Save as Template option (only for SAR markers without existing template)
|
// Save as Template option (only for SAR markers without existing template)
|
||||||
if (widget.message.isSarMarker)
|
if (widget.message.isSarMarker)
|
||||||
Builder(
|
Builder(
|
||||||
builder: (context) {
|
builder: (_) {
|
||||||
// Extract emoji from SAR message
|
// Extract emoji from SAR message
|
||||||
final sarInfo = SarMessageParser.parse(widget.message.text);
|
final sarInfo = SarMessageParser.parse(widget.message.text);
|
||||||
if (sarInfo == null || sarInfo.emoji.isEmpty) {
|
if (sarInfo == null || sarInfo.emoji.isEmpty) {
|
||||||
@@ -482,10 +486,10 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: Icon(Icons.bookmark_add),
|
leading: Icon(Icons.bookmark_add),
|
||||||
title: Text(AppLocalizations.of(context)!.saveAsTemplate),
|
title: Text(l10n.saveAsTemplate),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(sheetContext);
|
||||||
_saveAsTemplate(context);
|
_saveAsTemplate(parentContext);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -495,49 +499,49 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
widget.message.sarGpsCoordinates != null)
|
widget.message.sarGpsCoordinates != null)
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Icon(Icons.share_location),
|
leading: Icon(Icons.share_location),
|
||||||
title: Text(AppLocalizations.of(context)!.shareLocation),
|
title: Text(l10n.shareLocation),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(sheetContext);
|
||||||
_shareLocation(context);
|
_shareLocation(parentContext);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// Navigate to drawing option (only for drawing messages)
|
// Navigate to drawing option (only for drawing messages)
|
||||||
if (widget.message.isDrawing && widget.message.drawingId != null)
|
if (widget.message.isDrawing && widget.message.drawingId != null)
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Icon(Icons.map),
|
leading: Icon(Icons.map),
|
||||||
title: Text(AppLocalizations.of(context)!.navigateToDrawing),
|
title: Text(l10n.navigateToDrawing),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(sheetContext);
|
||||||
_navigateToDrawing(context);
|
_navigateToDrawing(parentContext);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// Copy coordinates option (only for drawing messages)
|
// Copy coordinates option (only for drawing messages)
|
||||||
if (widget.message.isDrawing && widget.message.drawingId != null)
|
if (widget.message.isDrawing && widget.message.drawingId != null)
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Icon(Icons.copy),
|
leading: Icon(Icons.copy),
|
||||||
title: Text(AppLocalizations.of(context)!.copyCoordinates),
|
title: Text(l10n.copyCoordinates),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(sheetContext);
|
||||||
_copyDrawingCoordinates(context);
|
_copyDrawingCoordinates(parentContext);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// Hide from map option (only for drawing messages)
|
// Hide from map option (only for drawing messages)
|
||||||
if (widget.message.isDrawing && widget.message.drawingId != null)
|
if (widget.message.isDrawing && widget.message.drawingId != null)
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Icon(Icons.visibility_off),
|
leading: Icon(Icons.visibility_off),
|
||||||
title: Text(AppLocalizations.of(context)!.hideFromMap),
|
title: Text(l10n.hideFromMap),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(sheetContext);
|
||||||
_hideDrawingFromMap(context);
|
_hideDrawingFromMap(parentContext);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// Technical details option
|
// Technical details option
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Icon(Icons.data_object),
|
leading: Icon(Icons.data_object),
|
||||||
title: Text(AppLocalizations.of(context)!.technicalDetails),
|
title: Text(l10n.technicalDetails),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(sheetContext);
|
||||||
_showTechnicalDetails(context);
|
_showTechnicalDetails(parentContext);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (!isOwnMessage &&
|
if (!isOwnMessage &&
|
||||||
@@ -545,22 +549,22 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
widget.message.pathLen < 255)
|
widget.message.pathLen < 255)
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Icon(Icons.route),
|
leading: Icon(Icons.route),
|
||||||
title: Text(AppLocalizations.of(context)!.trace),
|
title: Text(l10n.trace),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(sheetContext);
|
||||||
_showTraceSheet(context);
|
_showTraceSheet(parentContext);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// Delete message option
|
// Delete message option
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Icon(Icons.delete, color: Colors.red),
|
leading: Icon(Icons.delete, color: Colors.red),
|
||||||
title: Text(
|
title: Text(
|
||||||
AppLocalizations.of(context)!.delete,
|
l10n.delete,
|
||||||
style: const TextStyle(color: Colors.red),
|
style: const TextStyle(color: Colors.red),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(sheetContext);
|
||||||
_showDeleteConfirmation(context);
|
_showDeleteConfirmation(parentContext);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -582,6 +586,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showTechnicalDetails(BuildContext context) {
|
void _showTechnicalDetails(BuildContext context) {
|
||||||
|
final l10n = AppLocalizations.of(context)!;
|
||||||
final connectionProvider = context.read<ConnectionProvider>();
|
final connectionProvider = context.read<ConnectionProvider>();
|
||||||
final radioBw = connectionProvider.deviceInfo.radioBw;
|
final radioBw = connectionProvider.deviceInfo.radioBw;
|
||||||
final radioSf = connectionProvider.deviceInfo.radioSf;
|
final radioSf = connectionProvider.deviceInfo.radioSf;
|
||||||
@@ -596,6 +601,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
widget.message.isFromSelf(selfPublicKey);
|
widget.message.isFromSelf(selfPublicKey);
|
||||||
|
|
||||||
String? senderName;
|
String? senderName;
|
||||||
|
Contact? senderContact;
|
||||||
if (widget.message.senderPublicKeyPrefix != null) {
|
if (widget.message.senderPublicKeyPrefix != null) {
|
||||||
final senderKeyHex = widget.message.senderPublicKeyPrefix!
|
final senderKeyHex = widget.message.senderPublicKeyPrefix!
|
||||||
.sublist(
|
.sublist(
|
||||||
@@ -606,7 +612,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
)
|
)
|
||||||
.map((b) => b.toRadixString(16).padLeft(2, '0'))
|
.map((b) => b.toRadixString(16).padLeft(2, '0'))
|
||||||
.join('');
|
.join('');
|
||||||
final senderContact = contactsProvider.contacts
|
senderContact = contactsProvider.contacts
|
||||||
.where((c) => c.publicKeyHex.startsWith(senderKeyHex))
|
.where((c) => c.publicKeyHex.startsWith(senderKeyHex))
|
||||||
.firstOrNull;
|
.firstOrNull;
|
||||||
senderName = senderContact?.advName;
|
senderName = senderContact?.advName;
|
||||||
@@ -629,7 +635,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
recipientName = recipientContact?.advName;
|
recipientName = recipientContact?.advName;
|
||||||
}
|
}
|
||||||
|
|
||||||
final messageLocationSnapshot = messagesProvider.getMessageContactLocation(
|
final storedLocationSnapshot = messagesProvider.getMessageContactLocation(
|
||||||
widget.message.id,
|
widget.message.id,
|
||||||
);
|
);
|
||||||
final receptionDetails = messagesProvider.getMessageReceptionDetails(
|
final receptionDetails = messagesProvider.getMessageReceptionDetails(
|
||||||
@@ -710,6 +716,12 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
final routeMetadata = context
|
final routeMetadata = context
|
||||||
.read<MessagesProvider>()
|
.read<MessagesProvider>()
|
||||||
.getMessageRouteMetadata(widget.message.id);
|
.getMessageRouteMetadata(widget.message.id);
|
||||||
|
final messageLocationSnapshot = _resolveDetailsLocationSnapshot(
|
||||||
|
storedSnapshot: storedLocationSnapshot,
|
||||||
|
senderContact: senderContact,
|
||||||
|
connectionProvider: connectionProvider,
|
||||||
|
isOwnMessage: isOwnMessage,
|
||||||
|
);
|
||||||
|
|
||||||
final rawLines = <String>[
|
final rawLines = <String>[
|
||||||
'Message ID: ${widget.message.id}',
|
'Message ID: ${widget.message.id}',
|
||||||
@@ -834,12 +846,9 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void copyField(String value) {
|
void copyField(BuildContext feedbackContext, String value) {
|
||||||
Clipboard.setData(ClipboardData(text: value));
|
Clipboard.setData(ClipboardData(text: value));
|
||||||
ToastLogger.success(
|
ToastLogger.success(feedbackContext, l10n.textCopiedToClipboard);
|
||||||
context,
|
|
||||||
AppLocalizations.of(context)!.textCopiedToClipboard,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
@@ -861,14 +870,14 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
AppLocalizations.of(context)!.messageTechnicalDetails,
|
l10n.messageTechnicalDetails,
|
||||||
style: Theme.of(sheetContext).textTheme.titleLarge,
|
style: Theme.of(sheetContext).textTheme.titleLarge,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => Navigator.pop(sheetContext),
|
onPressed: () => Navigator.pop(sheetContext),
|
||||||
icon: Icon(Icons.close),
|
icon: Icon(Icons.close),
|
||||||
tooltip: AppLocalizations.of(context)!.close,
|
tooltip: l10n.close,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -913,7 +922,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
_techSection(
|
_techSection(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
icon: Icons.location_on,
|
icon: Icons.location_on,
|
||||||
title: AppLocalizations.of(context)!.location,
|
title: l10n.location,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
_buildLocationPreviewMap(
|
_buildLocationPreviewMap(
|
||||||
@@ -923,29 +932,29 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.coordinates,
|
||||||
context,
|
|
||||||
)!.coordinates,
|
|
||||||
value: messageLocationSnapshot
|
value: messageLocationSnapshot
|
||||||
.formattedCoordinates,
|
.formattedCoordinates,
|
||||||
onCopy: () => copyField(
|
onCopy: () => copyField(
|
||||||
|
sheetContext,
|
||||||
messageLocationSnapshot.formattedCoordinates,
|
messageLocationSnapshot.formattedCoordinates,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.source,
|
label: l10n.source,
|
||||||
value: messageLocationSnapshot
|
value: messageLocationSnapshot
|
||||||
.technicalSourceLabel,
|
.technicalSourceLabel,
|
||||||
),
|
),
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.captured,
|
label: l10n.captured,
|
||||||
value: _formatRfc3339(
|
value: _formatRfc3339(
|
||||||
messageLocationSnapshot.sourceTimestamp ??
|
messageLocationSnapshot.sourceTimestamp ??
|
||||||
messageLocationSnapshot.capturedAt,
|
messageLocationSnapshot.capturedAt,
|
||||||
),
|
),
|
||||||
onCopy: () => copyField(
|
onCopy: () => copyField(
|
||||||
|
sheetContext,
|
||||||
_formatRfc3339(
|
_formatRfc3339(
|
||||||
messageLocationSnapshot.sourceTimestamp ??
|
messageLocationSnapshot.sourceTimestamp ??
|
||||||
messageLocationSnapshot.capturedAt,
|
messageLocationSnapshot.capturedAt,
|
||||||
@@ -963,13 +972,13 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
_techSection(
|
_techSection(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
icon: Icons.network_check,
|
icon: Icons.network_check,
|
||||||
title: AppLocalizations.of(context)!.linkQuality,
|
title: l10n.linkQuality,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
if (rssiDbm != null)
|
if (rssiDbm != null)
|
||||||
_signalRow(
|
_signalRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.rssi,
|
label: l10n.rssi,
|
||||||
valueLabel: '$rssiDbm dBm',
|
valueLabel: '$rssiDbm dBm',
|
||||||
normalized:
|
normalized:
|
||||||
((rssiDbm.toDouble() + 120.0) / 70.0)
|
((rssiDbm.toDouble() + 120.0) / 70.0)
|
||||||
@@ -984,7 +993,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
_signalRow(
|
_signalRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.snr,
|
label: l10n.snr,
|
||||||
valueLabel: '${snrDb.toStringAsFixed(1)} dB',
|
valueLabel: '${snrDb.toStringAsFixed(1)} dB',
|
||||||
normalized: ((snrDb + 20.0) / 40.0).clamp(
|
normalized: ((snrDb + 20.0) / 40.0).clamp(
|
||||||
0.0,
|
0.0,
|
||||||
@@ -1005,39 +1014,34 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
_techSection(
|
_techSection(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
icon: Icons.tune,
|
icon: Icons.tune,
|
||||||
title: AppLocalizations.of(context)!.delivery,
|
title: l10n.delivery,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.status,
|
label: l10n.status,
|
||||||
value: widget.message.deliveryStatus.name,
|
value: widget.message.deliveryStatus.name,
|
||||||
),
|
),
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.receivedRfc3339,
|
||||||
context,
|
|
||||||
)!.receivedRfc3339,
|
|
||||||
value: _formatRfc3339(widget.message.receivedAt),
|
value: _formatRfc3339(widget.message.receivedAt),
|
||||||
onCopy: () => copyField(
|
onCopy: () => copyField(
|
||||||
|
sheetContext,
|
||||||
_formatRfc3339(widget.message.receivedAt),
|
_formatRfc3339(widget.message.receivedAt),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (widget.message.expectedAckTag != null)
|
if (widget.message.expectedAckTag != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.expectedAckTag,
|
||||||
context,
|
|
||||||
)!.expectedAckTag,
|
|
||||||
value: widget.message.expectedAckTag!
|
value: widget.message.expectedAckTag!
|
||||||
.toString(),
|
.toString(),
|
||||||
),
|
),
|
||||||
if (receptionDetails?.senderToReceiptMs != null)
|
if (receptionDetails?.senderToReceiptMs != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.senderToReceipt,
|
||||||
context,
|
|
||||||
)!.senderToReceipt,
|
|
||||||
value: _formatDurationMs(
|
value: _formatDurationMs(
|
||||||
receptionDetails!.senderToReceiptMs!,
|
receptionDetails!.senderToReceiptMs!,
|
||||||
),
|
),
|
||||||
@@ -1045,9 +1049,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
if (receptionDetails?.estimatedTransmitMs != null)
|
if (receptionDetails?.estimatedTransmitMs != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.estimatedTx,
|
||||||
context,
|
|
||||||
)!.estimatedTx,
|
|
||||||
value: _formatDurationMs(
|
value: _formatDurationMs(
|
||||||
receptionDetails!.estimatedTransmitMs!,
|
receptionDetails!.estimatedTransmitMs!,
|
||||||
),
|
),
|
||||||
@@ -1055,9 +1057,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
if (receptionDetails?.postTransmitDelayMs != null)
|
if (receptionDetails?.postTransmitDelayMs != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.posttxDelay,
|
||||||
context,
|
|
||||||
)!.posttxDelay,
|
|
||||||
value: _formatDurationMs(
|
value: _formatDurationMs(
|
||||||
receptionDetails!.postTransmitDelayMs!,
|
receptionDetails!.postTransmitDelayMs!,
|
||||||
),
|
),
|
||||||
@@ -1065,87 +1065,80 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
if (widget.receivedCopies > 1)
|
if (widget.receivedCopies > 1)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.receivedCopies,
|
||||||
context,
|
|
||||||
)!.receivedCopies,
|
|
||||||
value: '${widget.receivedCopies}',
|
value: '${widget.receivedCopies}',
|
||||||
),
|
),
|
||||||
if (widget.message.suggestedTimeoutMs != null)
|
if (widget.message.suggestedTimeoutMs != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.ackTimeout,
|
label: l10n.ackTimeout,
|
||||||
value:
|
value:
|
||||||
'${widget.message.suggestedTimeoutMs} ms',
|
'${widget.message.suggestedTimeoutMs} ms',
|
||||||
),
|
),
|
||||||
if (retryCause != null)
|
if (retryCause != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.retryCause,
|
label: l10n.retryCause,
|
||||||
value: retryCause,
|
value: retryCause,
|
||||||
),
|
),
|
||||||
if (retryMode != null)
|
if (retryMode != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.retryMode,
|
label: l10n.retryMode,
|
||||||
value: retryMode,
|
value: retryMode,
|
||||||
),
|
),
|
||||||
if (widget.message.roundTripTimeMs != null)
|
if (widget.message.roundTripTimeMs != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.roundTrip,
|
label: l10n.roundTrip,
|
||||||
value: '${widget.message.roundTripTimeMs} ms',
|
value: '${widget.message.roundTripTimeMs} ms',
|
||||||
),
|
),
|
||||||
if (widget.message.retryAttempt > 0)
|
if (widget.message.retryAttempt > 0)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.retryAttempt,
|
||||||
context,
|
|
||||||
)!.retryAttempt,
|
|
||||||
value: '${widget.message.retryAttempt}/4',
|
value: '${widget.message.retryAttempt}/4',
|
||||||
),
|
),
|
||||||
if (widget.message.lastRetryAt != null)
|
if (widget.message.lastRetryAt != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.lastRetry,
|
label: l10n.lastRetry,
|
||||||
value: _formatRfc3339(
|
value: _formatRfc3339(
|
||||||
widget.message.lastRetryAt!,
|
widget.message.lastRetryAt!,
|
||||||
),
|
),
|
||||||
onCopy: () => copyField(
|
onCopy: () => copyField(
|
||||||
|
sheetContext,
|
||||||
_formatRfc3339(widget.message.lastRetryAt!),
|
_formatRfc3339(widget.message.lastRetryAt!),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (widget.message.usedFloodFallback)
|
if (widget.message.usedFloodFallback)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.floodFallback,
|
||||||
context,
|
value: l10n.yes,
|
||||||
)!.floodFallback,
|
|
||||||
value: AppLocalizations.of(context)!.yes,
|
|
||||||
),
|
),
|
||||||
if (routeMetadata?.canonicalPath
|
if (routeMetadata?.canonicalPath
|
||||||
case final routePath?)
|
case final routePath?)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.selectedPath,
|
||||||
context,
|
|
||||||
)!.selectedPath,
|
|
||||||
value: routePath,
|
value: routePath,
|
||||||
onCopy: () => copyField(routePath),
|
onCopy: () =>
|
||||||
|
copyField(sheetContext, routePath),
|
||||||
),
|
),
|
||||||
if (retryResult != null)
|
if (retryResult != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.retryResult,
|
||||||
context,
|
|
||||||
)!.retryResult,
|
|
||||||
value: retryResult,
|
value: retryResult,
|
||||||
),
|
),
|
||||||
if (packetPathHex != null)
|
if (packetPathHex != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.pathBytes,
|
label: l10n.pathBytes,
|
||||||
value: packetPathHex,
|
value: packetPathHex,
|
||||||
onCopy: () => copyField(packetPathHex),
|
onCopy: () =>
|
||||||
|
copyField(sheetContext, packetPathHex),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -1154,18 +1147,19 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
_techSection(
|
_techSection(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
icon: Icons.badge,
|
icon: Icons.badge,
|
||||||
title: AppLocalizations.of(context)!.identity,
|
title: l10n.identity,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.messageId,
|
label: l10n.messageId,
|
||||||
value: widget.message.id,
|
value: widget.message.id,
|
||||||
onCopy: () => copyField(widget.message.id),
|
onCopy: () =>
|
||||||
|
copyField(sheetContext, widget.message.id),
|
||||||
),
|
),
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.sender,
|
label: l10n.sender,
|
||||||
value:
|
value:
|
||||||
senderName ??
|
senderName ??
|
||||||
widget.message.senderName ??
|
widget.message.senderName ??
|
||||||
@@ -1174,24 +1168,26 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
if (senderPrefixHex != null)
|
if (senderPrefixHex != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.senderKey,
|
label: l10n.senderKey,
|
||||||
value: senderPrefixHex,
|
value: senderPrefixHex,
|
||||||
onCopy: () => copyField(senderPrefixHex),
|
onCopy: () =>
|
||||||
|
copyField(sheetContext, senderPrefixHex),
|
||||||
),
|
),
|
||||||
if (recipientName != null)
|
if (recipientName != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.recipient,
|
label: l10n.recipient,
|
||||||
value: recipientName,
|
value: recipientName,
|
||||||
),
|
),
|
||||||
if (recipientPrefixHex != null)
|
if (recipientPrefixHex != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.recipientKey,
|
||||||
context,
|
|
||||||
)!.recipientKey,
|
|
||||||
value: recipientPrefixHex,
|
value: recipientPrefixHex,
|
||||||
onCopy: () => copyField(recipientPrefixHex),
|
onCopy: () => copyField(
|
||||||
|
sheetContext,
|
||||||
|
recipientPrefixHex,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -1201,53 +1197,47 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
_techSection(
|
_techSection(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
icon: Icons.graphic_eq,
|
icon: Icons.graphic_eq,
|
||||||
title: AppLocalizations.of(context)!.voice,
|
title: l10n.voice,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.voiceId,
|
label: l10n.voiceId,
|
||||||
value: widget.message.voiceId ?? '-',
|
value: widget.message.voiceId ?? '-',
|
||||||
),
|
),
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.envelope,
|
label: l10n.envelope,
|
||||||
value: envelope != null
|
value: envelope != null
|
||||||
? 'VE3 compact'
|
? 'VE3 compact'
|
||||||
: AppLocalizations.of(context)!.unknown,
|
: l10n.unknown,
|
||||||
),
|
),
|
||||||
if (voiceSession != null)
|
if (voiceSession != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.sessionProgress,
|
||||||
context,
|
|
||||||
)!.sessionProgress,
|
|
||||||
value:
|
value:
|
||||||
'${voiceSession.receivedCount}/${voiceSession.total} segments',
|
'${voiceSession.receivedCount}/${voiceSession.total} segments',
|
||||||
),
|
),
|
||||||
if (voiceSession != null)
|
if (voiceSession != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.complete,
|
label: l10n.complete,
|
||||||
value: voiceSession.isComplete
|
value: voiceSession.isComplete
|
||||||
? AppLocalizations.of(context)!.yes
|
? l10n.yes
|
||||||
: AppLocalizations.of(context)!.no,
|
: l10n.no,
|
||||||
),
|
),
|
||||||
if (transferDetails != null)
|
if (transferDetails != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.transfers,
|
||||||
context,
|
|
||||||
)!.transfers,
|
|
||||||
value: '${transferDetails.totalTransfers}',
|
value: '${transferDetails.totalTransfers}',
|
||||||
),
|
),
|
||||||
if (transferDetails != null &&
|
if (transferDetails != null &&
|
||||||
transferDetails.downloaders.isNotEmpty)
|
transferDetails.downloaders.isNotEmpty)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.downloadedBy,
|
||||||
context,
|
|
||||||
)!.downloadedBy,
|
|
||||||
value: _formatDownloaderSummary(
|
value: _formatDownloaderSummary(
|
||||||
transferDetails,
|
transferDetails,
|
||||||
),
|
),
|
||||||
@@ -1255,9 +1245,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
if (voiceTxEstimate > Duration.zero)
|
if (voiceTxEstimate > Duration.zero)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.estimatedTx,
|
||||||
context,
|
|
||||||
)!.estimatedTx,
|
|
||||||
value: voiceTxEstimate.inSeconds < 60
|
value: voiceTxEstimate.inSeconds < 60
|
||||||
? '~${voiceTxEstimate.inSeconds}s'
|
? '~${voiceTxEstimate.inSeconds}s'
|
||||||
: '~${voiceTxEstimate.inMinutes}m ${voiceTxEstimate.inSeconds % 60}s',
|
: '~${voiceTxEstimate.inMinutes}m ${voiceTxEstimate.inSeconds % 60}s',
|
||||||
@@ -1271,28 +1259,28 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
_techSection(
|
_techSection(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
icon: Icons.image_outlined,
|
icon: Icons.image_outlined,
|
||||||
title: AppLocalizations.of(context)!.image,
|
title: l10n.image,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.envelope,
|
label: l10n.envelope,
|
||||||
value: 'IE1',
|
value: 'IE1',
|
||||||
),
|
),
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.format,
|
label: l10n.format,
|
||||||
value: imageEnvelope.format.label,
|
value: imageEnvelope.format.label,
|
||||||
),
|
),
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.dimensions,
|
label: l10n.dimensions,
|
||||||
value:
|
value:
|
||||||
'${imageEnvelope.width}×${imageEnvelope.height}',
|
'${imageEnvelope.width}×${imageEnvelope.height}',
|
||||||
),
|
),
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.segments,
|
label: l10n.segments,
|
||||||
value: imageSession != null
|
value: imageSession != null
|
||||||
? '${imageSession.receivedCount}/${imageSession.total}'
|
? '${imageSession.receivedCount}/${imageSession.total}'
|
||||||
: '${imageEnvelope.total}',
|
: '${imageEnvelope.total}',
|
||||||
@@ -1300,26 +1288,22 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
if (imageSession != null)
|
if (imageSession != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(context)!.complete,
|
label: l10n.complete,
|
||||||
value: imageSession.isComplete
|
value: imageSession.isComplete
|
||||||
? AppLocalizations.of(context)!.yes
|
? l10n.yes
|
||||||
: AppLocalizations.of(context)!.no,
|
: l10n.no,
|
||||||
),
|
),
|
||||||
if (transferDetails != null)
|
if (transferDetails != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.transfers,
|
||||||
context,
|
|
||||||
)!.transfers,
|
|
||||||
value: '${transferDetails.totalTransfers}',
|
value: '${transferDetails.totalTransfers}',
|
||||||
),
|
),
|
||||||
if (transferDetails != null &&
|
if (transferDetails != null &&
|
||||||
transferDetails.downloaders.isNotEmpty)
|
transferDetails.downloaders.isNotEmpty)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.downloadedBy,
|
||||||
context,
|
|
||||||
)!.downloadedBy,
|
|
||||||
value: _formatDownloaderSummary(
|
value: _formatDownloaderSummary(
|
||||||
transferDetails,
|
transferDetails,
|
||||||
),
|
),
|
||||||
@@ -1327,9 +1311,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
if (imageTxEstimate > Duration.zero)
|
if (imageTxEstimate > Duration.zero)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
sheetContext,
|
sheetContext,
|
||||||
label: AppLocalizations.of(
|
label: l10n.estimatedTx,
|
||||||
context,
|
|
||||||
)!.estimatedTx,
|
|
||||||
value: imageTxEstimate.inSeconds < 60
|
value: imageTxEstimate.inSeconds < 60
|
||||||
? '~${imageTxEstimate.inSeconds}s'
|
? '~${imageTxEstimate.inSeconds}s'
|
||||||
: '~${imageTxEstimate.inMinutes}m ${imageTxEstimate.inSeconds % 60}s',
|
: '~${imageTxEstimate.inMinutes}m ${imageTxEstimate.inSeconds % 60}s',
|
||||||
@@ -1344,7 +1326,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
dense: true,
|
dense: true,
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
title: Text(
|
title: Text(
|
||||||
AppLocalizations.of(context)!.rawDump,
|
l10n.rawDump,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
@@ -1429,6 +1411,61 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MessageContactLocation? _resolveDetailsLocationSnapshot({
|
||||||
|
required MessageContactLocation? storedSnapshot,
|
||||||
|
required Contact? senderContact,
|
||||||
|
required ConnectionProvider connectionProvider,
|
||||||
|
required bool isOwnMessage,
|
||||||
|
}) {
|
||||||
|
if (storedSnapshot != null) {
|
||||||
|
return storedSnapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOwnMessage) {
|
||||||
|
final advLat = connectionProvider.deviceInfo.advLat;
|
||||||
|
final advLon = connectionProvider.deviceInfo.advLon;
|
||||||
|
if (advLat != null &&
|
||||||
|
advLon != null &&
|
||||||
|
(advLat != 0 || advLon != 0)) {
|
||||||
|
return MessageContactLocation(
|
||||||
|
location: LatLng(advLat / 1e6, advLon / 1e6),
|
||||||
|
source: 'shared',
|
||||||
|
capturedAt: widget.message.sentAt,
|
||||||
|
sourceTimestamp: widget.message.sentAt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final currentPosition = LocationTrackingService().currentPosition;
|
||||||
|
if (currentPosition != null) {
|
||||||
|
return MessageContactLocation(
|
||||||
|
location: LatLng(
|
||||||
|
currentPosition.latitude,
|
||||||
|
currentPosition.longitude,
|
||||||
|
),
|
||||||
|
source: 'shared',
|
||||||
|
capturedAt: currentPosition.timestamp,
|
||||||
|
sourceTimestamp: currentPosition.timestamp,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final contactLocation = senderContact?.displayLocation;
|
||||||
|
if (contactLocation != null) {
|
||||||
|
final source =
|
||||||
|
senderContact?.telemetry?.gpsLocation != null
|
||||||
|
? 'telemetry'
|
||||||
|
: 'advert';
|
||||||
|
return MessageContactLocation(
|
||||||
|
location: contactLocation,
|
||||||
|
source: source,
|
||||||
|
capturedAt: senderContact?.locationUpdateTime ?? widget.message.receivedAt,
|
||||||
|
sourceTimestamp: senderContact?.locationUpdateTime,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
Widget _techSection(
|
Widget _techSection(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
required IconData icon,
|
required IconData icon,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_map/flutter_map.dart' as flutter_map;
|
import 'package:flutter_map/flutter_map.dart' as flutter_map;
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:geolocator/geolocator.dart';
|
||||||
import 'package:meshcore_sar_app/l10n/app_localizations.dart';
|
import 'package:meshcore_sar_app/l10n/app_localizations.dart';
|
||||||
import 'package:meshcore_sar_app/models/message.dart';
|
import 'package:meshcore_sar_app/models/message.dart';
|
||||||
import 'package:meshcore_sar_app/models/message_contact_location.dart';
|
import 'package:meshcore_sar_app/models/message_contact_location.dart';
|
||||||
@@ -14,6 +15,7 @@ import 'package:meshcore_sar_app/providers/drawing_provider.dart';
|
|||||||
import 'package:meshcore_sar_app/providers/image_provider.dart' as ip;
|
import 'package:meshcore_sar_app/providers/image_provider.dart' as ip;
|
||||||
import 'package:meshcore_sar_app/providers/messages_provider.dart';
|
import 'package:meshcore_sar_app/providers/messages_provider.dart';
|
||||||
import 'package:meshcore_sar_app/providers/voice_provider.dart';
|
import 'package:meshcore_sar_app/providers/voice_provider.dart';
|
||||||
|
import 'package:meshcore_sar_app/services/location_tracking_service.dart';
|
||||||
import 'package:meshcore_sar_app/services/voice_codec_service.dart';
|
import 'package:meshcore_sar_app/services/voice_codec_service.dart';
|
||||||
import 'package:meshcore_sar_app/services/voice_player_service.dart';
|
import 'package:meshcore_sar_app/services/voice_player_service.dart';
|
||||||
import 'package:meshcore_sar_app/widgets/messages/message_bubble.dart';
|
import 'package:meshcore_sar_app/widgets/messages/message_bubble.dart';
|
||||||
@@ -28,6 +30,7 @@ void main() {
|
|||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
SharedPreferences.setMockInitialValues({});
|
SharedPreferences.setMockInitialValues({});
|
||||||
|
LocationTrackingService().currentPosition = null;
|
||||||
launchedUrls.clear();
|
launchedUrls.clear();
|
||||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||||
.setMockMethodCallHandler(
|
.setMockMethodCallHandler(
|
||||||
@@ -405,11 +408,60 @@ void main() {
|
|||||||
|
|
||||||
expect(find.byType(flutter_map.FlutterMap), findsOneWidget);
|
expect(find.byType(flutter_map.FlutterMap), findsOneWidget);
|
||||||
expect(find.text('46.056900, 14.505800'), findsOneWidget);
|
expect(find.text('46.056900, 14.505800'), findsOneWidget);
|
||||||
expect(find.text('advert'), findsOneWidget);
|
expect(find.text('shared advert'), findsOneWidget);
|
||||||
} finally {
|
} finally {
|
||||||
await _disposeHarness(tester, harness);
|
await _disposeHarness(tester, harness);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'technical details show a location map for sent channel messages using live position fallback',
|
||||||
|
(tester) async {
|
||||||
|
final harness = await _TestHarness.create();
|
||||||
|
try {
|
||||||
|
LocationTrackingService().currentPosition = Position(
|
||||||
|
latitude: 46.0569,
|
||||||
|
longitude: 14.5058,
|
||||||
|
timestamp: DateTime.fromMillisecondsSinceEpoch(1700000003000),
|
||||||
|
accuracy: 6.0,
|
||||||
|
altitude: 300.0,
|
||||||
|
altitudeAccuracy: 6.0,
|
||||||
|
heading: 0.0,
|
||||||
|
headingAccuracy: 0.0,
|
||||||
|
speed: 0.0,
|
||||||
|
speedAccuracy: 0.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
final message = Message(
|
||||||
|
id: 'channel-live-fallback',
|
||||||
|
messageType: MessageType.channel,
|
||||||
|
senderPublicKeyPrefix: _prefix(81),
|
||||||
|
channelIdx: 0,
|
||||||
|
pathLen: 0,
|
||||||
|
textType: MessageTextType.plain,
|
||||||
|
senderTimestamp: 1700000000,
|
||||||
|
text: 'Channel fallback',
|
||||||
|
receivedAt: DateTime.fromMillisecondsSinceEpoch(1700000000500),
|
||||||
|
deliveryStatus: MessageDeliveryStatus.sent,
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(_buildApp(harness, message));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
await tester.longPress(find.text('Channel fallback'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
await tester.tap(find.text('Technical details'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.byType(flutter_map.FlutterMap), findsOneWidget);
|
||||||
|
expect(find.text('46.056900, 14.505800'), findsOneWidget);
|
||||||
|
expect(find.text('shared location'), findsOneWidget);
|
||||||
|
} finally {
|
||||||
|
await _disposeHarness(tester, harness);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildApp(_TestHarness harness, Message message) {
|
Widget _buildApp(_TestHarness harness, Message message) {
|
||||||
|
|||||||
Reference in New Issue
Block a user