@@ -1484,6 +1484,42 @@ class _ErrorBanner extends _Banner {
1484
1484
}
1485
1485
}
1486
1486
1487
+ class _WarningBanner extends _Banner {
1488
+ const _WarningBanner ({
1489
+ required this .label,
1490
+ required this .onDismiss,
1491
+ });
1492
+
1493
+ final String label;
1494
+ final VoidCallback ? onDismiss;
1495
+
1496
+ @override
1497
+ String getLabel (ZulipLocalizations zulipLocalizations) => label;
1498
+
1499
+ @override
1500
+ Color getLabelColor (DesignVariables designVariables) =>
1501
+ designVariables.btnLabelAttMediumIntWarning;
1502
+
1503
+ @override
1504
+ Color getBackgroundColor (DesignVariables designVariables) =>
1505
+ designVariables.bannerBgIntWarning;
1506
+
1507
+ @override
1508
+ bool get padEnd => false ;
1509
+
1510
+ @override
1511
+ Widget ? buildTrailing (BuildContext context) {
1512
+ final designVariables = DesignVariables .of (context);
1513
+ return InkWell (
1514
+ splashFactory: NoSplash .splashFactory,
1515
+ onTap: onDismiss,
1516
+ child: Padding (
1517
+ padding: const EdgeInsets .all (8.0 ),
1518
+ child: Icon (ZulipIcons .remove,
1519
+ size: 24 , color: designVariables.btnLabelAttLowIntWarning)));
1520
+ }
1521
+ }
1522
+
1487
1523
/// The compose box.
1488
1524
///
1489
1525
/// Takes the full screen width, covering the horizontal insets with its surface.
@@ -1521,6 +1557,8 @@ class _ComposeBoxState extends State<ComposeBox> with PerAccountStoreAwareStateM
1521
1557
@override ComposeBoxController get controller => _controller! ;
1522
1558
ComposeBoxController ? _controller;
1523
1559
1560
+ bool _isWarningBannerDismissed = false ;
1561
+
1524
1562
@override
1525
1563
void onNewStore () {
1526
1564
switch (widget.narrow) {
@@ -1547,6 +1585,12 @@ class _ComposeBoxState extends State<ComposeBox> with PerAccountStoreAwareStateM
1547
1585
super .dispose ();
1548
1586
}
1549
1587
1588
+ void _dismissWarningBanner () {
1589
+ setState (() {
1590
+ _isWarningBannerDismissed = true ;
1591
+ });
1592
+ }
1593
+
1550
1594
/// An [_ErrorBanner] that replaces the compose box's text inputs.
1551
1595
Widget ? _errorBannerComposingNotAllowed (BuildContext context) {
1552
1596
final store = PerAccountStoreWidget .of (context);
@@ -1576,11 +1620,62 @@ class _ComposeBoxState extends State<ComposeBox> with PerAccountStoreAwareStateM
1576
1620
return null ;
1577
1621
}
1578
1622
1623
+ /// A [_WarningBanner] that goes at the top of the compose box.
1624
+ Widget ? _warningBanner (BuildContext context) {
1625
+ if (_isWarningBannerDismissed) return null ;
1626
+
1627
+ final store = PerAccountStoreWidget .of (context);
1628
+ final zulipLocalizations = ZulipLocalizations .of (context);
1629
+
1630
+ if (store.connection.zulipFeatureLevel! < 348 ||
1631
+ ! store.realmEnableGuestUserDmWarning) {
1632
+ return null ;
1633
+ }
1634
+
1635
+ switch (widget.narrow) {
1636
+ case DmNarrow (: final otherRecipientIds):
1637
+ final guestUsers = otherRecipientIds
1638
+ .map ((id) => store.getUser (id))
1639
+ .where ((user) => user? .role == UserRole .guest)
1640
+ .toList ();
1641
+
1642
+ if (guestUsers.isEmpty) return null ;
1643
+
1644
+ final guestNames = guestUsers
1645
+ .map ((user) => user != null
1646
+ ? store.userDisplayName (user.userId)
1647
+ : zulipLocalizations.unknownUserName)
1648
+ .toList ();
1649
+
1650
+ final String formattedNames;
1651
+ if (guestUsers.length == 1 ) {
1652
+ formattedNames = guestNames[0 ];
1653
+ } else {
1654
+ final allButLast =
1655
+ guestNames.sublist (0 , guestNames.length - 1 ).join (', ' );
1656
+ formattedNames =
1657
+ "$allButLast ${guestUsers .length > 2 ? ',' : '' } and ${guestNames .last }" ;
1658
+ }
1659
+
1660
+ final bannerText = guestUsers.length == 1
1661
+ ? zulipLocalizations.guestUserDmWarningOne (guestNames.first)
1662
+ : zulipLocalizations.guestUserDmWarningMany (formattedNames);
1663
+
1664
+ return _WarningBanner (label: bannerText,
1665
+ onDismiss: _dismissWarningBanner);
1666
+
1667
+ default :
1668
+ return null ;
1669
+ }
1670
+ }
1671
+
1579
1672
@override
1580
1673
Widget build (BuildContext context) {
1581
1674
final Widget ? body;
1582
1675
1583
1676
final errorBanner = _errorBannerComposingNotAllowed (context);
1677
+ final warningBanner = _warningBanner (context);
1678
+
1584
1679
if (errorBanner != null ) {
1585
1680
return _ComposeBoxContainer (body: null , banner: errorBanner);
1586
1681
}
@@ -1603,6 +1698,6 @@ class _ComposeBoxState extends State<ComposeBox> with PerAccountStoreAwareStateM
1603
1698
// errorBanner = _ErrorBanner(label:
1604
1699
// ZulipLocalizations.of(context).errorSendMessageTimeout);
1605
1700
// }
1606
- return _ComposeBoxContainer (body: body, banner: null );
1701
+ return _ComposeBoxContainer (body: body, banner: warningBanner );
1607
1702
}
1608
1703
}
0 commit comments