mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
fix: remove all auto-retry image fetch, user-initiated only
- No automatic IR1 requests; fragments arrive only when user taps load - Bubble sends IR1 with missing indices only if partial session exists (partial resume: only missing fragments requested, not the whole image) - 30s timeout resets the loading spinner so user can tap retry again - Saves LoRa airtime by never transmitting IR1 without explicit user action
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_avif/flutter_avif.dart';
|
||||
@@ -31,6 +32,13 @@ class ImageMessageBubble extends StatefulWidget {
|
||||
class _ImageMessageBubbleState extends State<ImageMessageBubble> {
|
||||
bool _isRequesting = false;
|
||||
String? _errorText;
|
||||
Timer? _requestTimeoutTimer;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_requestTimeoutTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -176,8 +184,6 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
|
||||
var sender = _resolveSender(envelope);
|
||||
if (sender == null) {
|
||||
final conn = context.read<ConnectionProvider>();
|
||||
// Retry once after refreshing contacts; resumable sessions may outlive
|
||||
// the in-memory contact cache.
|
||||
await conn.getContacts();
|
||||
if (!mounted) return;
|
||||
sender = _resolveSender(envelope);
|
||||
@@ -188,6 +194,7 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
|
||||
}
|
||||
|
||||
final conn = context.read<ConnectionProvider>();
|
||||
final imageProvider = context.read<ip.ImageProvider>();
|
||||
final deviceKey = conn.deviceInfo.publicKey;
|
||||
if (deviceKey == null || deviceKey.length < 6) {
|
||||
setState(() => _errorText = 'Device key unavailable');
|
||||
@@ -198,11 +205,24 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
|
||||
.sublist(0, 6)
|
||||
.map((b) => b.toRadixString(16).padLeft(2, '0'))
|
||||
.join();
|
||||
final request = ImageFetchRequest(
|
||||
sessionId: envelope.sessionId,
|
||||
requesterKey6: requesterKey6,
|
||||
timestampSec: DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||
);
|
||||
|
||||
// If we already have some fragments, request only what's missing.
|
||||
final missing = imageProvider.missingFragmentIndices(envelope.sessionId);
|
||||
final isPartialResume = missing.isNotEmpty &&
|
||||
missing.length < envelope.total;
|
||||
final request = isPartialResume
|
||||
? ImageFetchRequest(
|
||||
sessionId: envelope.sessionId,
|
||||
want: 'missing',
|
||||
missingIndices: missing,
|
||||
requesterKey6: requesterKey6,
|
||||
timestampSec: DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||
)
|
||||
: ImageFetchRequest(
|
||||
sessionId: envelope.sessionId,
|
||||
requesterKey6: requesterKey6,
|
||||
timestampSec: DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||
);
|
||||
|
||||
setState(() {
|
||||
_isRequesting = true;
|
||||
@@ -219,7 +239,16 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
|
||||
_isRequesting = false;
|
||||
_errorText = 'Image unavailable right now';
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset after 30s so user can retry if transfer stalls.
|
||||
_requestTimeoutTimer?.cancel();
|
||||
_requestTimeoutTimer = Timer(const Duration(seconds: 30), () {
|
||||
if (mounted && _isRequesting && !imageProvider.isComplete(envelope.sessionId)) {
|
||||
setState(() => _isRequesting = false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Contact? _resolveSender(ImageEnvelope envelope) {
|
||||
|
||||
Reference in New Issue
Block a user