diff --git a/lib/widgets/compose_box.dart b/lib/widgets/compose_box.dart index c80c5dd26a..80751bcf8b 100644 --- a/lib/widgets/compose_box.dart +++ b/lib/widgets/compose_box.dart @@ -5,6 +5,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/zulip_localizations.dart'; import 'package:image_picker/image_picker.dart'; +import '../api/exception.dart'; import '../api/model/model.dart'; import '../api/route/messages.dart'; import '../model/compose.dart'; @@ -716,7 +717,7 @@ class _SendButtonState extends State<_SendButton> { || widget.contentController.hasValidationErrors.value; } - void _send() { + void _send() async { if (_hasValidationErrors) { final zulipLocalizations = ZulipLocalizations.of(context); List validationErrorMessages = [ @@ -735,7 +736,23 @@ class _SendButtonState extends State<_SendButton> { final store = PerAccountStoreWidget.of(context); final content = widget.contentController.textNormalized; - store.sendMessage(destination: widget.getDestination(), content: content); + + try { + // TODO(#720) put input(s) and send button into a disabled "working on it" + // state (allowing input text to be selected for copying). + await store.sendMessage(destination: widget.getDestination(), content: content); + } on ApiRequestException catch (e) { + if (!mounted) return; + final zulipLocalizations = ZulipLocalizations.of(context); + final message = switch (e) { + ZulipApiException() => zulipLocalizations.errorServerMessage(e.message), + _ => e.message, + }; + showErrorDialog(context: context, + title: zulipLocalizations.errorMessageNotSent, + message: message); + return; + } widget.contentController.clear(); } diff --git a/test/widgets/compose_box_test.dart b/test/widgets/compose_box_test.dart index ecf35e4c7c..c953354369 100644 --- a/test/widgets/compose_box_test.dart +++ b/test/widgets/compose_box_test.dart @@ -16,6 +16,7 @@ import '../example_data.dart' as eg; import '../flutter_checks.dart'; import '../model/binding.dart'; import '../stdlib_checks.dart'; +import 'dialog_checks.dart'; void main() { TestZulipBinding.ensureInitialized(); @@ -225,5 +226,23 @@ void main() { final errorDialogs = tester.widgetList(find.byType(AlertDialog)); check(errorDialogs).isEmpty(); }); + + testWidgets('ZulipApiException', (tester) async { + await setupAndTapSend(tester, prepareResponse: (message) { + connection.prepare( + httpStatus: 400, + json: { + 'result': 'error', + 'code': 'BAD_REQUEST', + 'msg': 'You do not have permission to initiate direct message conversations.', + }); + }); + final zulipLocalizations = GlobalLocalizations.zulipLocalizations; + await tester.tap(find.byWidget(checkErrorDialog(tester, + expectedTitle: zulipLocalizations.errorMessageNotSent, + expectedMessage: zulipLocalizations.errorServerMessage( + 'You do not have permission to initiate direct message conversations.'), + ))); + }); }); }