Skip to content

Commit 0dcbc89

Browse files
message-list : add a prompt to notify guest(s) when DM
Fixes zulip#798 showDMWarningBanner could be initialized in the initState to shouldShowGuestUserWarningPrompt(store) and Visibility wrapper could have been gotten rid off , but due to unavailability of store beforehand it was not possible. To create the guests list, I got rid off all the users in the store which are not in the recepients list and role-checked them.
1 parent 4f438d3 commit 0dcbc89

16 files changed

+197
-10
lines changed

assets/l10n/app_en.arb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,5 +831,13 @@
831831
"zulipAppTitle": "Zulip",
832832
"@zulipAppTitle": {
833833
"description": "The name of Zulip. This should be either 'Zulip' or a transliteration."
834+
},
835+
"bannerText": "{guestCount, plural, =1{{guestNamesList} is a guest in this organization.} other{{guestNamesList} are guests in this organization.}}",
836+
"@bannerText": {
837+
"description": "Message displaying guest names in the organization.",
838+
"placeholders": {
839+
"guestCount": {"type": "int", "example": "3"},
840+
"guestNamesList": {"type": "String", "example": "Alice, Bob, Charlie"}
841+
}
834842
}
835843
}

lib/api/model/initial_snapshot.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ class InitialSnapshot {
7979
/// https://zulip.com/api/roles-and-permissions#determining-if-a-user-is-a-full-member
8080
final int realmWaitingPeriodThreshold;
8181

82+
final bool? realmEnableGuestUserDmWarning;
83+
8284
final Map<String, RealmDefaultExternalAccount> realmDefaultExternalAccounts;
8385

8486
final int maxFileUploadSizeMib;
@@ -135,6 +137,7 @@ class InitialSnapshot {
135137
required this.realmWildcardMentionPolicy,
136138
required this.realmMandatoryTopics,
137139
required this.realmWaitingPeriodThreshold,
140+
required this.realmEnableGuestUserDmWarning,
138141
required this.realmDefaultExternalAccounts,
139142
required this.maxFileUploadSizeMib,
140143
required this.serverEmojiDataUrl,

lib/api/model/initial_snapshot.g.dart

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/generated/l10n/zulip_localizations.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,12 @@ abstract class ZulipLocalizations {
12141214
/// In en, this message translates to:
12151215
/// **'Zulip'**
12161216
String get zulipAppTitle;
1217+
1218+
/// Message displaying guest names in the organization.
1219+
///
1220+
/// In en, this message translates to:
1221+
/// **'{guestCount, plural, =1{{guestNamesList} is a guest in this organization.} other{{guestNamesList} are guests in this organization.}}'**
1222+
String bannerText(int guestCount, String guestNamesList);
12171223
}
12181224

12191225
class _ZulipLocalizationsDelegate extends LocalizationsDelegate<ZulipLocalizations> {

lib/generated/l10n/zulip_localizations_ar.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,4 +648,15 @@ class ZulipLocalizationsAr extends ZulipLocalizations {
648648

649649
@override
650650
String get zulipAppTitle => 'Zulip';
651+
652+
@override
653+
String bannerText(int guestCount, String guestNamesList) {
654+
String _temp0 = intl.Intl.pluralLogic(
655+
guestCount,
656+
locale: localeName,
657+
other: '$guestNamesList are guests in this organization.',
658+
one: '$guestNamesList is a guest in this organization.',
659+
);
660+
return '$_temp0';
661+
}
651662
}

lib/generated/l10n/zulip_localizations_en.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,4 +648,15 @@ class ZulipLocalizationsEn extends ZulipLocalizations {
648648

649649
@override
650650
String get zulipAppTitle => 'Zulip';
651+
652+
@override
653+
String bannerText(int guestCount, String guestNamesList) {
654+
String _temp0 = intl.Intl.pluralLogic(
655+
guestCount,
656+
locale: localeName,
657+
other: '$guestNamesList are guests in this organization.',
658+
one: '$guestNamesList is a guest in this organization.',
659+
);
660+
return '$_temp0';
661+
}
651662
}

lib/generated/l10n/zulip_localizations_ja.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,4 +648,15 @@ class ZulipLocalizationsJa extends ZulipLocalizations {
648648

649649
@override
650650
String get zulipAppTitle => 'Zulip';
651+
652+
@override
653+
String bannerText(int guestCount, String guestNamesList) {
654+
String _temp0 = intl.Intl.pluralLogic(
655+
guestCount,
656+
locale: localeName,
657+
other: '$guestNamesList are guests in this organization.',
658+
one: '$guestNamesList is a guest in this organization.',
659+
);
660+
return '$_temp0';
661+
}
651662
}

lib/generated/l10n/zulip_localizations_nb.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,4 +648,15 @@ class ZulipLocalizationsNb extends ZulipLocalizations {
648648

649649
@override
650650
String get zulipAppTitle => 'Zulip';
651+
652+
@override
653+
String bannerText(int guestCount, String guestNamesList) {
654+
String _temp0 = intl.Intl.pluralLogic(
655+
guestCount,
656+
locale: localeName,
657+
other: '$guestNamesList are guests in this organization.',
658+
one: '$guestNamesList is a guest in this organization.',
659+
);
660+
return '$_temp0';
661+
}
651662
}

lib/generated/l10n/zulip_localizations_pl.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,4 +648,15 @@ class ZulipLocalizationsPl extends ZulipLocalizations {
648648

649649
@override
650650
String get zulipAppTitle => 'Zulip';
651+
652+
@override
653+
String bannerText(int guestCount, String guestNamesList) {
654+
String _temp0 = intl.Intl.pluralLogic(
655+
guestCount,
656+
locale: localeName,
657+
other: '$guestNamesList are guests in this organization.',
658+
one: '$guestNamesList is a guest in this organization.',
659+
);
660+
return '$_temp0';
661+
}
651662
}

lib/generated/l10n/zulip_localizations_ru.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,4 +648,15 @@ class ZulipLocalizationsRu extends ZulipLocalizations {
648648

649649
@override
650650
String get zulipAppTitle => 'Zulip';
651+
652+
@override
653+
String bannerText(int guestCount, String guestNamesList) {
654+
String _temp0 = intl.Intl.pluralLogic(
655+
guestCount,
656+
locale: localeName,
657+
other: '$guestNamesList are guests in this organization.',
658+
one: '$guestNamesList is a guest in this organization.',
659+
);
660+
return '$_temp0';
661+
}
651662
}

lib/generated/l10n/zulip_localizations_sk.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,4 +648,15 @@ class ZulipLocalizationsSk extends ZulipLocalizations {
648648

649649
@override
650650
String get zulipAppTitle => 'Zulip';
651+
652+
@override
653+
String bannerText(int guestCount, String guestNamesList) {
654+
String _temp0 = intl.Intl.pluralLogic(
655+
guestCount,
656+
locale: localeName,
657+
other: '$guestNamesList are guests in this organization.',
658+
one: '$guestNamesList is a guest in this organization.',
659+
);
660+
return '$_temp0';
661+
}
651662
}

lib/model/store.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ class PerAccountStore extends ChangeNotifier with EmojiStore, ChannelStore, Mess
335335
recentDmConversationsView: RecentDmConversationsView(
336336
initial: initialSnapshot.recentPrivateConversations, selfUserId: account.userId),
337337
recentSenders: RecentSenders(),
338+
realmEnableGuestUserDmWarning: initialSnapshot.realmEnableGuestUserDmWarning,
338339
);
339340
}
340341

@@ -361,6 +362,7 @@ class PerAccountStore extends ChangeNotifier with EmojiStore, ChannelStore, Mess
361362
required this.unreads,
362363
required this.recentDmConversationsView,
363364
required this.recentSenders,
365+
required this.realmEnableGuestUserDmWarning,
364366
}) : assert(selfUserId == globalStore.getAccount(accountId)!.userId),
365367
assert(realmUrl == globalStore.getAccount(accountId)!.realmUrl),
366368
assert(realmUrl == connection.realmUrl),
@@ -567,6 +569,8 @@ class PerAccountStore extends ChangeNotifier with EmojiStore, ChannelStore, Mess
567569

568570
final AutocompleteViewManager autocompleteViewManager = AutocompleteViewManager();
569571

572+
final bool? realmEnableGuestUserDmWarning;
573+
570574
// End of data.
571575
////////////////////////////////////////////////////////////////
572576

lib/widgets/message_list.dart

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ abstract class MessageListPageState {
183183
///
184184
/// This is null if [MessageList] has not mounted yet.
185185
MessageListView? get model;
186+
187+
bool showDMWarningBanner = true;
186188
}
187189

188190
class MessageListPage extends StatefulWidget {
@@ -224,10 +226,14 @@ class _MessageListPageState extends State<MessageListPage> implements MessageLis
224226
MessageListView? get model => _messageListKey.currentState?.model;
225227
final GlobalKey<_MessageListState> _messageListKey = GlobalKey();
226228

229+
@override
230+
late bool showDMWarningBanner;
231+
227232
@override
228233
void initState() {
229234
super.initState();
230235
narrow = widget.initNarrow;
236+
showDMWarningBanner = narrow is DmNarrow;
231237
}
232238

233239
void _narrowChanged(Narrow newNarrow) {
@@ -241,6 +247,7 @@ class _MessageListPageState extends State<MessageListPage> implements MessageLis
241247
final store = PerAccountStoreWidget.of(context);
242248
final messageListTheme = MessageListTheme.of(context);
243249
final zulipLocalizations = ZulipLocalizations.of(context);
250+
final designVariables= DesignVariables.of(context);
244251

245252
final Color? appBarBackgroundColor;
246253
bool removeAppBarBottomBorder = false;
@@ -318,10 +325,72 @@ class _MessageListPageState extends State<MessageListPage> implements MessageLis
318325
narrow: narrow,
319326
onNarrowChanged: _narrowChanged,
320327
))),
328+
if(shouldShowGuestUserWarningPrompt(store))
329+
showGuestUserWarningPrompt(narrow as DmNarrow, store, designVariables, zulipLocalizations),
321330
if (ComposeBox.hasComposeBox(narrow))
322331
ComposeBox(key: _composeBoxKey, narrow: narrow)
323332
]))));
324333
}
334+
335+
bool shouldShowGuestUserWarningPrompt(PerAccountStore store) =>
336+
store.connection.zulipFeatureLevel! >= 348 &&
337+
narrow is DmNarrow && (store.realmEnableGuestUserDmWarning ?? false);
338+
339+
Widget showGuestUserWarningPrompt(
340+
DmNarrow narrow,
341+
PerAccountStore store,
342+
DesignVariables designVariables,
343+
ZulipLocalizations zulipLocalizations,
344+
) {
345+
final recipients = narrow.otherRecipientIds;
346+
final allUsersInStore = Map<int, User>.from(store.users);
347+
allUsersInStore.removeWhere((userId, user) => !recipients.contains(userId));
348+
final guestNames =
349+
allUsersInStore.values
350+
.where((user) => user.role == UserRole.guest)
351+
.map((e) => e.fullName)
352+
.toList();
353+
354+
if(guestNames.isEmpty){
355+
return SizedBox();
356+
}
357+
358+
return Visibility(
359+
visible: showDMWarningBanner,
360+
child: Align(
361+
alignment: Alignment.centerLeft,
362+
child: Container(
363+
width: MediaQuery.of(context).size.width,
364+
decoration: BoxDecoration(
365+
borderRadius: BorderRadius.circular(4),
366+
border: Border.all(color: designVariables.dmUserWarningBannerBorder,),
367+
color: designVariables.dmUserWarningBanner,
368+
),
369+
margin: EdgeInsets.all(4),
370+
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 8),
371+
child: Row(
372+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
373+
children: [
374+
Flexible(
375+
child: Text(
376+
zulipLocalizations.bannerText(guestNames.length, guestNames.join(', ')),
377+
style: TextStyle(color: Colors.white),
378+
),
379+
),
380+
GestureDetector(
381+
onTap: () {
382+
setState(() {
383+
showDMWarningBanner= false;
384+
});
385+
},
386+
child: Icon(Icons.close, color: Colors.white),
387+
),
388+
],
389+
),
390+
),
391+
),
392+
);
393+
}
325394
}
326395

327396
class MessageListAppBarTitle extends StatelessWidget {

lib/widgets/theme.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
167167
subscriptionListHeaderLine: const HSLColor.fromAHSL(0.2, 240, 0.1, 0.5).toColor(),
168168
subscriptionListHeaderText: const HSLColor.fromAHSL(1.0, 240, 0.1, 0.5).toColor(),
169169
unreadCountBadgeTextForChannel: Colors.black.withValues(alpha: 0.9),
170+
dmUserWarningBanner: Color.fromARGB(255, 133, 118, 71),
171+
dmUserWarningBannerBorder: Color.fromARGB(255, 127, 111, 60),
170172
);
171173

172174
static final dark = DesignVariables._(
@@ -224,6 +226,8 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
224226
// TODO(design-dark) need proper dark-theme color (this is ad hoc)
225227
subscriptionListHeaderText: const HSLColor.fromAHSL(1.0, 240, 0.1, 0.75).toColor(),
226228
unreadCountBadgeTextForChannel: Colors.white.withValues(alpha: 0.9),
229+
dmUserWarningBanner: Color.fromARGB(255, 133, 118, 71),
230+
dmUserWarningBannerBorder: Color.fromARGB(255, 127, 111, 60),
227231
);
228232

229233
DesignVariables._({
@@ -273,6 +277,8 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
273277
required this.subscriptionListHeaderLine,
274278
required this.subscriptionListHeaderText,
275279
required this.unreadCountBadgeTextForChannel,
280+
required this.dmUserWarningBanner,
281+
required this.dmUserWarningBannerBorder,
276282
});
277283

278284
/// The [DesignVariables] from the context's active theme.
@@ -335,6 +341,8 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
335341
final Color subscriptionListHeaderLine;
336342
final Color subscriptionListHeaderText;
337343
final Color unreadCountBadgeTextForChannel;
344+
final Color dmUserWarningBanner;
345+
final Color dmUserWarningBannerBorder;
338346

339347
@override
340348
DesignVariables copyWith({
@@ -384,6 +392,8 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
384392
Color? subscriptionListHeaderLine,
385393
Color? subscriptionListHeaderText,
386394
Color? unreadCountBadgeTextForChannel,
395+
Color? dmUserWarningBanner,
396+
Color? dmUserWarningBannerBorder,
387397
}) {
388398
return DesignVariables._(
389399
background: background ?? this.background,
@@ -432,6 +442,8 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
432442
subscriptionListHeaderLine: subscriptionListHeaderLine ?? this.subscriptionListHeaderLine,
433443
subscriptionListHeaderText: subscriptionListHeaderText ?? this.subscriptionListHeaderText,
434444
unreadCountBadgeTextForChannel: unreadCountBadgeTextForChannel ?? this.unreadCountBadgeTextForChannel,
445+
dmUserWarningBanner: dmUserWarningBanner ?? this.dmUserWarningBanner,
446+
dmUserWarningBannerBorder: dmUserWarningBannerBorder ?? this.dmUserWarningBannerBorder,
435447
);
436448
}
437449

@@ -487,6 +499,8 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
487499
subscriptionListHeaderLine: Color.lerp(subscriptionListHeaderLine, other.subscriptionListHeaderLine, t)!,
488500
subscriptionListHeaderText: Color.lerp(subscriptionListHeaderText, other.subscriptionListHeaderText, t)!,
489501
unreadCountBadgeTextForChannel: Color.lerp(unreadCountBadgeTextForChannel, other.unreadCountBadgeTextForChannel, t)!,
502+
dmUserWarningBanner: Color.lerp(dmUserWarningBanner, other.dmUserWarningBanner, t)!,
503+
dmUserWarningBannerBorder: Color.lerp(dmUserWarningBannerBorder, other.dmUserWarningBannerBorder, t)!,
490504
);
491505
}
492506
}

0 commit comments

Comments
 (0)