@@ -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) {
@@ -1291,17 +1319,23 @@ class _ErrorBanner extends StatelessWidget {
1291
1319
// which is a variable equivalent to this value.
1292
1320
wght: 600 ));
1293
1321
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
+
1294
1327
return Container (
1295
1328
constraints: const BoxConstraints (minHeight: 40 ),
1296
- decoration: BoxDecoration (
1297
- color: designVariables.bannerBgIntDanger),
1329
+ decoration: BoxDecoration (color: designVariables.bannerBgIntDanger),
1298
1330
child: Row (
1299
1331
mainAxisAlignment: MainAxisAlignment .spaceBetween,
1332
+ crossAxisAlignment: CrossAxisAlignment .start,
1300
1333
children: [
1301
1334
Expanded (
1302
1335
child: Padding (
1303
- padding: const EdgeInsets . symmetric (horizontal : 16 , vertical : 9 ) ,
1336
+ padding: padding ,
1304
1337
child: Text (label, style: labelTextStyle))),
1338
+ if (action != null ) action! ,
1305
1339
]));
1306
1340
}
1307
1341
}
@@ -1327,10 +1361,14 @@ class _FixedDestinationComposeBoxState extends State<_FixedDestinationComposeBox
1327
1361
@override FocusNode get contentFocusNode => _contentFocusNode;
1328
1362
final _contentFocusNode = FocusNode ();
1329
1363
1364
+ @override ValueNotifier <String ?> get sendMessageError => _sendMessageError;
1365
+ final _sendMessageError = ValueNotifier <String ?>(null );
1366
+
1330
1367
@override
1331
1368
void dispose () {
1332
1369
_contentController.dispose ();
1333
1370
_contentFocusNode.dispose ();
1371
+ _sendMessageError.dispose ();
1334
1372
super .dispose ();
1335
1373
}
1336
1374
@@ -1340,7 +1378,10 @@ class _FixedDestinationComposeBoxState extends State<_FixedDestinationComposeBox
1340
1378
valueListenable: _enabled,
1341
1379
builder: (context, enabled, child) {
1342
1380
return _ComposeBoxLayout (
1343
- topBar: _TopBar (showProgressIndicator: ! enabled),
1381
+ topBar: _TopBar (
1382
+ showProgressIndicator: ! enabled,
1383
+ sendMessageError: sendMessageError,
1384
+ ),
1344
1385
topicInput: null ,
1345
1386
contentInput: _FixedDestinationContentInput (
1346
1387
enabled: enabled,
@@ -1357,6 +1398,7 @@ class _FixedDestinationComposeBoxState extends State<_FixedDestinationComposeBox
1357
1398
enabled: _enabled,
1358
1399
topicController: null ,
1359
1400
contentController: _contentController,
1401
+ sendMessageError: _sendMessageError,
1360
1402
getDestination: () => widget.narrow.destination,
1361
1403
));
1362
1404
});
0 commit comments