Skip to content

Commit b3edf00

Browse files
committed
compose: Implement guest user DM warning banner
1 parent 1ee4f1f commit b3edf00

11 files changed

+214
-1
lines changed

assets/l10n/app_en.arb

+15
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,21 @@
415415
"others": {"type": "String", "example": "Alice, Bob"}
416416
}
417417
},
418+
"guestUserDmWarningOne": "{guestUser} is a guest in this organization.",
419+
"@guestUserDmWarningOne": {
420+
"description": "Warning shown when composing a DM to one guest user",
421+
"placeholders": {
422+
"guestUser": {"type": "String", "example": "Alice"}
423+
}
424+
},
425+
426+
"guestUserDmWarningMany": "{guestUsers} are guests in this organization.",
427+
"@guestUserDmWarningMany": {
428+
"description": "Warning shown when composing DMs to multiple guest users",
429+
"placeholders": {
430+
"guestUsers": {"type": "String", "example": "Alice, Bob, and Charlie"}
431+
}
432+
},
418433
"messageListGroupYouWithYourself": "Messages with yourself",
419434
"@messageListGroupYouWithYourself": {
420435
"description": "Message list recipient header for a DM group that only includes yourself."

lib/generated/l10n/zulip_localizations.dart

+12
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,18 @@ abstract class ZulipLocalizations {
647647
/// **'DMs with {others}'**
648648
String dmsWithOthersPageTitle(String others);
649649

650+
/// Warning shown when composing a DM to one guest user
651+
///
652+
/// In en, this message translates to:
653+
/// **'{guestUser} is a guest in this organization.'**
654+
String guestUserDmWarningOne(String guestUser);
655+
656+
/// Warning shown when composing DMs to multiple guest users
657+
///
658+
/// In en, this message translates to:
659+
/// **'{guestUsers} are guests in this organization.'**
660+
String guestUserDmWarningMany(String guestUsers);
661+
650662
/// Message list recipient header for a DM group that only includes yourself.
651663
///
652664
/// In en, this message translates to:

lib/generated/l10n/zulip_localizations_ar.dart

+10
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,16 @@ class ZulipLocalizationsAr extends ZulipLocalizations {
324324
return 'DMs with $others';
325325
}
326326

327+
@override
328+
String guestUserDmWarningOne(String guestUser) {
329+
return '$guestUser is a guest in this organization.';
330+
}
331+
332+
@override
333+
String guestUserDmWarningMany(String guestUsers) {
334+
return '$guestUsers are guests in this organization.';
335+
}
336+
327337
@override
328338
String get messageListGroupYouWithYourself => 'Messages with yourself';
329339

lib/generated/l10n/zulip_localizations_en.dart

+10
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,16 @@ class ZulipLocalizationsEn extends ZulipLocalizations {
324324
return 'DMs with $others';
325325
}
326326

327+
@override
328+
String guestUserDmWarningOne(String guestUser) {
329+
return '$guestUser is a guest in this organization.';
330+
}
331+
332+
@override
333+
String guestUserDmWarningMany(String guestUsers) {
334+
return '$guestUsers are guests in this organization.';
335+
}
336+
327337
@override
328338
String get messageListGroupYouWithYourself => 'Messages with yourself';
329339

lib/generated/l10n/zulip_localizations_ja.dart

+10
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,16 @@ class ZulipLocalizationsJa extends ZulipLocalizations {
324324
return 'DMs with $others';
325325
}
326326

327+
@override
328+
String guestUserDmWarningOne(String guestUser) {
329+
return '$guestUser is a guest in this organization.';
330+
}
331+
332+
@override
333+
String guestUserDmWarningMany(String guestUsers) {
334+
return '$guestUsers are guests in this organization.';
335+
}
336+
327337
@override
328338
String get messageListGroupYouWithYourself => 'Messages with yourself';
329339

lib/generated/l10n/zulip_localizations_nb.dart

+10
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,16 @@ class ZulipLocalizationsNb extends ZulipLocalizations {
324324
return 'DMs with $others';
325325
}
326326

327+
@override
328+
String guestUserDmWarningOne(String guestUser) {
329+
return '$guestUser is a guest in this organization.';
330+
}
331+
332+
@override
333+
String guestUserDmWarningMany(String guestUsers) {
334+
return '$guestUsers are guests in this organization.';
335+
}
336+
327337
@override
328338
String get messageListGroupYouWithYourself => 'Messages with yourself';
329339

lib/generated/l10n/zulip_localizations_pl.dart

+10
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,16 @@ class ZulipLocalizationsPl extends ZulipLocalizations {
324324
return 'DM z $others';
325325
}
326326

327+
@override
328+
String guestUserDmWarningOne(String guestUser) {
329+
return '$guestUser is a guest in this organization.';
330+
}
331+
332+
@override
333+
String guestUserDmWarningMany(String guestUsers) {
334+
return '$guestUsers are guests in this organization.';
335+
}
336+
327337
@override
328338
String get messageListGroupYouWithYourself => 'Zapiski na własne konto';
329339

lib/generated/l10n/zulip_localizations_ru.dart

+10
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,16 @@ class ZulipLocalizationsRu extends ZulipLocalizations {
324324
return 'ЛС с $others';
325325
}
326326

327+
@override
328+
String guestUserDmWarningOne(String guestUser) {
329+
return '$guestUser is a guest in this organization.';
330+
}
331+
332+
@override
333+
String guestUserDmWarningMany(String guestUsers) {
334+
return '$guestUsers are guests in this organization.';
335+
}
336+
327337
@override
328338
String get messageListGroupYouWithYourself => 'Сообщения с собой';
329339

lib/generated/l10n/zulip_localizations_sk.dart

+10
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,16 @@ class ZulipLocalizationsSk extends ZulipLocalizations {
324324
return 'DMs with $others';
325325
}
326326

327+
@override
328+
String guestUserDmWarningOne(String guestUser) {
329+
return '$guestUser is a guest in this organization.';
330+
}
331+
332+
@override
333+
String guestUserDmWarningMany(String guestUsers) {
334+
return '$guestUsers are guests in this organization.';
335+
}
336+
327337
@override
328338
String get messageListGroupYouWithYourself => 'Messages with yourself';
329339

lib/widgets/compose_box.dart

+96-1
Original file line numberDiff line numberDiff line change
@@ -1484,6 +1484,42 @@ class _ErrorBanner extends _Banner {
14841484
}
14851485
}
14861486

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+
14871523
/// The compose box.
14881524
///
14891525
/// Takes the full screen width, covering the horizontal insets with its surface.
@@ -1521,6 +1557,8 @@ class _ComposeBoxState extends State<ComposeBox> with PerAccountStoreAwareStateM
15211557
@override ComposeBoxController get controller => _controller!;
15221558
ComposeBoxController? _controller;
15231559

1560+
bool _isWarningBannerDismissed = false;
1561+
15241562
@override
15251563
void onNewStore() {
15261564
switch (widget.narrow) {
@@ -1547,6 +1585,12 @@ class _ComposeBoxState extends State<ComposeBox> with PerAccountStoreAwareStateM
15471585
super.dispose();
15481586
}
15491587

1588+
void _dismissWarningBanner() {
1589+
setState(() {
1590+
_isWarningBannerDismissed = true;
1591+
});
1592+
}
1593+
15501594
/// An [_ErrorBanner] that replaces the compose box's text inputs.
15511595
Widget? _errorBannerComposingNotAllowed(BuildContext context) {
15521596
final store = PerAccountStoreWidget.of(context);
@@ -1576,11 +1620,62 @@ class _ComposeBoxState extends State<ComposeBox> with PerAccountStoreAwareStateM
15761620
return null;
15771621
}
15781622

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+
15791672
@override
15801673
Widget build(BuildContext context) {
15811674
final Widget? body;
15821675

15831676
final errorBanner = _errorBannerComposingNotAllowed(context);
1677+
final warningBanner = _warningBanner(context);
1678+
15841679
if (errorBanner != null) {
15851680
return _ComposeBoxContainer(body: null, banner: errorBanner);
15861681
}
@@ -1603,6 +1698,6 @@ class _ComposeBoxState extends State<ComposeBox> with PerAccountStoreAwareStateM
16031698
// errorBanner = _ErrorBanner(label:
16041699
// ZulipLocalizations.of(context).errorSendMessageTimeout);
16051700
// }
1606-
return _ComposeBoxContainer(body: body, banner: null);
1701+
return _ComposeBoxContainer(body: body, banner: warningBanner);
16071702
}
16081703
}

0 commit comments

Comments
 (0)