Skip to content

Commit

Permalink
message-list : add a prompt to notify guest(s) when DM
Browse files Browse the repository at this point in the history
Fixes #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.
  • Loading branch information
Abhisheksainii committed Feb 19, 2025
1 parent 4f438d3 commit 0dcbc89
Show file tree
Hide file tree
Showing 16 changed files with 197 additions and 10 deletions.
8 changes: 8 additions & 0 deletions assets/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -831,5 +831,13 @@
"zulipAppTitle": "Zulip",
"@zulipAppTitle": {
"description": "The name of Zulip. This should be either 'Zulip' or a transliteration."
},
"bannerText": "{guestCount, plural, =1{{guestNamesList} is a guest in this organization.} other{{guestNamesList} are guests in this organization.}}",
"@bannerText": {
"description": "Message displaying guest names in the organization.",
"placeholders": {
"guestCount": {"type": "int", "example": "3"},
"guestNamesList": {"type": "String", "example": "Alice, Bob, Charlie"}
}
}
}
3 changes: 3 additions & 0 deletions lib/api/model/initial_snapshot.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ class InitialSnapshot {
/// https://zulip.com/api/roles-and-permissions#determining-if-a-user-is-a-full-member
final int realmWaitingPeriodThreshold;

final bool? realmEnableGuestUserDmWarning;

final Map<String, RealmDefaultExternalAccount> realmDefaultExternalAccounts;

final int maxFileUploadSizeMib;
Expand Down Expand Up @@ -135,6 +137,7 @@ class InitialSnapshot {
required this.realmWildcardMentionPolicy,
required this.realmMandatoryTopics,
required this.realmWaitingPeriodThreshold,
required this.realmEnableGuestUserDmWarning,
required this.realmDefaultExternalAccounts,
required this.maxFileUploadSizeMib,
required this.serverEmojiDataUrl,
Expand Down
4 changes: 4 additions & 0 deletions lib/api/model/initial_snapshot.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions lib/generated/l10n/zulip_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,12 @@ abstract class ZulipLocalizations {
/// In en, this message translates to:
/// **'Zulip'**
String get zulipAppTitle;

/// Message displaying guest names in the organization.
///
/// In en, this message translates to:
/// **'{guestCount, plural, =1{{guestNamesList} is a guest in this organization.} other{{guestNamesList} are guests in this organization.}}'**
String bannerText(int guestCount, String guestNamesList);
}

class _ZulipLocalizationsDelegate extends LocalizationsDelegate<ZulipLocalizations> {
Expand Down
11 changes: 11 additions & 0 deletions lib/generated/l10n/zulip_localizations_ar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -648,4 +648,15 @@ class ZulipLocalizationsAr extends ZulipLocalizations {

@override
String get zulipAppTitle => 'Zulip';

@override
String bannerText(int guestCount, String guestNamesList) {
String _temp0 = intl.Intl.pluralLogic(
guestCount,
locale: localeName,
other: '$guestNamesList are guests in this organization.',
one: '$guestNamesList is a guest in this organization.',
);
return '$_temp0';
}
}
11 changes: 11 additions & 0 deletions lib/generated/l10n/zulip_localizations_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -648,4 +648,15 @@ class ZulipLocalizationsEn extends ZulipLocalizations {

@override
String get zulipAppTitle => 'Zulip';

@override
String bannerText(int guestCount, String guestNamesList) {
String _temp0 = intl.Intl.pluralLogic(
guestCount,
locale: localeName,
other: '$guestNamesList are guests in this organization.',
one: '$guestNamesList is a guest in this organization.',
);
return '$_temp0';
}
}
11 changes: 11 additions & 0 deletions lib/generated/l10n/zulip_localizations_ja.dart
Original file line number Diff line number Diff line change
Expand Up @@ -648,4 +648,15 @@ class ZulipLocalizationsJa extends ZulipLocalizations {

@override
String get zulipAppTitle => 'Zulip';

@override
String bannerText(int guestCount, String guestNamesList) {
String _temp0 = intl.Intl.pluralLogic(
guestCount,
locale: localeName,
other: '$guestNamesList are guests in this organization.',
one: '$guestNamesList is a guest in this organization.',
);
return '$_temp0';
}
}
11 changes: 11 additions & 0 deletions lib/generated/l10n/zulip_localizations_nb.dart
Original file line number Diff line number Diff line change
Expand Up @@ -648,4 +648,15 @@ class ZulipLocalizationsNb extends ZulipLocalizations {

@override
String get zulipAppTitle => 'Zulip';

@override
String bannerText(int guestCount, String guestNamesList) {
String _temp0 = intl.Intl.pluralLogic(
guestCount,
locale: localeName,
other: '$guestNamesList are guests in this organization.',
one: '$guestNamesList is a guest in this organization.',
);
return '$_temp0';
}
}
11 changes: 11 additions & 0 deletions lib/generated/l10n/zulip_localizations_pl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -648,4 +648,15 @@ class ZulipLocalizationsPl extends ZulipLocalizations {

@override
String get zulipAppTitle => 'Zulip';

@override
String bannerText(int guestCount, String guestNamesList) {
String _temp0 = intl.Intl.pluralLogic(
guestCount,
locale: localeName,
other: '$guestNamesList are guests in this organization.',
one: '$guestNamesList is a guest in this organization.',
);
return '$_temp0';
}
}
11 changes: 11 additions & 0 deletions lib/generated/l10n/zulip_localizations_ru.dart
Original file line number Diff line number Diff line change
Expand Up @@ -648,4 +648,15 @@ class ZulipLocalizationsRu extends ZulipLocalizations {

@override
String get zulipAppTitle => 'Zulip';

@override
String bannerText(int guestCount, String guestNamesList) {
String _temp0 = intl.Intl.pluralLogic(
guestCount,
locale: localeName,
other: '$guestNamesList are guests in this organization.',
one: '$guestNamesList is a guest in this organization.',
);
return '$_temp0';
}
}
11 changes: 11 additions & 0 deletions lib/generated/l10n/zulip_localizations_sk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -648,4 +648,15 @@ class ZulipLocalizationsSk extends ZulipLocalizations {

@override
String get zulipAppTitle => 'Zulip';

@override
String bannerText(int guestCount, String guestNamesList) {
String _temp0 = intl.Intl.pluralLogic(
guestCount,
locale: localeName,
other: '$guestNamesList are guests in this organization.',
one: '$guestNamesList is a guest in this organization.',
);
return '$_temp0';
}
}
4 changes: 4 additions & 0 deletions lib/model/store.dart
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ class PerAccountStore extends ChangeNotifier with EmojiStore, ChannelStore, Mess
recentDmConversationsView: RecentDmConversationsView(
initial: initialSnapshot.recentPrivateConversations, selfUserId: account.userId),
recentSenders: RecentSenders(),
realmEnableGuestUserDmWarning: initialSnapshot.realmEnableGuestUserDmWarning,
);
}

Expand All @@ -361,6 +362,7 @@ class PerAccountStore extends ChangeNotifier with EmojiStore, ChannelStore, Mess
required this.unreads,
required this.recentDmConversationsView,
required this.recentSenders,
required this.realmEnableGuestUserDmWarning,
}) : assert(selfUserId == globalStore.getAccount(accountId)!.userId),
assert(realmUrl == globalStore.getAccount(accountId)!.realmUrl),
assert(realmUrl == connection.realmUrl),
Expand Down Expand Up @@ -567,6 +569,8 @@ class PerAccountStore extends ChangeNotifier with EmojiStore, ChannelStore, Mess

final AutocompleteViewManager autocompleteViewManager = AutocompleteViewManager();

final bool? realmEnableGuestUserDmWarning;

// End of data.
////////////////////////////////////////////////////////////////
Expand Down
69 changes: 69 additions & 0 deletions lib/widgets/message_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ abstract class MessageListPageState {
///
/// This is null if [MessageList] has not mounted yet.
MessageListView? get model;

bool showDMWarningBanner = true;
}

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

@override
late bool showDMWarningBanner;

@override
void initState() {
super.initState();
narrow = widget.initNarrow;
showDMWarningBanner = narrow is DmNarrow;
}

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

final Color? appBarBackgroundColor;
bool removeAppBarBottomBorder = false;
Expand Down Expand Up @@ -318,10 +325,72 @@ class _MessageListPageState extends State<MessageListPage> implements MessageLis
narrow: narrow,
onNarrowChanged: _narrowChanged,
))),
if(shouldShowGuestUserWarningPrompt(store))
showGuestUserWarningPrompt(narrow as DmNarrow, store, designVariables, zulipLocalizations),
if (ComposeBox.hasComposeBox(narrow))
ComposeBox(key: _composeBoxKey, narrow: narrow)
]))));
}

bool shouldShowGuestUserWarningPrompt(PerAccountStore store) =>
store.connection.zulipFeatureLevel! >= 348 &&
narrow is DmNarrow && (store.realmEnableGuestUserDmWarning ?? false);

Widget showGuestUserWarningPrompt(
DmNarrow narrow,
PerAccountStore store,
DesignVariables designVariables,
ZulipLocalizations zulipLocalizations,
) {
final recipients = narrow.otherRecipientIds;
final allUsersInStore = Map<int, User>.from(store.users);
allUsersInStore.removeWhere((userId, user) => !recipients.contains(userId));
final guestNames =
allUsersInStore.values
.where((user) => user.role == UserRole.guest)
.map((e) => e.fullName)
.toList();

if(guestNames.isEmpty){
return SizedBox();
}

return Visibility(
visible: showDMWarningBanner,
child: Align(
alignment: Alignment.centerLeft,
child: Container(
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
border: Border.all(color: designVariables.dmUserWarningBannerBorder,),
color: designVariables.dmUserWarningBanner,
),
margin: EdgeInsets.all(4),
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Text(
zulipLocalizations.bannerText(guestNames.length, guestNames.join(', ')),
style: TextStyle(color: Colors.white),
),
),
GestureDetector(
onTap: () {
setState(() {
showDMWarningBanner= false;
});
},
child: Icon(Icons.close, color: Colors.white),
),
],
),
),
),
);
}
}

class MessageListAppBarTitle extends StatelessWidget {
Expand Down
14 changes: 14 additions & 0 deletions lib/widgets/theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
subscriptionListHeaderLine: const HSLColor.fromAHSL(0.2, 240, 0.1, 0.5).toColor(),
subscriptionListHeaderText: const HSLColor.fromAHSL(1.0, 240, 0.1, 0.5).toColor(),
unreadCountBadgeTextForChannel: Colors.black.withValues(alpha: 0.9),
dmUserWarningBanner: Color.fromARGB(255, 133, 118, 71),
dmUserWarningBannerBorder: Color.fromARGB(255, 127, 111, 60),
);

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

DesignVariables._({
Expand Down Expand Up @@ -273,6 +277,8 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
required this.subscriptionListHeaderLine,
required this.subscriptionListHeaderText,
required this.unreadCountBadgeTextForChannel,
required this.dmUserWarningBanner,
required this.dmUserWarningBannerBorder,
});

/// The [DesignVariables] from the context's active theme.
Expand Down Expand Up @@ -335,6 +341,8 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
final Color subscriptionListHeaderLine;
final Color subscriptionListHeaderText;
final Color unreadCountBadgeTextForChannel;
final Color dmUserWarningBanner;
final Color dmUserWarningBannerBorder;

@override
DesignVariables copyWith({
Expand Down Expand Up @@ -384,6 +392,8 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
Color? subscriptionListHeaderLine,
Color? subscriptionListHeaderText,
Color? unreadCountBadgeTextForChannel,
Color? dmUserWarningBanner,
Color? dmUserWarningBannerBorder,
}) {
return DesignVariables._(
background: background ?? this.background,
Expand Down Expand Up @@ -432,6 +442,8 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
subscriptionListHeaderLine: subscriptionListHeaderLine ?? this.subscriptionListHeaderLine,
subscriptionListHeaderText: subscriptionListHeaderText ?? this.subscriptionListHeaderText,
unreadCountBadgeTextForChannel: unreadCountBadgeTextForChannel ?? this.unreadCountBadgeTextForChannel,
dmUserWarningBanner: dmUserWarningBanner ?? this.dmUserWarningBanner,
dmUserWarningBannerBorder: dmUserWarningBannerBorder ?? this.dmUserWarningBannerBorder,
);
}

Expand Down Expand Up @@ -487,6 +499,8 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
subscriptionListHeaderLine: Color.lerp(subscriptionListHeaderLine, other.subscriptionListHeaderLine, t)!,
subscriptionListHeaderText: Color.lerp(subscriptionListHeaderText, other.subscriptionListHeaderText, t)!,
unreadCountBadgeTextForChannel: Color.lerp(unreadCountBadgeTextForChannel, other.unreadCountBadgeTextForChannel, t)!,
dmUserWarningBanner: Color.lerp(dmUserWarningBanner, other.dmUserWarningBanner, t)!,
dmUserWarningBannerBorder: Color.lerp(dmUserWarningBannerBorder, other.dmUserWarningBannerBorder, t)!,
);
}
}
Expand Down
Loading

0 comments on commit 0dcbc89

Please sign in to comment.