Skip to content

Commit 6863d9f

Browse files
committed
compose: Add timeout to send message requests
Signed-off-by: Zixuan James Li <[email protected]>
1 parent 6697b4b commit 6863d9f

File tree

7 files changed

+53
-6
lines changed

7 files changed

+53
-6
lines changed

assets/l10n/app_en.arb

+4
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@
169169
"@errorQuotationFailed": {
170170
"description": "Error message when quoting a message failed."
171171
},
172+
"errorSendMessageTimeout": "Message isn’t sent. Check your connection.",
173+
"@errorSendMessageTimeout": {
174+
"description": "Error message when failed to send a message due to timeout."
175+
},
172176
"errorServerMessage": "The server said:\n\n{message}",
173177
"@errorServerMessage": {
174178
"description": "Error message that quotes an error from the server.",

lib/generated/l10n/zulip_localizations.dart

+6
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,12 @@ abstract class ZulipLocalizations {
325325
/// **'Quotation failed'**
326326
String get errorQuotationFailed;
327327

328+
/// Error message when failed to send a message due to timeout.
329+
///
330+
/// In en, this message translates to:
331+
/// **'Message isn’t sent. Check your connection.'**
332+
String get errorSendMessageTimeout;
333+
328334
/// Error message that quotes an error from the server.
329335
///
330336
/// In en, this message translates to:

lib/generated/l10n/zulip_localizations_ar.dart

+3
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ class ZulipLocalizationsAr extends ZulipLocalizations {
144144
@override
145145
String get errorQuotationFailed => 'Quotation failed';
146146

147+
@override
148+
String get errorSendMessageTimeout => 'Message isn’t sent. Check your connection.';
149+
147150
@override
148151
String errorServerMessage(String message) {
149152
return 'The server said:\n\n$message';

lib/generated/l10n/zulip_localizations_en.dart

+3
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ class ZulipLocalizationsEn extends ZulipLocalizations {
144144
@override
145145
String get errorQuotationFailed => 'Quotation failed';
146146

147+
@override
148+
String get errorSendMessageTimeout => 'Message isn’t sent. Check your connection.';
149+
147150
@override
148151
String errorServerMessage(String message) {
149152
return 'The server said:\n\n$message';

lib/generated/l10n/zulip_localizations_ja.dart

+3
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ class ZulipLocalizationsJa extends ZulipLocalizations {
144144
@override
145145
String get errorQuotationFailed => 'Quotation failed';
146146

147+
@override
148+
String get errorSendMessageTimeout => 'Message isn’t sent. Check your connection.';
149+
147150
@override
148151
String errorServerMessage(String message) {
149152
return 'The server said:\n\n$message';

lib/widgets/compose_box.dart

+15-6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'dart:async';
12
import 'dart:math';
23

34
import 'package:app_settings/app_settings.dart';
@@ -22,6 +23,8 @@ import 'store.dart';
2223
import 'text.dart';
2324
import 'theme.dart';
2425

26+
const Duration kSendMessageTimeout = Duration(seconds: 5);
27+
2528
const double _composeButtonSize = 44;
2629

2730
/// A [TextEditingController] for use in the compose box.
@@ -1046,15 +1049,21 @@ class _SendButtonState extends State<_SendButton> {
10461049
widget.enabled.value = false;
10471050

10481051
try {
1049-
await store.sendMessage(destination: widget.getDestination(), content: content);
1052+
await store
1053+
.sendMessage(destination: widget.getDestination(), content: content)
1054+
.timeout(kSendMessageTimeout);
10501055
widget.contentController.clear();
1051-
} on ApiRequestException catch (e) {
1056+
} catch (e) {
10521057
if (!mounted) return;
1058+
10531059
final zulipLocalizations = ZulipLocalizations.of(context);
1054-
final message = switch (e) {
1055-
ZulipApiException() => zulipLocalizations.errorServerMessage(e.message),
1056-
_ => e.message,
1057-
};
1060+
String message;
1061+
switch (e) {
1062+
case ZulipApiException(): message = zulipLocalizations.errorServerMessage(e.message);
1063+
case ApiRequestException(): message = e.message;
1064+
case TimeoutException(): message = zulipLocalizations.errorSendMessageTimeout;
1065+
default: rethrow;
1066+
}
10581067
showErrorDialog(context: context,
10591068
title: zulipLocalizations.errorMessageNotSent,
10601069
message: message);

test/widgets/compose_box_test.dart

+19
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,25 @@ void main() {
476476
check(composeBoxController.contentController.text).equals(oldText);
477477
});
478478

479+
testWidgets('fail after timeout', (tester) async {
480+
const longDelay = Duration(hours: 1);
481+
assert(longDelay > kSendMessageTimeout);
482+
await setupAndTapSend(tester, prepareResponse: (_) {
483+
connection.prepare(
484+
httpStatus: 400,
485+
json: {'result': 'error', 'code': 'BAD_REQUEST'},
486+
delay: longDelay);
487+
});
488+
489+
await tester.pump(kSendMessageTimeout);
490+
final zulipLocalizations = GlobalLocalizations.zulipLocalizations;
491+
await tester.tap(find.byWidget(checkErrorDialog(tester,
492+
expectedTitle: zulipLocalizations.errorMessageNotSent,
493+
expectedMessage: zulipLocalizations.errorSendMessageTimeout)));
494+
495+
await tester.pump(longDelay);
496+
});
497+
479498
testWidgets('ZulipApiException', (tester) async {
480499
await setupAndTapSend(tester, prepareResponse: (message) {
481500
connection.prepare(

0 commit comments

Comments
 (0)