mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
Update iOS project version
This commit is contained in:
@@ -489,7 +489,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 90;
|
CURRENT_PROJECT_VERSION = 92;
|
||||||
DEVELOPMENT_TEAM = JND55328G8;
|
DEVELOPMENT_TEAM = JND55328G8;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@@ -511,7 +511,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 90;
|
CURRENT_PROJECT_VERSION = 92;
|
||||||
DEVELOPMENT_TEAM = JND55328G8;
|
DEVELOPMENT_TEAM = JND55328G8;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
@@ -530,7 +530,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 90;
|
CURRENT_PROJECT_VERSION = 92;
|
||||||
DEVELOPMENT_TEAM = JND55328G8;
|
DEVELOPMENT_TEAM = JND55328G8;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
@@ -547,7 +547,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 90;
|
CURRENT_PROJECT_VERSION = 92;
|
||||||
DEVELOPMENT_TEAM = JND55328G8;
|
DEVELOPMENT_TEAM = JND55328G8;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
@@ -679,7 +679,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 90;
|
CURRENT_PROJECT_VERSION = 92;
|
||||||
DEVELOPMENT_TEAM = JND55328G8;
|
DEVELOPMENT_TEAM = JND55328G8;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@@ -702,7 +702,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 90;
|
CURRENT_PROJECT_VERSION = 92;
|
||||||
DEVELOPMENT_TEAM = JND55328G8;
|
DEVELOPMENT_TEAM = JND55328G8;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>90</string>
|
<string>92</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>NSBluetoothAlwaysUsageDescription</key>
|
<key>NSBluetoothAlwaysUsageDescription</key>
|
||||||
|
|||||||
@@ -5,22 +5,22 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000191">
|
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000216">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="1: increment_build_number" time="0.4516">
|
<testcase classname="fastlane.lanes" name="1: increment_build_number" time="0.451844">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="2: build_app" time="111.029845">
|
<testcase classname="fastlane.lanes" name="2: build_app" time="110.228647">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="3: upload_to_app_store" time="49.313568">
|
<testcase classname="fastlane.lanes" name="3: upload_to_app_store" time="475.510001">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|||||||
@@ -1041,6 +1041,32 @@ class AppProvider with ChangeNotifier {
|
|||||||
retryAttempt: retryAttempt,
|
retryAttempt: retryAttempt,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
messagesProvider.onDirectPathFailedCallback =
|
||||||
|
({required contact, required failureStreak}) async {
|
||||||
|
debugPrint(
|
||||||
|
'🧭 [AppProvider] Clearing unhealthy path for ${contact.advName} after $failureStreak failed send chain(s)',
|
||||||
|
);
|
||||||
|
|
||||||
|
contactsProvider.markPathUnhealthy(contact.publicKey);
|
||||||
|
|
||||||
|
if (!connectionProvider.deviceInfo.isConnected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await connectionProvider.resetPath(contact.publicKey);
|
||||||
|
Future.delayed(const Duration(milliseconds: 150), () {
|
||||||
|
if (connectionProvider.deviceInfo.isConnected) {
|
||||||
|
connectionProvider.getContact(contact.publicKey);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint(
|
||||||
|
'⚠️ [AppProvider] Failed to reset path for ${contact.advName}: $e',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize the app (load contacts, sync time, etc.)
|
/// Initialize the app (load contacts, sync time, etc.)
|
||||||
|
|||||||
@@ -517,6 +517,22 @@ class ContactsProvider with ChangeNotifier {
|
|||||||
return _contacts[keyHex];
|
return _contacts[keyHex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clear a contact's learned path locally so the UI and next send both
|
||||||
|
/// prefer flood routing until the radio reports a fresh route.
|
||||||
|
void markPathUnhealthy(Uint8List publicKey) {
|
||||||
|
final contact = findContactByKey(publicKey);
|
||||||
|
if (contact == null || !contact.hasPath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_contacts[contact.publicKeyHex] = contact.copyWith(
|
||||||
|
outPathLen: -1,
|
||||||
|
outPath: Uint8List(0),
|
||||||
|
);
|
||||||
|
_persistContacts();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
/// Add or refresh a pending advert entry from PUSH_CODE_ADVERT (0x80).
|
/// Add or refresh a pending advert entry from PUSH_CODE_ADVERT (0x80).
|
||||||
/// Excludes self key and existing contacts.
|
/// Excludes self key and existing contacts.
|
||||||
void addPendingAdvert(Uint8List publicKey, {Uint8List? devicePublicKey}) {
|
void addPendingAdvert(Uint8List publicKey, {Uint8List? devicePublicKey}) {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class MessageRetryManager {
|
|||||||
// Track retry state for each message ID
|
// Track retry state for each message ID
|
||||||
final Map<String, int> _retryAttempts = {};
|
final Map<String, int> _retryAttempts = {};
|
||||||
final Map<String, DateTime> _lastRetryTimes = {};
|
final Map<String, DateTime> _lastRetryTimes = {};
|
||||||
|
final Map<String, int> _pathFailureStreaks = {};
|
||||||
|
|
||||||
// Progressive timeout values in milliseconds
|
// Progressive timeout values in milliseconds
|
||||||
// These are app-level timeouts, separate from firmware's suggested timeout
|
// These are app-level timeouts, separate from firmware's suggested timeout
|
||||||
@@ -120,6 +121,7 @@ class MessageRetryManager {
|
|||||||
void clearAll() {
|
void clearAll() {
|
||||||
_retryAttempts.clear();
|
_retryAttempts.clear();
|
||||||
_lastRetryTimes.clear();
|
_lastRetryTimes.clear();
|
||||||
|
_pathFailureStreaks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get current retry attempt for a message (for debugging)
|
/// Get current retry attempt for a message (for debugging)
|
||||||
@@ -132,6 +134,27 @@ class MessageRetryManager {
|
|||||||
return _lastRetryTimes[messageId];
|
return _lastRetryTimes[messageId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Record a successful delivery for a contact and clear any accumulated
|
||||||
|
/// route failure streak for future sends.
|
||||||
|
void recordDeliverySuccess(Contact contact) {
|
||||||
|
_pathFailureStreaks.remove(contact.publicKeyHex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Record a permanent route failure for a contact.
|
||||||
|
///
|
||||||
|
/// Returns the updated failure streak so callers can decide when to reset
|
||||||
|
/// the learned path on the radio and in local state.
|
||||||
|
int recordPathFailure(Contact contact) {
|
||||||
|
final contactKey = contact.publicKeyHex;
|
||||||
|
final next = (_pathFailureStreaks[contactKey] ?? 0) + 1;
|
||||||
|
_pathFailureStreaks[contactKey] = next;
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
int? getPathFailureStreak(Contact contact) {
|
||||||
|
return _pathFailureStreaks[contact.publicKeyHex];
|
||||||
|
}
|
||||||
|
|
||||||
int _estimateLoRaAirtimeMs(int payloadLenBytes) {
|
int _estimateLoRaAirtimeMs(int payloadLenBytes) {
|
||||||
final sf = _defaultLoRaSf;
|
final sf = _defaultLoRaSf;
|
||||||
final bw = _defaultLoRaBwHz.toDouble();
|
final bw = _defaultLoRaBwHz.toDouble();
|
||||||
|
|||||||
@@ -74,6 +74,12 @@ class MessagesProvider with ChangeNotifier {
|
|||||||
})?
|
})?
|
||||||
sendMessageCallback;
|
sendMessageCallback;
|
||||||
|
|
||||||
|
Future<void> Function({
|
||||||
|
required Contact contact,
|
||||||
|
required int failureStreak,
|
||||||
|
})?
|
||||||
|
onDirectPathFailedCallback;
|
||||||
|
|
||||||
List<Message> get messages => List.unmodifiable(_messages);
|
List<Message> get messages => List.unmodifiable(_messages);
|
||||||
|
|
||||||
List<Message> get contactMessages =>
|
List<Message> get contactMessages =>
|
||||||
@@ -1388,6 +1394,10 @@ class MessagesProvider with ChangeNotifier {
|
|||||||
|
|
||||||
// Clear retry tracking on successful delivery
|
// Clear retry tracking on successful delivery
|
||||||
_retryManager.clearRetry(message.id);
|
_retryManager.clearRetry(message.id);
|
||||||
|
final deliveredContact = _messageContactMap[message.id];
|
||||||
|
if (deliveredContact != null) {
|
||||||
|
_retryManager.recordDeliverySuccess(deliveredContact);
|
||||||
|
}
|
||||||
|
|
||||||
debugPrint(
|
debugPrint(
|
||||||
'✅ [MessagesProvider] Message ${message.id} delivered in ${roundTripTimeMs}ms (ACK $ackCode)',
|
'✅ [MessagesProvider] Message ${message.id} delivered in ${roundTripTimeMs}ms (ACK $ackCode)',
|
||||||
@@ -1427,6 +1437,10 @@ class MessagesProvider with ChangeNotifier {
|
|||||||
_rememberCompletedAck(ackCode);
|
_rememberCompletedAck(ackCode);
|
||||||
_clearAckHistoryForMessage(historicalMessageId);
|
_clearAckHistoryForMessage(historicalMessageId);
|
||||||
_retryManager.clearRetry(historicalMessageId);
|
_retryManager.clearRetry(historicalMessageId);
|
||||||
|
final deliveredContact = _messageContactMap[historicalMessageId];
|
||||||
|
if (deliveredContact != null) {
|
||||||
|
_retryManager.recordDeliverySuccess(deliveredContact);
|
||||||
|
}
|
||||||
_persistMessages();
|
_persistMessages();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
debugPrint(
|
debugPrint(
|
||||||
@@ -1663,6 +1677,22 @@ class MessagesProvider with ChangeNotifier {
|
|||||||
// Clear retry tracking
|
// Clear retry tracking
|
||||||
_retryManager.clearRetry(messageId);
|
_retryManager.clearRetry(messageId);
|
||||||
|
|
||||||
|
final failedContact = _messageContactMap[messageId];
|
||||||
|
if (failedContact != null && failedContact.hasPath) {
|
||||||
|
final failureStreak = _retryManager.recordPathFailure(failedContact);
|
||||||
|
debugPrint(
|
||||||
|
' Path failure streak for ${failedContact.advName}: $failureStreak',
|
||||||
|
);
|
||||||
|
if (failureStreak >= 2 && onDirectPathFailedCallback != null) {
|
||||||
|
unawaited(
|
||||||
|
onDirectPathFailedCallback!(
|
||||||
|
contact: failedContact,
|
||||||
|
failureStreak: failureStreak,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_persistMessages();
|
_persistMessages();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -574,6 +574,9 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
: null);
|
: null);
|
||||||
final rssiDbm =
|
final rssiDbm =
|
||||||
matchedRxLog?.logRxDataInfo?.rssiDbm ?? widget.message.lastEchoRssiDbm;
|
matchedRxLog?.logRxDataInfo?.rssiDbm ?? widget.message.lastEchoRssiDbm;
|
||||||
|
final retryCause = _retryCauseLabel(widget.message);
|
||||||
|
final retryResult = _retryResultLabel(widget.message);
|
||||||
|
final retryMode = _retryModeLabel(widget.message);
|
||||||
|
|
||||||
final rawLines = <String>[
|
final rawLines = <String>[
|
||||||
'Message ID: ${widget.message.id}',
|
'Message ID: ${widget.message.id}',
|
||||||
@@ -598,6 +601,9 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
'Round-trip ms: ${widget.message.roundTripTimeMs ?? '-'}',
|
'Round-trip ms: ${widget.message.roundTripTimeMs ?? '-'}',
|
||||||
'Retry attempt: ${widget.message.retryAttempt}',
|
'Retry attempt: ${widget.message.retryAttempt}',
|
||||||
'Used flood fallback: ${widget.message.usedFloodFallback}',
|
'Used flood fallback: ${widget.message.usedFloodFallback}',
|
||||||
|
'Retry cause: ${retryCause ?? '-'}',
|
||||||
|
'Retry mode: ${retryMode ?? '-'}',
|
||||||
|
'Retry result: ${retryResult ?? '-'}',
|
||||||
'Sender key prefix: ${senderPrefixHex ?? '-'}',
|
'Sender key prefix: ${senderPrefixHex ?? '-'}',
|
||||||
'Sender name: ${senderName ?? widget.message.senderName ?? '-'}',
|
'Sender name: ${senderName ?? widget.message.senderName ?? '-'}',
|
||||||
'Sender location at receipt: ${senderLocationSnapshot?.formattedCoordinates ?? '-'}',
|
'Sender location at receipt: ${senderLocationSnapshot?.formattedCoordinates ?? '-'}',
|
||||||
@@ -848,6 +854,18 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
value:
|
value:
|
||||||
'${widget.message.suggestedTimeoutMs} ms',
|
'${widget.message.suggestedTimeoutMs} ms',
|
||||||
),
|
),
|
||||||
|
if (retryCause != null)
|
||||||
|
_detailRow(
|
||||||
|
context,
|
||||||
|
label: 'Retry cause',
|
||||||
|
value: retryCause,
|
||||||
|
),
|
||||||
|
if (retryMode != null)
|
||||||
|
_detailRow(
|
||||||
|
context,
|
||||||
|
label: 'Retry mode',
|
||||||
|
value: retryMode,
|
||||||
|
),
|
||||||
if (widget.message.roundTripTimeMs != null)
|
if (widget.message.roundTripTimeMs != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
context,
|
context,
|
||||||
@@ -877,6 +895,12 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
label: l10n.floodFallback,
|
label: l10n.floodFallback,
|
||||||
value: l10n.yes,
|
value: l10n.yes,
|
||||||
),
|
),
|
||||||
|
if (retryResult != null)
|
||||||
|
_detailRow(
|
||||||
|
context,
|
||||||
|
label: 'Retry result',
|
||||||
|
value: retryResult,
|
||||||
|
),
|
||||||
if (packetPathHex != null)
|
if (packetPathHex != null)
|
||||||
_detailRow(
|
_detailRow(
|
||||||
context,
|
context,
|
||||||
@@ -1718,6 +1742,85 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
return _hopDisplayLabel(message);
|
return _hopDisplayLabel(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String? _retryCauseLabel(Message message) {
|
||||||
|
if (!message.isContactMessage || message.expectedAckTag == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.deliveryStatus == MessageDeliveryStatus.sending &&
|
||||||
|
message.retryAttempt == 0) {
|
||||||
|
return 'Waiting for delivery ACK';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.retryAttempt > 0 || message.usedFloodFallback) {
|
||||||
|
return 'Delivery ACK timeout';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.deliveryStatus == MessageDeliveryStatus.failed) {
|
||||||
|
return 'Delivery confirmation not received';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _retryModeLabel(Message message) {
|
||||||
|
if (!message.isContactMessage) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.usedFloodFallback) {
|
||||||
|
return 'Flood fallback';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.retryAttempt > 0 || message.expectedAckTag != null) {
|
||||||
|
return 'Learned direct path';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _retryResultLabel(Message message) {
|
||||||
|
if (!message.isContactMessage) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.deliveryStatus == MessageDeliveryStatus.delivered) {
|
||||||
|
if (message.usedFloodFallback) {
|
||||||
|
return 'Delivered after flood fallback';
|
||||||
|
}
|
||||||
|
if (message.retryAttempt > 0) {
|
||||||
|
return 'Delivered after retry';
|
||||||
|
}
|
||||||
|
if (message.expectedAckTag != null) {
|
||||||
|
return 'Delivery confirmed';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.deliveryStatus == MessageDeliveryStatus.sending) {
|
||||||
|
if (message.usedFloodFallback) {
|
||||||
|
return 'Flood fallback in progress';
|
||||||
|
}
|
||||||
|
if (message.retryAttempt > 0) {
|
||||||
|
return 'Retry in progress';
|
||||||
|
}
|
||||||
|
if (message.expectedAckTag != null) {
|
||||||
|
return 'Awaiting confirmation';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.deliveryStatus == MessageDeliveryStatus.failed) {
|
||||||
|
if (message.usedFloodFallback) {
|
||||||
|
return 'Failed after flood fallback';
|
||||||
|
}
|
||||||
|
if (message.retryAttempt > 0) {
|
||||||
|
return 'Failed after retry attempts';
|
||||||
|
}
|
||||||
|
return 'Delivery failed';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
Widget _techChip(
|
Widget _techChip(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
required IconData icon,
|
required IconData icon,
|
||||||
|
|||||||
@@ -161,5 +161,76 @@ void main() {
|
|||||||
);
|
);
|
||||||
expect(provider.messages.single.roundTripTimeMs, 220);
|
expect(provider.messages.single.roundTripTimeMs, 220);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('repeated max-retry failures request path reset', () async {
|
||||||
|
final provider = MessagesProvider();
|
||||||
|
final contact = _buildContact();
|
||||||
|
final resetRequests = <(String, int)>[];
|
||||||
|
provider.onDirectPathFailedCallback =
|
||||||
|
({required contact, required failureStreak}) async {
|
||||||
|
resetRequests.add((contact.advName, failureStreak));
|
||||||
|
};
|
||||||
|
|
||||||
|
provider.addSentMessage(
|
||||||
|
_buildDirectMessage('m5').copyWith(
|
||||||
|
retryAttempt: 3,
|
||||||
|
usedFloodFallback: true,
|
||||||
|
),
|
||||||
|
contact: contact,
|
||||||
|
);
|
||||||
|
provider.markMessageFailed('m5');
|
||||||
|
|
||||||
|
provider.addSentMessage(
|
||||||
|
_buildDirectMessage('m6').copyWith(
|
||||||
|
retryAttempt: 3,
|
||||||
|
usedFloodFallback: true,
|
||||||
|
),
|
||||||
|
contact: contact,
|
||||||
|
);
|
||||||
|
provider.markMessageFailed('m6');
|
||||||
|
|
||||||
|
await Future<void>.delayed(Duration.zero);
|
||||||
|
|
||||||
|
expect(resetRequests, [('Teammate', 2)]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('successful delivery clears path failure streak', () async {
|
||||||
|
final provider = MessagesProvider();
|
||||||
|
final contact = _buildContact();
|
||||||
|
final resetRequests = <int>[];
|
||||||
|
provider.onDirectPathFailedCallback =
|
||||||
|
({required contact, required failureStreak}) async {
|
||||||
|
resetRequests.add(failureStreak);
|
||||||
|
};
|
||||||
|
|
||||||
|
provider.addSentMessage(
|
||||||
|
_buildDirectMessage('m7').copyWith(
|
||||||
|
retryAttempt: 3,
|
||||||
|
usedFloodFallback: true,
|
||||||
|
),
|
||||||
|
contact: contact,
|
||||||
|
);
|
||||||
|
provider.markMessageFailed('m7');
|
||||||
|
|
||||||
|
provider.addSentMessage(
|
||||||
|
_buildDirectMessage('m8'),
|
||||||
|
contact: contact,
|
||||||
|
);
|
||||||
|
provider.markMessageSent('m8', 123, 10);
|
||||||
|
provider.markMessageDelivered(123, 150);
|
||||||
|
|
||||||
|
provider.addSentMessage(
|
||||||
|
_buildDirectMessage('m9').copyWith(
|
||||||
|
retryAttempt: 3,
|
||||||
|
usedFloodFallback: true,
|
||||||
|
),
|
||||||
|
contact: contact,
|
||||||
|
);
|
||||||
|
provider.markMessageFailed('m9');
|
||||||
|
|
||||||
|
await Future<void>.delayed(Duration.zero);
|
||||||
|
|
||||||
|
expect(resetRequests, isEmpty);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user