@@ -276,16 +276,33 @@ class ComposeContentController extends ComposeController<ContentValidationError>
276276}
277277
278278class _TopBar extends StatelessWidget {
279- const _TopBar ({required this .showProgressIndicator});
279+ const _TopBar ({required this .showProgressIndicator, required this .sendMessageError });
280280
281281 final bool showProgressIndicator;
282+ final ValueNotifier <String ?> sendMessageError;
282283
283284 @override
284285 Widget build (BuildContext context) {
286+ final designVariables = DesignVariables .of (context);
287+ final iconButtonTheme = IconButtonTheme .of (context);
285288 // TODO: Figure out a way so that this does not shift the message list
286289 // when it gains more height.
287290 return Column (children: [
288291 if (showProgressIndicator) _progressIndicator (context),
292+ ValueListenableBuilder (
293+ valueListenable: sendMessageError,
294+ builder: (context, errorMessage, child) {
295+ if (errorMessage != null ) {
296+ return _ErrorBanner (label: errorMessage, action: child);
297+ }
298+ return const SizedBox .shrink ();
299+ },
300+ child: IconButton (
301+ style: iconButtonTheme.style! .copyWith (
302+ shape: const WidgetStatePropertyAll (ContinuousRectangleBorder (
303+ borderRadius: BorderRadius .all (Radius .circular (4 ))))),
304+ icon: Icon (ZulipIcons .remove, color: designVariables.btnLabelAttLowIntDanger),
305+ onPressed: () => sendMessageError.value = null )),
289306 ]);
290307 }
291308}
@@ -968,12 +985,14 @@ class _SendButton extends StatefulWidget {
968985 required this .enabled,
969986 required this .topicController,
970987 required this .contentController,
988+ required this .sendMessageError,
971989 required this .getDestination,
972990 });
973991
974992 final ValueNotifier <bool > enabled;
975993 final ComposeTopicController ? topicController;
976994 final ComposeContentController contentController;
995+ final ValueNotifier <String ?> sendMessageError;
977996 final MessageDestination Function () getDestination;
978997
979998 @override
@@ -1053,6 +1072,7 @@ class _SendButtonState extends State<_SendButton> {
10531072 .sendMessage (destination: widget.getDestination (), content: content)
10541073 .timeout (kSendMessageTimeout);
10551074 widget.contentController.clear ();
1075+ widget.sendMessageError.value = null ;
10561076 } catch (e) {
10571077 if (! mounted) return ;
10581078
@@ -1064,9 +1084,7 @@ class _SendButtonState extends State<_SendButton> {
10641084 case TimeoutException (): message = zulipLocalizations.errorSendMessageTimeout;
10651085 default : rethrow ;
10661086 }
1067- showErrorDialog (context: context,
1068- title: zulipLocalizations.errorMessageNotSent,
1069- message: message);
1087+ widget.sendMessageError.value = message;
10701088 return ;
10711089 } finally {
10721090 widget.enabled.value = true ;
@@ -1197,6 +1215,7 @@ abstract class ComposeBoxController<T extends StatefulWidget> extends State<T> {
11971215 ComposeTopicController ? get topicController;
11981216 ComposeContentController get contentController;
11991217 FocusNode get contentFocusNode;
1218+ ValueNotifier <String ?> get sendMessageError;
12001219}
12011220
12021221/// A compose box for use in a channel narrow.
@@ -1229,11 +1248,15 @@ class _StreamComposeBoxState extends State<_StreamComposeBox> implements Compose
12291248 FocusNode get topicFocusNode => _topicFocusNode;
12301249 final _topicFocusNode = FocusNode ();
12311250
1251+ @override ValueNotifier <String ?> get sendMessageError => _sendMessageError;
1252+ final _sendMessageError = ValueNotifier <String ?>(null );
1253+
12321254 @override
12331255 void dispose () {
12341256 _topicController.dispose ();
12351257 _contentController.dispose ();
12361258 _contentFocusNode.dispose ();
1259+ _sendMessageError.dispose ();
12371260 super .dispose ();
12381261 }
12391262
@@ -1243,7 +1266,10 @@ class _StreamComposeBoxState extends State<_StreamComposeBox> implements Compose
12431266 valueListenable: _enabled,
12441267 builder: (context, enabled, child) {
12451268 return _ComposeBoxLayout (
1246- topBar: _TopBar (showProgressIndicator: ! enabled),
1269+ topBar: _TopBar (
1270+ showProgressIndicator: ! enabled,
1271+ sendMessageError: sendMessageError,
1272+ ),
12471273 topicInput: _TopicInput (
12481274 enabled: enabled,
12491275 streamId: widget.narrow.streamId,
@@ -1267,6 +1293,7 @@ class _StreamComposeBoxState extends State<_StreamComposeBox> implements Compose
12671293 enabled: _enabled,
12681294 topicController: _topicController,
12691295 contentController: _contentController,
1296+ sendMessageError: _sendMessageError,
12701297 getDestination: () => StreamDestination (
12711298 widget.narrow.streamId, _topicController.textNormalized),
12721299 ));
@@ -1275,9 +1302,10 @@ class _StreamComposeBoxState extends State<_StreamComposeBox> implements Compose
12751302}
12761303
12771304class _ErrorBanner extends StatelessWidget {
1278- const _ErrorBanner ({required this .label});
1305+ const _ErrorBanner ({required this .label, this .action });
12791306
12801307 final String label;
1308+ final Widget ? action;
12811309
12821310 @override
12831311 Widget build (BuildContext context) {
@@ -1291,17 +1319,23 @@ class _ErrorBanner extends StatelessWidget {
12911319 // which is a variable equivalent to this value.
12921320 wght: 600 ));
12931321
1322+ final padding = (action == null )
1323+ // Ensure that the text is centered when it is the only element.
1324+ ? const EdgeInsets .symmetric (horizontal: 16 , vertical: 9 )
1325+ : const EdgeInsetsDirectional .fromSTEB (16 , 9 , 8 , 9 );
1326+
12941327 return Container (
12951328 constraints: const BoxConstraints (minHeight: 40 ),
1296- decoration: BoxDecoration (
1297- color: designVariables.bannerBgIntDanger),
1329+ decoration: BoxDecoration (color: designVariables.bannerBgIntDanger),
12981330 child: Row (
12991331 mainAxisAlignment: MainAxisAlignment .spaceBetween,
1332+ crossAxisAlignment: CrossAxisAlignment .start,
13001333 children: [
13011334 Expanded (
13021335 child: Padding (
1303- padding: const EdgeInsets . symmetric (horizontal : 16 , vertical : 9 ) ,
1336+ padding: padding ,
13041337 child: Text (label, style: labelTextStyle))),
1338+ if (action != null ) action! ,
13051339 ]));
13061340 }
13071341}
@@ -1327,10 +1361,14 @@ class _FixedDestinationComposeBoxState extends State<_FixedDestinationComposeBox
13271361 @override FocusNode get contentFocusNode => _contentFocusNode;
13281362 final _contentFocusNode = FocusNode ();
13291363
1364+ @override ValueNotifier <String ?> get sendMessageError => _sendMessageError;
1365+ final _sendMessageError = ValueNotifier <String ?>(null );
1366+
13301367 @override
13311368 void dispose () {
13321369 _contentController.dispose ();
13331370 _contentFocusNode.dispose ();
1371+ _sendMessageError.dispose ();
13341372 super .dispose ();
13351373 }
13361374
@@ -1340,7 +1378,10 @@ class _FixedDestinationComposeBoxState extends State<_FixedDestinationComposeBox
13401378 valueListenable: _enabled,
13411379 builder: (context, enabled, child) {
13421380 return _ComposeBoxLayout (
1343- topBar: _TopBar (showProgressIndicator: ! enabled),
1381+ topBar: _TopBar (
1382+ showProgressIndicator: ! enabled,
1383+ sendMessageError: sendMessageError,
1384+ ),
13441385 topicInput: null ,
13451386 contentInput: _FixedDestinationContentInput (
13461387 enabled: enabled,
@@ -1357,6 +1398,7 @@ class _FixedDestinationComposeBoxState extends State<_FixedDestinationComposeBox
13571398 enabled: _enabled,
13581399 topicController: null ,
13591400 contentController: _contentController,
1401+ sendMessageError: _sendMessageError,
13601402 getDestination: () => widget.narrow.destination,
13611403 ));
13621404 });
0 commit comments