Add fragment transfer estimate

This commit is contained in:
Janez T
2026-03-05 09:38:39 +01:00
parent 55a85659d3
commit 76b093685e
4 changed files with 77 additions and 2 deletions

View File

@@ -60,6 +60,9 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
builder: (context, imageProvider, _) {
final session = imageProvider.session(envelope.sessionId);
final isComplete = imageProvider.isComplete(envelope.sessionId);
final eta = imageProvider.estimateRemainingTransferTime(
envelope.sessionId,
);
if (_isRequesting && isComplete) {
WidgetsBinding.instance.addPostFrameCallback((_) {
@@ -112,6 +115,7 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
radioCr: radioCr,
error: _errorText,
isSentByMe: widget.isSentByMe,
eta: eta,
),
style: TextStyle(
fontSize: 11,
@@ -374,6 +378,7 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
required int? radioCr,
required String? error,
required bool isSentByMe,
required Duration? eta,
}) {
final txEstimate = estimateImageTransmitDuration(
fragmentCount: envelope.total,
@@ -386,7 +391,10 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
final txEstimateLabel = _formatTransmitEstimate(txEstimate);
if (error != null) return error;
if (isRequesting) return '📥 Loading… $received/$total · $txEstimateLabel';
if (isRequesting) {
final etaLabel = _formatEta(eta);
return '📥 Loading… $received/$total · $etaLabel · $txEstimateLabel';
}
if (isComplete) {
final base =
'🖼️ ${envelope.width}×${envelope.height} ${envelope.format.label}';
@@ -404,6 +412,14 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
return '~${minutes}m ${seconds}s tx';
}
static String _formatEta(Duration? eta) {
if (eta == null || eta <= Duration.zero) return 'ETA --';
if (eta.inSeconds < 60) return 'ETA ~${eta.inSeconds}s';
final minutes = eta.inMinutes;
final seconds = eta.inSeconds % 60;
return 'ETA ~${minutes}m ${seconds}s';
}
void _showFullScreen(BuildContext context, Uint8List imageBytes) {
showGeneralDialog<void>(
context: context,

View File

@@ -102,6 +102,7 @@ class _VoiceMessageBubbleState extends State<VoiceMessageBubble> {
radioCr: radioCr,
);
final txEstimateLabel = _formatTransmitEstimate(txEstimate);
final eta = voiceProvider.estimateRemainingTransferTime(voiceId);
return Row(
mainAxisSize: MainAxisSize.min,
@@ -175,6 +176,7 @@ class _VoiceMessageBubbleState extends State<VoiceMessageBubble> {
requestingLabel: AppLocalizations.of(
context,
)!.requestingVoice,
eta: eta,
),
style: TextStyle(
fontSize: 11,
@@ -378,11 +380,12 @@ class _VoiceMessageBubbleState extends State<VoiceMessageBubble> {
required bool isRequesting,
required String? errorText,
required String requestingLabel,
required Duration? eta,
}) {
if (errorText != null) return errorText;
final progress = total > 0 ? ' ($received/$total)' : '';
if (isRequesting) {
return '$requestingLabel$progress · $txEstimateLabel';
return '$requestingLabel$progress · ${_formatEta(eta)} · $txEstimateLabel';
}
if (!isComplete && total > 0) {
return '🎙️ $durationLabel · $modeLabel$progress · $txEstimateLabel';
@@ -462,6 +465,14 @@ class _VoiceMessageBubbleState extends State<VoiceMessageBubble> {
final seconds = value.inSeconds % 60;
return '~${minutes}m ${seconds}s tx';
}
static String _formatEta(Duration? eta) {
if (eta == null || eta <= Duration.zero) return 'ETA --';
if (eta.inSeconds < 60) return 'ETA ~${eta.inSeconds}s';
final minutes = eta.inMinutes;
final seconds = eta.inSeconds % 60;
return 'ETA ~${minutes}m ${seconds}s';
}
}
/// Voice waveform rendered as a row of bars.