@@ -1430,20 +1430,20 @@ abstract class _Banner extends StatelessWidget {
1430
1430
minimum: EdgeInsetsDirectional .only (start: 8 , end: padEnd ? 8 : 0 )
1431
1431
// (SafeArea.minimum doesn't take an EdgeInsetsDirectional)
1432
1432
.resolve (Directionality .of (context)),
1433
- child: Padding (
1434
- padding : const EdgeInsetsDirectional . fromSTEB ( 8 , 5 , 0 , 5 ),
1435
- child : Row (
1436
- children : [
1437
- Expanded (
1433
+ child: Row (
1434
+ children : [
1435
+ Expanded (
1436
+ child : Padding (
1437
+ padding : const EdgeInsetsDirectional . fromSTEB ( 8 , 5 , 0 , 5 ),
1438
1438
child: Padding (
1439
1439
padding: const EdgeInsets .symmetric (vertical: 4 ),
1440
1440
child: Text (style: labelTextStyle,
1441
- getLabel (zulipLocalizations)))),
1442
- if (trailing != null ) ...[
1443
- const SizedBox (width: 8 ),
1444
- trailing,
1445
- ],
1446
- ]) )));
1441
+ getLabel (zulipLocalizations))))) ,
1442
+ if (trailing != null ) ...[
1443
+ const SizedBox (width: 8 ),
1444
+ trailing,
1445
+ ],
1446
+ ] )));
1447
1447
}
1448
1448
}
1449
1449
@@ -1475,6 +1475,42 @@ class _ErrorBanner extends _Banner {
1475
1475
}
1476
1476
}
1477
1477
1478
+ class _WarningBanner extends _Banner {
1479
+ const _WarningBanner ({
1480
+ required this .label,
1481
+ required this .onDismiss,
1482
+ });
1483
+
1484
+ final String label;
1485
+ final VoidCallback ? onDismiss;
1486
+
1487
+ @override
1488
+ String getLabel (ZulipLocalizations zulipLocalizations) => label;
1489
+
1490
+ @override
1491
+ Color getLabelColor (DesignVariables designVariables) =>
1492
+ designVariables.btnLabelAttMediumIntWarning;
1493
+
1494
+ @override
1495
+ Color getBackgroundColor (DesignVariables designVariables) =>
1496
+ designVariables.bannerBgIntWarning;
1497
+
1498
+ @override
1499
+ bool get padEnd => false ;
1500
+
1501
+ @override
1502
+ Widget ? buildTrailing (BuildContext context) {
1503
+ final designVariables = DesignVariables .of (context);
1504
+ return InkWell (
1505
+ splashFactory: NoSplash .splashFactory,
1506
+ onTap: onDismiss,
1507
+ child: Padding (
1508
+ padding: const EdgeInsets .all (8.0 ),
1509
+ child: Icon (ZulipIcons .remove,
1510
+ size: 24 , color: designVariables.btnLabelAttLowIntWarning)));
1511
+ }
1512
+ }
1513
+
1478
1514
/// The compose box.
1479
1515
///
1480
1516
/// Takes the full screen width, covering the horizontal insets with its surface.
@@ -1512,6 +1548,8 @@ class _ComposeBoxState extends State<ComposeBox> with PerAccountStoreAwareStateM
1512
1548
@override ComposeBoxController get controller => _controller! ;
1513
1549
ComposeBoxController ? _controller;
1514
1550
1551
+ bool _isWarningBannerDismissed = false ;
1552
+
1515
1553
@override
1516
1554
void onNewStore () {
1517
1555
switch (widget.narrow) {
@@ -1538,6 +1576,12 @@ class _ComposeBoxState extends State<ComposeBox> with PerAccountStoreAwareStateM
1538
1576
super .dispose ();
1539
1577
}
1540
1578
1579
+ void _dismissWarningBanner () {
1580
+ setState (() {
1581
+ _isWarningBannerDismissed = true ;
1582
+ });
1583
+ }
1584
+
1541
1585
/// An [_ErrorBanner] that replaces the compose box's text inputs.
1542
1586
Widget ? _errorBannerComposingNotAllowed (BuildContext context) {
1543
1587
final store = PerAccountStoreWidget .of (context);
@@ -1567,11 +1611,62 @@ class _ComposeBoxState extends State<ComposeBox> with PerAccountStoreAwareStateM
1567
1611
return null ;
1568
1612
}
1569
1613
1614
+ /// A [_WarningBanner] that goes at the top of the compose box.
1615
+ Widget ? _warningBanner (BuildContext context) {
1616
+ if (_isWarningBannerDismissed) return null ;
1617
+
1618
+ final store = PerAccountStoreWidget .of (context);
1619
+ final zulipLocalizations = ZulipLocalizations .of (context);
1620
+
1621
+ if (store.connection.zulipFeatureLevel! < 348 ||
1622
+ ! store.realmEnableGuestUserDmWarning) {
1623
+ return null ;
1624
+ }
1625
+
1626
+ switch (widget.narrow) {
1627
+ case DmNarrow (: final otherRecipientIds):
1628
+ final guestUsers = otherRecipientIds
1629
+ .map ((id) => store.getUser (id))
1630
+ .where ((user) => user? .role == UserRole .guest)
1631
+ .toList ();
1632
+
1633
+ if (guestUsers.isEmpty) return null ;
1634
+
1635
+ final guestNames = guestUsers
1636
+ .map ((user) => user != null
1637
+ ? store.userDisplayName (user.userId)
1638
+ : zulipLocalizations.unknownUserName)
1639
+ .toList ();
1640
+
1641
+ final String formattedNames;
1642
+ if (guestUsers.length == 1 ) {
1643
+ formattedNames = guestNames[0 ];
1644
+ } else {
1645
+ final allButLast =
1646
+ guestNames.sublist (0 , guestNames.length - 1 ).join (', ' );
1647
+ formattedNames =
1648
+ "$allButLast ${guestUsers .length > 2 ? ',' : '' } and ${guestNames .last }" ;
1649
+ }
1650
+
1651
+ final bannerText = guestUsers.length == 1
1652
+ ? zulipLocalizations.guestUserDmWarningOne (guestNames.first)
1653
+ : zulipLocalizations.guestUserDmWarningMany (formattedNames);
1654
+
1655
+ return _WarningBanner (label: bannerText,
1656
+ onDismiss: _dismissWarningBanner);
1657
+
1658
+ default :
1659
+ return null ;
1660
+ }
1661
+ }
1662
+
1570
1663
@override
1571
1664
Widget build (BuildContext context) {
1572
1665
final Widget ? body;
1573
1666
1574
1667
final errorBanner = _errorBannerComposingNotAllowed (context);
1668
+ final warningBanner = _warningBanner (context);
1669
+
1575
1670
if (errorBanner != null ) {
1576
1671
return _ComposeBoxContainer (body: null , banner: errorBanner);
1577
1672
}
@@ -1594,6 +1689,6 @@ class _ComposeBoxState extends State<ComposeBox> with PerAccountStoreAwareStateM
1594
1689
// errorBanner = _ErrorBanner(label:
1595
1690
// ZulipLocalizations.of(context).errorSendMessageTimeout);
1596
1691
// }
1597
- return _ComposeBoxContainer (body: body, banner: null );
1692
+ return _ComposeBoxContainer (body: body, banner: warningBanner );
1598
1693
}
1599
1694
}
0 commit comments