@@ -276,16 +276,33 @@ class ComposeContentController extends ComposeController<ContentValidationError>
276
276
}
277
277
278
278
class _TopBar extends StatelessWidget {
279
- const _TopBar ({required this .showProgressIndicator});
279
+ const _TopBar ({required this .showProgressIndicator, required this .sendMessageError });
280
280
281
281
final bool showProgressIndicator;
282
+ final ValueNotifier <String ?> sendMessageError;
282
283
283
284
@override
284
285
Widget build (BuildContext context) {
286
+ final designVariables = DesignVariables .of (context);
287
+ final iconButtonTheme = IconButtonTheme .of (context);
285
288
// TODO: Figure out a way so that this does not shift the message list
286
289
// when it gains more height.
287
290
return Column (children: [
288
291
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 )),
289
306
]);
290
307
}
291
308
}
@@ -968,12 +985,14 @@ class _SendButton extends StatefulWidget {
968
985
required this .enabled,
969
986
required this .topicController,
970
987
required this .contentController,
988
+ required this .sendMessageError,
971
989
required this .getDestination,
972
990
});
973
991
974
992
final ValueNotifier <bool > enabled;
975
993
final ComposeTopicController ? topicController;
976
994
final ComposeContentController contentController;
995
+ final ValueNotifier <String ?> sendMessageError;
977
996
final MessageDestination Function () getDestination;
978
997
979
998
@override
@@ -1053,6 +1072,7 @@ class _SendButtonState extends State<_SendButton> {
1053
1072
.sendMessage (destination: widget.getDestination (), content: content)
1054
1073
.timeout (kSendMessageTimeout);
1055
1074
widget.contentController.clear ();
1075
+ widget.sendMessageError.value = null ;
1056
1076
} catch (e) {
1057
1077
if (! mounted) return ;
1058
1078
@@ -1064,9 +1084,7 @@ class _SendButtonState extends State<_SendButton> {
1064
1084
case TimeoutException (): message = zulipLocalizations.errorSendMessageTimeout;
1065
1085
default : rethrow ;
1066
1086
}
1067
- showErrorDialog (context: context,
1068
- title: zulipLocalizations.errorMessageNotSent,
1069
- message: message);
1087
+ widget.sendMessageError.value = message;
1070
1088
return ;
1071
1089
} finally {
1072
1090
widget.enabled.value = true ;
@@ -1197,6 +1215,7 @@ abstract class ComposeBoxController<T extends StatefulWidget> extends State<T> {
1197
1215
ComposeTopicController ? get topicController;
1198
1216
ComposeContentController get contentController;
1199
1217
FocusNode get contentFocusNode;
1218
+ ValueNotifier <String ?> get sendMessageError;
1200
1219
}
1201
1220
1202
1221
/// A compose box for use in a channel narrow.
@@ -1229,11 +1248,15 @@ class _StreamComposeBoxState extends State<_StreamComposeBox> implements Compose
1229
1248
FocusNode get topicFocusNode => _topicFocusNode;
1230
1249
final _topicFocusNode = FocusNode ();
1231
1250
1251
+ @override ValueNotifier <String ?> get sendMessageError => _sendMessageError;
1252
+ final _sendMessageError = ValueNotifier <String ?>(null );
1253
+
1232
1254
@override
1233
1255
void dispose () {
1234
1256
_topicController.dispose ();
1235
1257
_contentController.dispose ();
1236
1258
_contentFocusNode.dispose ();
1259
+ _sendMessageError.dispose ();
1237
1260
super .dispose ();
1238
1261
}
1239
1262
@@ -1243,7 +1266,10 @@ class _StreamComposeBoxState extends State<_StreamComposeBox> implements Compose
1243
1266
valueListenable: _enabled,
1244
1267
builder: (context, enabled, child) {
1245
1268
return _ComposeBoxLayout (
1246
- topBar: _TopBar (showProgressIndicator: ! enabled),
1269
+ topBar: _TopBar (
1270
+ showProgressIndicator: ! enabled,
1271
+ sendMessageError: sendMessageError,
1272
+ ),
1247
1273
topicInput: _TopicInput (
1248
1274
enabled: enabled,
1249
1275
streamId: widget.narrow.streamId,
@@ -1267,6 +1293,7 @@ class _StreamComposeBoxState extends State<_StreamComposeBox> implements Compose
1267
1293
enabled: _enabled,
1268
1294
topicController: _topicController,
1269
1295
contentController: _contentController,
1296
+ sendMessageError: _sendMessageError,
1270
1297
getDestination: () => StreamDestination (
1271
1298
widget.narrow.streamId, _topicController.textNormalized),
1272
1299
));
@@ -1275,9 +1302,10 @@ class _StreamComposeBoxState extends State<_StreamComposeBox> implements Compose
1275
1302
}
1276
1303
1277
1304
class _ErrorBanner extends StatelessWidget {
1278
- const _ErrorBanner ({required this .label});
1305
+ const _ErrorBanner ({required this .label, this .action });
1279
1306
1280
1307
final String label;
1308
+ final Widget ? action;
1281
1309
1282
1310
@override
1283
1311
Widget build (BuildContext context) {
@@ -1288,17 +1316,23 @@ class _ErrorBanner extends StatelessWidget {
1288
1316
color: designVariables.btnLabelAttMediumDanger,
1289
1317
).merge (weightVariableTextStyle (context, wght: 600 ));
1290
1318
1319
+ final padding = (action == null )
1320
+ // Ensure that the text is centered when it is the only element.
1321
+ ? const EdgeInsets .symmetric (horizontal: 16 , vertical: 9 )
1322
+ : const EdgeInsetsDirectional .fromSTEB (16 , 9 , 8 , 9 );
1323
+
1291
1324
return Container (
1292
1325
constraints: const BoxConstraints (minHeight: 40 ),
1293
- decoration: BoxDecoration (
1294
- color: designVariables.bannerBgIntDanger),
1326
+ decoration: BoxDecoration (color: designVariables.bannerBgIntDanger),
1295
1327
child: Row (
1296
1328
mainAxisAlignment: MainAxisAlignment .spaceBetween,
1329
+ crossAxisAlignment: CrossAxisAlignment .start,
1297
1330
children: [
1298
1331
Expanded (
1299
1332
child: Padding (
1300
- padding: const EdgeInsets . symmetric (horizontal : 16 , vertical : 9 ) ,
1333
+ padding: padding ,
1301
1334
child: Text (label, style: labelTextStyle))),
1335
+ if (action != null ) action! ,
1302
1336
]));
1303
1337
}
1304
1338
}
@@ -1324,10 +1358,14 @@ class _FixedDestinationComposeBoxState extends State<_FixedDestinationComposeBox
1324
1358
@override FocusNode get contentFocusNode => _contentFocusNode;
1325
1359
final _contentFocusNode = FocusNode ();
1326
1360
1361
+ @override ValueNotifier <String ?> get sendMessageError => _sendMessageError;
1362
+ final _sendMessageError = ValueNotifier <String ?>(null );
1363
+
1327
1364
@override
1328
1365
void dispose () {
1329
1366
_contentController.dispose ();
1330
1367
_contentFocusNode.dispose ();
1368
+ _sendMessageError.dispose ();
1331
1369
super .dispose ();
1332
1370
}
1333
1371
@@ -1337,7 +1375,10 @@ class _FixedDestinationComposeBoxState extends State<_FixedDestinationComposeBox
1337
1375
valueListenable: _enabled,
1338
1376
builder: (context, enabled, child) {
1339
1377
return _ComposeBoxLayout (
1340
- topBar: _TopBar (showProgressIndicator: ! enabled),
1378
+ topBar: _TopBar (
1379
+ showProgressIndicator: ! enabled,
1380
+ sendMessageError: sendMessageError,
1381
+ ),
1341
1382
topicInput: null ,
1342
1383
contentInput: _FixedDestinationContentInput (
1343
1384
enabled: enabled,
@@ -1354,6 +1395,7 @@ class _FixedDestinationComposeBoxState extends State<_FixedDestinationComposeBox
1354
1395
enabled: _enabled,
1355
1396
topicController: null ,
1356
1397
contentController: _contentController,
1398
+ sendMessageError: _sendMessageError,
1357
1399
getDestination: () => widget.narrow.destination,
1358
1400
));
1359
1401
});
0 commit comments