From 0ad41a8f5d310b05702491489312cc38c5ca3e66 Mon Sep 17 00:00:00 2001 From: chimnayajith Date: Fri, 7 Feb 2025 21:06:37 +0530 Subject: [PATCH 1/2] icons: Add inbox_done icon for empty inbox screen Reference: https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=5967-178714&t=gMqARx3pF7eyMkj6-4 --- assets/icons/ZulipIcons.ttf | Bin 12464 -> 12864 bytes assets/icons/inbox_done.svg | 7 +++++++ lib/widgets/icons.dart | 39 +++++++++++++++++++----------------- 3 files changed, 28 insertions(+), 18 deletions(-) create mode 100644 assets/icons/inbox_done.svg diff --git a/assets/icons/ZulipIcons.ttf b/assets/icons/ZulipIcons.ttf index 0be670d5cb409c5a8b45cecef5c85e0fedfded51..f0807d5e6daa07dab7a310ad2903dac70049e9c7 100644 GIT binary patch delta 2023 zcmb7FU1(Eh7=FL=*CZz=Nn@IJHrgg>(O>4{gXLYWE zEINc;ZKwem`vDmYyxDN-g@Q;Cuf`~YjonO!lZ}ZKh9J8r*zNg_r`v9q!;|Mb@AtmX z`}3WgT&u0r8^nl6rL!bbX7_>DcI+M9yF$bYMB+pRDRv73t$rosC&LPH`#WpsuLw26A8xR!ikgnzIgv_ zqD(jLyDr`dYolfd=)(Pd+e{v#EyYBaOPz*A;rkI@lg|iaikO!X@FRup<0P z5;-VDL$sd+bRWYNr+!L+)0CkB(&3@vTc;@HaG#(-?BvNnHphSblV)5B(`$img0g0O ziW&?8+faf5fut#HK*+%M2pqE{lM@B#RHi-D2FVl^AXSAYmrTL49g-=eHq4@Q+JcxO z)H1O6u-=UDBU1|H#nFmF9poV!#TvM+AQhJ@k{j-MSPBT`PTQ#!-9=Fb?<{S0nFr^{ zOZI>4o4xReZt6k711QD7m4GrYD{Ge83-vfGJYFFeMKIUK7`;MoP}m$|6t0^CM^o3yD@sve44TDvTio3r!WqvFQt|<0-cTuF_CB2ArV9jU9Rh%e5bTo+a z3Z}0l)SFkN3hQe=l&&>BvedjFH_}hc-x$%aC<(tblqBMjCH4fWcspQiLu&#ZrWrc@ zw69rK!UG9pDL^}ktb^unDvkE?$dW>HBbX6?est6~jKNf?)V!qZMc$v4$xg%;a!F$_ zGk}_UtkkSc|=;7b^RDr`565yJJDENql0{Ezf9Jp>F37)iI zfTt`Bf{$6qfTt}C5c1F9h9eEUWdZL+nzfJz&sivf=Pjhb3l`$wMGGU~;}*)`B?~d| z2@6^9NscPM@J=MW2TfGKrz{ME-?7jSUbavLpSDoC(QW^RIiL0saS+ga>`08;ZT2Pm zn&V~1=Z**JxVq-N zq_|mD_jj?ZQD(!eFTy2ws7nY3LlL+dTguE3+kF0Pu)$-)P()lL&BO^ssmmT&msPjY zb4d1Vtoj0MA`puOHXck!s;cbNygQW^me9Oj?fH*3Rkf8oowgJAWSh9_ylE#%DVfuePpvH}0t(wbk{zEY8e`@&}Gq$5lysLQDX1shnhKnPV7MW{kzQ@SXEbFXhGTRb}R-N*UP_qd+B zJGL#f5f%> z=NH!6XW#tpFA6LGv4`_>t=Y@>_P!$bXTq81LZ9Yqa(st4Ip0}X{cQPf@_qEzu&_8& zYqjsa!_Ck>30FI<)g^68JI>8v&SMu_ow>t1O&c&~0ll=ecxh$tg@N~g>;~sU*Y1o( zo3a8{u{=HV;M#plYkDJE+tnW-iWuvl^wP`cLp7gi%!R8gy2l zB>X7TE60^X9GfUHNCSuIV=|2tQx0GlgXoiK*QuCc zj4}quE^@yF$CacJJi#gw3?eGX%4&+V;}}Bl-}Qr>g=`FCh>)FRaECrsnU+ma6DKL3 z;)?JOpr6TC@H}3}+w7$1Rf1jzeR4&s3_QaydAT9PvUImGLYafL7{_I<3-$@t=P-fof-*@J(!*?(pm$GGv_HTn z!O)`V2_Aq*sp}fzSTs?{M3{)EctUnF#+oCXC*|>o)2^Y;xkb+?^-}Z`o@ugOaY9cq zY=U8Pq;=18SPBYh@&EAiZzJ4$$QxyyM=9?xIa{$sGsQ6&c$#d{n>vnhRF@&eaUH^Q ze49+DyB6oJO=fbG0iu+7+#Z`16_{U1l70HLx8rMQaqppT)4S<+bNli;0`4*$aVHqG zA%2Mal>D0W5R=sToWljI{So)x3XEA9MkrC&V90{}Zn7+`$OtA2tFk{yreiU=3cJ$4 zgm)|O3grXdmcMn3-lcKFDdh6>DwfSuwU)H4dL`YM@ILLm>h1ddZiJE1;}dWG8pJt; z3~fsxLCZ@)qC`8RFiy*hL&Bh)Q*db83I*Ep3OU+&g*@#ASG!^&wunr}OLHUVh74w{|uB1MrmJMgRZ+ diff --git a/assets/icons/inbox_done.svg b/assets/icons/inbox_done.svg new file mode 100644 index 0000000000..ac164358de --- /dev/null +++ b/assets/icons/inbox_done.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/lib/widgets/icons.dart b/lib/widgets/icons.dart index 82cb83704b..e18695aad9 100644 --- a/lib/widgets/icons.dart +++ b/lib/widgets/icons.dart @@ -84,59 +84,62 @@ abstract final class ZulipIcons { /// The Zulip custom icon "inbox". static const IconData inbox = IconData(0xf114, fontFamily: "Zulip Icons"); + /// The Zulip custom icon "inbox_done". + static const IconData inbox_done = IconData(0xf115, fontFamily: "Zulip Icons"); + /// The Zulip custom icon "info". - static const IconData info = IconData(0xf115, fontFamily: "Zulip Icons"); + static const IconData info = IconData(0xf116, fontFamily: "Zulip Icons"); /// The Zulip custom icon "inherit". - static const IconData inherit = IconData(0xf116, fontFamily: "Zulip Icons"); + static const IconData inherit = IconData(0xf117, fontFamily: "Zulip Icons"); /// The Zulip custom icon "language". - static const IconData language = IconData(0xf117, fontFamily: "Zulip Icons"); + static const IconData language = IconData(0xf118, fontFamily: "Zulip Icons"); /// The Zulip custom icon "lock". - static const IconData lock = IconData(0xf118, fontFamily: "Zulip Icons"); + static const IconData lock = IconData(0xf119, fontFamily: "Zulip Icons"); /// The Zulip custom icon "menu". - static const IconData menu = IconData(0xf119, fontFamily: "Zulip Icons"); + static const IconData menu = IconData(0xf11a, fontFamily: "Zulip Icons"); /// The Zulip custom icon "message_feed". - static const IconData message_feed = IconData(0xf11a, fontFamily: "Zulip Icons"); + static const IconData message_feed = IconData(0xf11b, fontFamily: "Zulip Icons"); /// The Zulip custom icon "mute". - static const IconData mute = IconData(0xf11b, fontFamily: "Zulip Icons"); + static const IconData mute = IconData(0xf11c, fontFamily: "Zulip Icons"); /// The Zulip custom icon "read_receipts". - static const IconData read_receipts = IconData(0xf11c, fontFamily: "Zulip Icons"); + static const IconData read_receipts = IconData(0xf11d, fontFamily: "Zulip Icons"); /// The Zulip custom icon "send". - static const IconData send = IconData(0xf11d, fontFamily: "Zulip Icons"); + static const IconData send = IconData(0xf11e, fontFamily: "Zulip Icons"); /// The Zulip custom icon "share". - static const IconData share = IconData(0xf11e, fontFamily: "Zulip Icons"); + static const IconData share = IconData(0xf11f, fontFamily: "Zulip Icons"); /// The Zulip custom icon "share_ios". - static const IconData share_ios = IconData(0xf11f, fontFamily: "Zulip Icons"); + static const IconData share_ios = IconData(0xf120, fontFamily: "Zulip Icons"); /// The Zulip custom icon "smile". - static const IconData smile = IconData(0xf120, fontFamily: "Zulip Icons"); + static const IconData smile = IconData(0xf121, fontFamily: "Zulip Icons"); /// The Zulip custom icon "star". - static const IconData star = IconData(0xf121, fontFamily: "Zulip Icons"); + static const IconData star = IconData(0xf122, fontFamily: "Zulip Icons"); /// The Zulip custom icon "star_filled". - static const IconData star_filled = IconData(0xf122, fontFamily: "Zulip Icons"); + static const IconData star_filled = IconData(0xf123, fontFamily: "Zulip Icons"); /// The Zulip custom icon "three_person". - static const IconData three_person = IconData(0xf123, fontFamily: "Zulip Icons"); + static const IconData three_person = IconData(0xf124, fontFamily: "Zulip Icons"); /// The Zulip custom icon "topic". - static const IconData topic = IconData(0xf124, fontFamily: "Zulip Icons"); + static const IconData topic = IconData(0xf125, fontFamily: "Zulip Icons"); /// The Zulip custom icon "unmute". - static const IconData unmute = IconData(0xf125, fontFamily: "Zulip Icons"); + static const IconData unmute = IconData(0xf126, fontFamily: "Zulip Icons"); /// The Zulip custom icon "user". - static const IconData user = IconData(0xf126, fontFamily: "Zulip Icons"); + static const IconData user = IconData(0xf127, fontFamily: "Zulip Icons"); // END GENERATED ICON DATA } From f9767ee9176951bd75968a42fce12e0a0ddcdb42 Mon Sep 17 00:00:00 2001 From: chimnayajith Date: Fri, 7 Feb 2025 21:12:36 +0530 Subject: [PATCH 2/2] inbox: Show empty state when there are no unreads Figma: https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=5966-176662&t=gMqARx3pF7eyMkj6-4 CZO discussion: https://chat.zulip.org/#narrow/channel/530-mobile-design/topic/InboxPage.3A.20Design/with/2082578 --- assets/l10n/app_en.arb | 4 + lib/generated/l10n/zulip_localizations.dart | 6 ++ .../l10n/zulip_localizations_ar.dart | 3 + .../l10n/zulip_localizations_en.dart | 3 + .../l10n/zulip_localizations_ja.dart | 3 + .../l10n/zulip_localizations_nb.dart | 3 + .../l10n/zulip_localizations_pl.dart | 3 + .../l10n/zulip_localizations_ru.dart | 3 + .../l10n/zulip_localizations_sk.dart | 3 + lib/widgets/inbox.dart | 91 +++++++++++++++++++ lib/widgets/theme.dart | 14 +++ test/widgets/inbox_test.dart | 38 ++++++++ 12 files changed, 174 insertions(+) diff --git a/assets/l10n/app_en.arb b/assets/l10n/app_en.arb index 0640af9ee1..c23d00d82b 100644 --- a/assets/l10n/app_en.arb +++ b/assets/l10n/app_en.arb @@ -824,5 +824,9 @@ "zulipAppTitle": "Zulip", "@zulipAppTitle": { "description": "The name of Zulip. This should be either 'Zulip' or a transliteration." + }, + "emptyInboxMessage": "There are no unread messages in your Inbox.\nCheck out the [combined feed] for recent messages.", + "@emptyInboxMessage": { + "description": "Message shown when inbox is empty. [combined feed] will be replaced with a clickable link to Combined Feed." } } diff --git a/lib/generated/l10n/zulip_localizations.dart b/lib/generated/l10n/zulip_localizations.dart index 3cbd917563..5320e0d564 100644 --- a/lib/generated/l10n/zulip_localizations.dart +++ b/lib/generated/l10n/zulip_localizations.dart @@ -1208,6 +1208,12 @@ abstract class ZulipLocalizations { /// In en, this message translates to: /// **'Zulip'** String get zulipAppTitle; + + /// Message shown when inbox is empty. [combined feed] will be replaced with a clickable link to Combined Feed. + /// + /// In en, this message translates to: + /// **'There are no unread messages in your Inbox.\nCheck out the [combined feed] for recent messages.'** + String get emptyInboxMessage; } class _ZulipLocalizationsDelegate extends LocalizationsDelegate { diff --git a/lib/generated/l10n/zulip_localizations_ar.dart b/lib/generated/l10n/zulip_localizations_ar.dart index 0304fd3e6f..b9ee5b0224 100644 --- a/lib/generated/l10n/zulip_localizations_ar.dart +++ b/lib/generated/l10n/zulip_localizations_ar.dart @@ -643,4 +643,7 @@ class ZulipLocalizationsAr extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get emptyInboxMessage => 'There are no unread messages in your Inbox.\nCheck out the [combined feed] for recent messages.'; } diff --git a/lib/generated/l10n/zulip_localizations_en.dart b/lib/generated/l10n/zulip_localizations_en.dart index 7af8cd7bab..05615377d5 100644 --- a/lib/generated/l10n/zulip_localizations_en.dart +++ b/lib/generated/l10n/zulip_localizations_en.dart @@ -643,4 +643,7 @@ class ZulipLocalizationsEn extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get emptyInboxMessage => 'There are no unread messages in your Inbox.\nCheck out the [combined feed] for recent messages.'; } diff --git a/lib/generated/l10n/zulip_localizations_ja.dart b/lib/generated/l10n/zulip_localizations_ja.dart index 6ac34645e2..51349ef6a4 100644 --- a/lib/generated/l10n/zulip_localizations_ja.dart +++ b/lib/generated/l10n/zulip_localizations_ja.dart @@ -643,4 +643,7 @@ class ZulipLocalizationsJa extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get emptyInboxMessage => 'There are no unread messages in your Inbox.\nCheck out the [combined feed] for recent messages.'; } diff --git a/lib/generated/l10n/zulip_localizations_nb.dart b/lib/generated/l10n/zulip_localizations_nb.dart index b3360e9f62..0945d45ccc 100644 --- a/lib/generated/l10n/zulip_localizations_nb.dart +++ b/lib/generated/l10n/zulip_localizations_nb.dart @@ -643,4 +643,7 @@ class ZulipLocalizationsNb extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get emptyInboxMessage => 'There are no unread messages in your Inbox.\nCheck out the [combined feed] for recent messages.'; } diff --git a/lib/generated/l10n/zulip_localizations_pl.dart b/lib/generated/l10n/zulip_localizations_pl.dart index cab571c163..f8835802a4 100644 --- a/lib/generated/l10n/zulip_localizations_pl.dart +++ b/lib/generated/l10n/zulip_localizations_pl.dart @@ -643,4 +643,7 @@ class ZulipLocalizationsPl extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get emptyInboxMessage => 'There are no unread messages in your Inbox.\nCheck out the [combined feed] for recent messages.'; } diff --git a/lib/generated/l10n/zulip_localizations_ru.dart b/lib/generated/l10n/zulip_localizations_ru.dart index babbc976fd..ea17fb7d68 100644 --- a/lib/generated/l10n/zulip_localizations_ru.dart +++ b/lib/generated/l10n/zulip_localizations_ru.dart @@ -643,4 +643,7 @@ class ZulipLocalizationsRu extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get emptyInboxMessage => 'There are no unread messages in your Inbox.\nCheck out the [combined feed] for recent messages.'; } diff --git a/lib/generated/l10n/zulip_localizations_sk.dart b/lib/generated/l10n/zulip_localizations_sk.dart index 964dbc29ad..a10a2701ab 100644 --- a/lib/generated/l10n/zulip_localizations_sk.dart +++ b/lib/generated/l10n/zulip_localizations_sk.dart @@ -643,4 +643,7 @@ class ZulipLocalizationsSk extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get emptyInboxMessage => 'There are no unread messages in your Inbox.\nCheck out the [combined feed] for recent messages.'; } diff --git a/lib/widgets/inbox.dart b/lib/widgets/inbox.dart index 799f763f1c..02c640bbb9 100644 --- a/lib/widgets/inbox.dart +++ b/lib/widgets/inbox.dart @@ -6,6 +6,7 @@ import '../model/narrow.dart'; import '../model/recent_dm_conversations.dart'; import '../model/unreads.dart'; import 'action_sheet.dart'; +import 'color.dart'; import 'icons.dart'; import 'message_list.dart'; import 'sticky_header.dart'; @@ -160,6 +161,10 @@ class _InboxPageState extends State with PerAccountStoreAwareStat sections.add(_StreamSectionData(streamId, countInStream, streamHasMention, topicItems)); } + if (sections.isEmpty) { + return const InboxEmptyWidget(); + } + return SafeArea( // Don't pad the bottom here; we want the list content to do that. bottom: false, @@ -182,6 +187,92 @@ class _InboxPageState extends State with PerAccountStoreAwareStat } } +class InboxEmptyWidget extends StatelessWidget { + const InboxEmptyWidget({super.key}); + + // Splits a message containing text in square brackets into three parts. + List _splitMessage(String message) { + final pattern = RegExp(r'(.*?)\[(.*?)\](.*)', dotAll: true); + final match = pattern.firstMatch(message); + + return match == null + ? [message, '', ''] + : [ + match.group(1) ?? '', + match.group(2) ?? '', + match.group(3) ?? '', + ]; + } + + @override + Widget build(BuildContext context) { + final zulipLocalizations = ZulipLocalizations.of(context); + final designVariables = DesignVariables.of(context); + + final messageParts = _splitMessage(zulipLocalizations.emptyInboxMessage); + + return Center( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const SizedBox(height: 48), + Icon( + ZulipIcons.inbox_done, + size: 80, + color: designVariables.foreground.withFadedAlpha(0.3), + ), + const SizedBox(height: 16), + Text.rich( + TextSpan( + style: TextStyle( + color: designVariables.labelSearchPrompt, + fontSize: 17, + fontWeight: FontWeight.w500, + ), + children: [ + TextSpan(text: messageParts[0]), + WidgetSpan( + alignment: PlaceholderAlignment.baseline, + baseline: TextBaseline.alphabetic, + child: TextButton( + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + minimumSize: Size.zero, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + splashFactory: NoSplash.splashFactory + ), + onPressed: () => Navigator.push(context, + MessageListPage.buildRoute(context: context, + narrow: const CombinedFeedNarrow())), + child: Text( + messageParts[1], + style: TextStyle( + fontSize: 17, + fontWeight: FontWeight.w500, + color: designVariables.link, + decoration: TextDecoration.underline, + decorationStyle: TextDecorationStyle.solid, + decorationThickness: 2.5, + decorationColor: designVariables.link, + height: 1.5, + ) + ), + ), + ), + TextSpan(text: messageParts[2]), + ], + ), + textAlign: TextAlign.center, + ), + ], + ), + ), + ); + } +} + sealed class _InboxSectionData { const _InboxSectionData(); } diff --git a/lib/widgets/theme.dart b/lib/widgets/theme.dart index ec8ad8aecc..782316fec3 100644 --- a/lib/widgets/theme.dart +++ b/lib/widgets/theme.dart @@ -157,8 +157,10 @@ class DesignVariables extends ThemeExtension { groupDmConversationIcon: Colors.black.withValues(alpha: 0.5), groupDmConversationIconBg: const Color(0x33808080), inboxItemIconMarker: const HSLColor.fromAHSL(0.5, 0, 0, 0.2).toColor(), + labelSearchPrompt: const Color(0xff000000).withValues(alpha: 0.5), loginOrDivider: const Color(0xffdedede), loginOrDividerText: const Color(0xff575757), + link: const Color(0xff066bd0), modalBarrierColor: const Color(0xff000000).withValues(alpha: 0.3), mutedUnreadBadge: const HSLColor.fromAHSL(0.5, 0, 0, 0.8).toColor(), navigationButtonBg: Colors.black.withValues(alpha: 0.05), @@ -209,8 +211,10 @@ class DesignVariables extends ThemeExtension { // TODO(design-dark) need proper dark-theme color (this is ad hoc) groupDmConversationIconBg: const Color(0x33cccccc), inboxItemIconMarker: const HSLColor.fromAHSL(0.4, 0, 0, 1).toColor(), + labelSearchPrompt: const Color(0xffffffff).withValues(alpha: 0.5), loginOrDivider: const Color(0xff424242), loginOrDividerText: const Color(0xffa8a8a8), + link: const Color(0xff00aaff), modalBarrierColor: const Color(0xff000000).withValues(alpha: 0.5), // TODO(design-dark) need proper dark-theme color (this is ad hoc) mutedUnreadBadge: const HSLColor.fromAHSL(0.5, 0, 0, 0.6).toColor(), @@ -263,8 +267,10 @@ class DesignVariables extends ThemeExtension { required this.groupDmConversationIcon, required this.groupDmConversationIconBg, required this.inboxItemIconMarker, + required this.labelSearchPrompt, required this.loginOrDivider, required this.loginOrDividerText, + required this.link, required this.modalBarrierColor, required this.mutedUnreadBadge, required this.navigationButtonBg, @@ -325,8 +331,10 @@ class DesignVariables extends ThemeExtension { final Color groupDmConversationIcon; final Color groupDmConversationIconBg; final Color inboxItemIconMarker; + final Color labelSearchPrompt; final Color loginOrDivider; // TODO(design-dark) need proper dark-theme color (this is ad hoc) final Color loginOrDividerText; // TODO(design-dark) need proper dark-theme color (this is ad hoc) + final Color link; final Color modalBarrierColor; final Color mutedUnreadBadge; final Color navigationButtonBg; @@ -374,8 +382,10 @@ class DesignVariables extends ThemeExtension { Color? groupDmConversationIcon, Color? groupDmConversationIconBg, Color? inboxItemIconMarker, + Color? labelSearchPrompt, Color? loginOrDivider, Color? loginOrDividerText, + Color? link, Color? modalBarrierColor, Color? mutedUnreadBadge, Color? navigationButtonBg, @@ -422,8 +432,10 @@ class DesignVariables extends ThemeExtension { groupDmConversationIcon: groupDmConversationIcon ?? this.groupDmConversationIcon, groupDmConversationIconBg: groupDmConversationIconBg ?? this.groupDmConversationIconBg, inboxItemIconMarker: inboxItemIconMarker ?? this.inboxItemIconMarker, + labelSearchPrompt: labelSearchPrompt ?? this.labelSearchPrompt, loginOrDivider: loginOrDivider ?? this.loginOrDivider, loginOrDividerText: loginOrDividerText ?? this.loginOrDividerText, + link: link ?? this.link, modalBarrierColor: modalBarrierColor ?? this.modalBarrierColor, mutedUnreadBadge: mutedUnreadBadge ?? this.mutedUnreadBadge, navigationButtonBg: navigationButtonBg ?? this.navigationButtonBg, @@ -477,8 +489,10 @@ class DesignVariables extends ThemeExtension { groupDmConversationIcon: Color.lerp(groupDmConversationIcon, other.groupDmConversationIcon, t)!, groupDmConversationIconBg: Color.lerp(groupDmConversationIconBg, other.groupDmConversationIconBg, t)!, inboxItemIconMarker: Color.lerp(inboxItemIconMarker, other.inboxItemIconMarker, t)!, + labelSearchPrompt: Color.lerp(labelSearchPrompt, other.labelSearchPrompt, t)!, loginOrDivider: Color.lerp(loginOrDivider, other.loginOrDivider, t)!, loginOrDividerText: Color.lerp(loginOrDividerText, other.loginOrDividerText, t)!, + link: Color.lerp(link, other.link, t)!, modalBarrierColor: Color.lerp(modalBarrierColor, other.modalBarrierColor, t)!, mutedUnreadBadge: Color.lerp(mutedUnreadBadge, other.mutedUnreadBadge, t)!, navigationButtonBg: Color.lerp(navigationButtonBg, other.navigationButtonBg, t)!, diff --git a/test/widgets/inbox_test.dart b/test/widgets/inbox_test.dart index 3fa3713d5d..7cc4f3305b 100644 --- a/test/widgets/inbox_test.dart +++ b/test/widgets/inbox_test.dart @@ -4,17 +4,24 @@ import 'package:flutter_checks/flutter_checks.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:zulip/api/model/events.dart'; import 'package:zulip/api/model/model.dart'; +import 'package:zulip/model/narrow.dart'; import 'package:zulip/model/store.dart'; import 'package:zulip/widgets/color.dart'; import 'package:zulip/widgets/home.dart'; import 'package:zulip/widgets/icons.dart'; import 'package:zulip/widgets/channel_colors.dart'; +import 'package:zulip/widgets/message_list.dart'; +import 'package:zulip/widgets/page.dart'; +import '../api/fake_api.dart'; import '../example_data.dart' as eg; import '../flutter_checks.dart'; import '../model/binding.dart'; import '../model/test_store.dart'; +import '../test_navigation.dart'; +import 'message_list_checks.dart'; import 'test_app.dart'; +import 'page_checks.dart'; /// Repeatedly drags `view` by `moveStep` until `finder` is invisible. /// @@ -52,6 +59,7 @@ void main() { TestZulipBinding.ensureInitialized(); late PerAccountStore store; + late FakeApiConnection connection; Future setupPage(WidgetTester tester, { List? streams, @@ -203,6 +211,36 @@ void main() { await setupVarious(tester); }); + testWidgets('empty inbox shows empty state', (tester) async { + final pushedRoutes = >[]; + final testNavObserver = TestNavigatorObserver() + ..onPushed = (route, prevRoute) => pushedRoutes.add(route); + + await setupPage(tester, + unreadMessages: [], + navigatorObserver: testNavObserver); + pushedRoutes.clear(); + + connection = store.connection as FakeApiConnection; + connection.prepare(json: eg.newestGetMessagesResult( + foundOldest: true, + messages: []).toJson()); + + expect(find.byIcon(ZulipIcons.inbox_done), findsOneWidget); + expect(find.textContaining('There are no unread messages in your Inbox.'), findsOneWidget); + + final combinedFeedButton = find.text('combined feed'); + expect(combinedFeedButton, findsOneWidget); + + await tester.tap(combinedFeedButton); + await tester.pump(); + await tester.pump(const Duration(milliseconds: 250)); + + check(pushedRoutes).single.isA().page + .isA() + .initNarrow.equals(const CombinedFeedNarrow()); + }); + // TODO test that tapping a conversation row opens the message list // for the conversation