From 4113c34363eb72feb9794ed8379c263ae69f0001 Mon Sep 17 00:00:00 2001 From: Zixuan James Li Date: Thu, 21 Nov 2024 14:33:17 -0500 Subject: [PATCH 1/4] nav [nfc]: Extract some *PageBody widgets These are extracted because we will later make them accessible as bottom tabs on the HomePage. Among those bottom tabs, the "Users" page (not yet implemented) and MessageListPage are skipped. These are out-of-scope of as now. See Figma for the available tabs: https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=3307-50151&node-type=frame&t=hGZtzX2QTLse81un-0 Signed-off-by: Zixuan James Li --- lib/widgets/inbox.dart | 57 +++++++++++++--------- lib/widgets/recent_dm_conversations.dart | 48 ++++++++++-------- lib/widgets/subscription_list.dart | 62 +++++++++++++----------- 3 files changed, 96 insertions(+), 71 deletions(-) diff --git a/lib/widgets/inbox.dart b/lib/widgets/inbox.dart index eb3a54da5a..dcbd1acdb2 100644 --- a/lib/widgets/inbox.dart +++ b/lib/widgets/inbox.dart @@ -14,7 +14,7 @@ import 'text.dart'; import 'theme.dart'; import 'unread_count_badge.dart'; -class InboxPage extends StatefulWidget { +class InboxPage extends StatelessWidget { const InboxPage({super.key}); static Route buildRoute({int? accountId, BuildContext? context}) { @@ -23,10 +23,21 @@ class InboxPage extends StatefulWidget { } @override - State createState() => _InboxPageState(); + Widget build(BuildContext context) { + return Scaffold( + appBar: ZulipAppBar(title: const Text('Inbox')), + body: const InboxPageBody()); + } +} + +class InboxPageBody extends StatefulWidget { + const InboxPageBody({super.key}); + + @override + State createState() => _InboxPageState(); } -class _InboxPageState extends State with PerAccountStoreAwareStateMixin { +class _InboxPageState extends State with PerAccountStoreAwareStateMixin { Unreads? unreadsModel; RecentDmConversationsView? recentDmConversationsModel; @@ -160,27 +171,25 @@ class _InboxPageState extends State with PerAccountStoreAwareStateMix sections.add(_StreamSectionData(streamId, countInStream, streamHasMention, topicItems)); } - return Scaffold( - appBar: ZulipAppBar(title: const Text('Inbox')), - body: SafeArea( - // Don't pad the bottom here; we want the list content to do that. - bottom: false, - child: StickyHeaderListView.builder( - itemCount: sections.length, - itemBuilder: (context, index) { - final section = sections[index]; - switch (section) { - case _AllDmsSectionData(): - return _AllDmsSection( - data: section, - collapsed: allDmsCollapsed, - pageState: this, - ); - case _StreamSectionData(:var streamId): - final collapsed = collapsedStreamIds.contains(streamId); - return _StreamSection(data: section, collapsed: collapsed, pageState: this); - } - }))); + return SafeArea( + // Don't pad the bottom here; we want the list content to do that. + bottom: false, + child: StickyHeaderListView.builder( + itemCount: sections.length, + itemBuilder: (context, index) { + final section = sections[index]; + switch (section) { + case _AllDmsSectionData(): + return _AllDmsSection( + data: section, + collapsed: allDmsCollapsed, + pageState: this, + ); + case _StreamSectionData(:var streamId): + final collapsed = collapsedStreamIds.contains(streamId); + return _StreamSection(data: section, collapsed: collapsed, pageState: this); + } + })); } } diff --git a/lib/widgets/recent_dm_conversations.dart b/lib/widgets/recent_dm_conversations.dart index ea7a051d9a..69245adc04 100644 --- a/lib/widgets/recent_dm_conversations.dart +++ b/lib/widgets/recent_dm_conversations.dart @@ -13,7 +13,7 @@ import 'store.dart'; import 'theme.dart'; import 'unread_count_badge.dart'; -class RecentDmConversationsPage extends StatefulWidget { +class RecentDmConversationsPage extends StatelessWidget { const RecentDmConversationsPage({super.key}); static Route buildRoute({int? accountId, BuildContext? context}) { @@ -22,10 +22,23 @@ class RecentDmConversationsPage extends StatefulWidget { } @override - State createState() => _RecentDmConversationsPageState(); + Widget build(BuildContext context) { + final zulipLocalizations = ZulipLocalizations.of(context); + return Scaffold( + appBar: ZulipAppBar( + title: Text(zulipLocalizations.recentDmConversationsPageTitle)), + body: const RecentDmConversationsPageBody()); + } +} + +class RecentDmConversationsPageBody extends StatefulWidget { + const RecentDmConversationsPageBody({super.key}); + + @override + State createState() => _RecentDmConversationsPageBodyState(); } -class _RecentDmConversationsPageState extends State with PerAccountStoreAwareStateMixin { +class _RecentDmConversationsPageBodyState extends State with PerAccountStoreAwareStateMixin { RecentDmConversationsView? model; Unreads? unreadsModel; @@ -56,24 +69,19 @@ class _RecentDmConversationsPageState extends State w @override Widget build(BuildContext context) { - final zulipLocalizations = ZulipLocalizations.of(context); final sorted = model!.sorted; - return Scaffold( - appBar: ZulipAppBar( - title: Text(zulipLocalizations.recentDmConversationsPageTitle)), - body: SafeArea( - // Don't pad the bottom here; we want the list content to do that. - bottom: false, - child: ListView.builder( - itemCount: sorted.length, - itemBuilder: (context, index) { - final narrow = sorted[index]; - return RecentDmConversationsItem( - narrow: narrow, - unreadCount: unreadsModel!.countInDmNarrow(narrow), - ); - }), - )); + return SafeArea( + // Don't pad the bottom here; we want the list content to do that. + bottom: false, + child: ListView.builder( + itemCount: sorted.length, + itemBuilder: (context, index) { + final narrow = sorted[index]; + return RecentDmConversationsItem( + narrow: narrow, + unreadCount: unreadsModel!.countInDmNarrow(narrow), + ); + })); } } diff --git a/lib/widgets/subscription_list.dart b/lib/widgets/subscription_list.dart index 86a9caf4e4..a7fa96de7e 100644 --- a/lib/widgets/subscription_list.dart +++ b/lib/widgets/subscription_list.dart @@ -13,7 +13,7 @@ import 'theme.dart'; import 'unread_count_badge.dart'; /// Scrollable listing of subscribed streams. -class SubscriptionListPage extends StatefulWidget { +class SubscriptionListPage extends StatelessWidget { const SubscriptionListPage({super.key}); static Route buildRoute({int? accountId, BuildContext? context}) { @@ -22,10 +22,21 @@ class SubscriptionListPage extends StatefulWidget { } @override - State createState() => _SubscriptionListPageState(); + Widget build(BuildContext context) { + return Scaffold( + appBar: ZulipAppBar(title: const Text("Channels")), + body: const SubscriptionListPageBody()); + } +} + +class SubscriptionListPageBody extends StatefulWidget { + const SubscriptionListPageBody({super.key}); + + @override + State createState() => _SubscriptionListPageBodyState(); } -class _SubscriptionListPageState extends State with PerAccountStoreAwareStateMixin { +class _SubscriptionListPageBodyState extends State with PerAccountStoreAwareStateMixin { Unreads? unreadsModel; @override @@ -89,30 +100,27 @@ class _SubscriptionListPageState extends State with PerAcc _sortSubs(pinned); _sortSubs(unpinned); - return Scaffold( - appBar: ZulipAppBar(title: const Text("Channels")), - body: SafeArea( - // Don't pad the bottom here; we want the list content to do that. - bottom: false, - child: CustomScrollView( - slivers: [ - if (pinned.isEmpty && unpinned.isEmpty) - const _NoSubscriptionsItem(), - if (pinned.isNotEmpty) ...[ - const _SubscriptionListHeader(label: "Pinned"), - _SubscriptionList(unreadsModel: unreadsModel, subscriptions: pinned), - ], - if (unpinned.isNotEmpty) ...[ - const _SubscriptionListHeader(label: "Unpinned"), - _SubscriptionList(unreadsModel: unreadsModel, subscriptions: unpinned), - ], - - // TODO(#188): add button leading to "All Streams" page with ability to subscribe - - // This ensures last item in scrollable can settle in an unobstructed area. - const SliverSafeArea(sliver: SliverToBoxAdapter(child: SizedBox.shrink())), - ]), - )); + return SafeArea( + // Don't pad the bottom here; we want the list content to do that. + bottom: false, + child: CustomScrollView( + slivers: [ + if (pinned.isEmpty && unpinned.isEmpty) + const _NoSubscriptionsItem(), + if (pinned.isNotEmpty) ...[ + const _SubscriptionListHeader(label: "Pinned"), + _SubscriptionList(unreadsModel: unreadsModel, subscriptions: pinned), + ], + if (unpinned.isNotEmpty) ...[ + const _SubscriptionListHeader(label: "Unpinned"), + _SubscriptionList(unreadsModel: unreadsModel, subscriptions: unpinned), + ], + + // TODO(#188): add button leading to "All Streams" page with ability to subscribe + + // This ensures last item in scrollable can settle in an unobstructed area. + const SliverSafeArea(sliver: SliverToBoxAdapter(child: SizedBox.shrink())), + ])); } } From 97f17052e9c1aa14b7da0a2aaa9156e2542e6081 Mon Sep 17 00:00:00 2001 From: Zixuan James Li Date: Thu, 21 Nov 2024 15:32:25 -0500 Subject: [PATCH 2/4] nav [nfc]: Move HomePage to a new home Signed-off-by: Zixuan James Li --- lib/widgets/app.dart | 92 +------------------------- lib/widgets/home.dart | 97 ++++++++++++++++++++++++++++ lib/widgets/login.dart | 2 +- test/notifications/display_test.dart | 1 + test/widgets/app_test.dart | 1 + test/widgets/login_test.dart | 1 + 6 files changed, 102 insertions(+), 92 deletions(-) create mode 100644 lib/widgets/home.dart diff --git a/lib/widgets/app.dart b/lib/widgets/app.dart index b5aad7bbba..12e83d1285 100644 --- a/lib/widgets/app.dart +++ b/lib/widgets/app.dart @@ -7,21 +7,16 @@ import 'package:flutter/scheduler.dart'; import '../generated/l10n/zulip_localizations.dart'; import '../log.dart'; import '../model/localizations.dart'; -import '../model/narrow.dart'; import '../model/store.dart'; import '../notifications/display.dart'; import 'about_zulip.dart'; import 'actions.dart'; -import 'app_bar.dart'; import 'dialog.dart'; +import 'home.dart'; import 'inbox.dart'; import 'login.dart'; -import 'message_list.dart'; import 'page.dart'; -import 'recent_dm_conversations.dart'; import 'store.dart'; -import 'subscription_list.dart'; -import 'text.dart'; import 'theme.dart'; class ZulipApp extends StatefulWidget { @@ -339,88 +334,3 @@ class ChooseAccountPageOverflowButton extends StatelessWidget { }); } } - -class HomePage extends StatelessWidget { - const HomePage({super.key}); - - static Route buildRoute({required int accountId}) { - return MaterialAccountWidgetRoute(accountId: accountId, - page: const HomePage()); - } - - @override - Widget build(BuildContext context) { - final store = PerAccountStoreWidget.of(context); - final zulipLocalizations = ZulipLocalizations.of(context); - - final colorScheme = ColorScheme.of(context); - - InlineSpan bold(String text) => TextSpan( - style: const TextStyle().merge(weightVariableTextStyle(context, wght: 700)), - text: text); - - int? testStreamId; - if (store.connection.realmUrl.origin == 'https://chat.zulip.org') { - testStreamId = 7; // i.e. `#test here`; TODO cut this scaffolding hack - } - - return Scaffold( - appBar: ZulipAppBar(title: const Text("Home")), - body: ElevatedButtonTheme( - data: ElevatedButtonThemeData(style: ButtonStyle( - backgroundColor: WidgetStatePropertyAll(colorScheme.secondaryContainer), - foregroundColor: WidgetStatePropertyAll(colorScheme.onSecondaryContainer))), - child: Center( - child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ - DefaultTextStyle.merge( - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 18), - child: Column(children: [ - Text.rich(TextSpan( - text: 'Connected to: ', - children: [bold(store.realmUrl.toString())])), - ])), - const SizedBox(height: 16), - ElevatedButton( - onPressed: () => Navigator.push(context, - MessageListPage.buildRoute(context: context, - narrow: const CombinedFeedNarrow())), - child: Text(zulipLocalizations.combinedFeedPageTitle)), - const SizedBox(height: 16), - ElevatedButton( - onPressed: () => Navigator.push(context, - MessageListPage.buildRoute(context: context, - narrow: const MentionsNarrow())), - child: Text(zulipLocalizations.mentionsPageTitle)), - const SizedBox(height: 16), - ElevatedButton( - onPressed: () => Navigator.push(context, - MessageListPage.buildRoute(context: context, - narrow: const StarredMessagesNarrow())), - child: Text(zulipLocalizations.starredMessagesPageTitle)), - const SizedBox(height: 16), - ElevatedButton( - onPressed: () => Navigator.push(context, - InboxPage.buildRoute(context: context)), - child: const Text("Inbox")), // TODO(i18n) - const SizedBox(height: 16), - ElevatedButton( - onPressed: () => Navigator.push(context, - SubscriptionListPage.buildRoute(context: context)), - child: const Text("Subscribed channels")), - const SizedBox(height: 16), - ElevatedButton( - onPressed: () => Navigator.push(context, - RecentDmConversationsPage.buildRoute(context: context)), - child: Text(zulipLocalizations.recentDmConversationsPageTitle)), - if (testStreamId != null) ...[ - const SizedBox(height: 16), - ElevatedButton( - onPressed: () => Navigator.push(context, - MessageListPage.buildRoute(context: context, - narrow: ChannelNarrow(testStreamId!))), - child: const Text("#test here")), // scaffolding hack, see above - ], - ])))); - } -} diff --git a/lib/widgets/home.dart b/lib/widgets/home.dart new file mode 100644 index 0000000000..fd115370de --- /dev/null +++ b/lib/widgets/home.dart @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart'; + +import '../generated/l10n/zulip_localizations.dart'; +import '../model/narrow.dart'; +import 'app_bar.dart'; +import 'inbox.dart'; +import 'message_list.dart'; +import 'page.dart'; +import 'recent_dm_conversations.dart'; +import 'store.dart'; +import 'subscription_list.dart'; +import 'text.dart'; + +class HomePage extends StatelessWidget { + const HomePage({super.key}); + + static Route buildRoute({required int accountId}) { + return MaterialAccountWidgetRoute(accountId: accountId, + page: const HomePage()); + } + + @override + Widget build(BuildContext context) { + final store = PerAccountStoreWidget.of(context); + final zulipLocalizations = ZulipLocalizations.of(context); + + final colorScheme = ColorScheme.of(context); + + InlineSpan bold(String text) => TextSpan( + style: const TextStyle().merge(weightVariableTextStyle(context, wght: 700)), + text: text); + + int? testStreamId; + if (store.connection.realmUrl.origin == 'https://chat.zulip.org') { + testStreamId = 7; // i.e. `#test here`; TODO cut this scaffolding hack + } + + return Scaffold( + appBar: ZulipAppBar(title: const Text("Home")), + body: ElevatedButtonTheme( + data: ElevatedButtonThemeData(style: ButtonStyle( + backgroundColor: WidgetStatePropertyAll(colorScheme.secondaryContainer), + foregroundColor: WidgetStatePropertyAll(colorScheme.onSecondaryContainer))), + child: Center( + child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ + DefaultTextStyle.merge( + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 18), + child: Column(children: [ + Text.rich(TextSpan( + text: 'Connected to: ', + children: [bold(store.realmUrl.toString())])), + ])), + const SizedBox(height: 16), + ElevatedButton( + onPressed: () => Navigator.push(context, + MessageListPage.buildRoute(context: context, + narrow: const CombinedFeedNarrow())), + child: Text(zulipLocalizations.combinedFeedPageTitle)), + const SizedBox(height: 16), + ElevatedButton( + onPressed: () => Navigator.push(context, + MessageListPage.buildRoute(context: context, + narrow: const MentionsNarrow())), + child: Text(zulipLocalizations.mentionsPageTitle)), + const SizedBox(height: 16), + ElevatedButton( + onPressed: () => Navigator.push(context, + MessageListPage.buildRoute(context: context, + narrow: const StarredMessagesNarrow())), + child: Text(zulipLocalizations.starredMessagesPageTitle)), + const SizedBox(height: 16), + ElevatedButton( + onPressed: () => Navigator.push(context, + InboxPage.buildRoute(context: context)), + child: const Text("Inbox")), // TODO(i18n) + const SizedBox(height: 16), + ElevatedButton( + onPressed: () => Navigator.push(context, + SubscriptionListPage.buildRoute(context: context)), + child: const Text("Subscribed channels")), + const SizedBox(height: 16), + ElevatedButton( + onPressed: () => Navigator.push(context, + RecentDmConversationsPage.buildRoute(context: context)), + child: Text(zulipLocalizations.recentDmConversationsPageTitle)), + if (testStreamId != null) ...[ + const SizedBox(height: 16), + ElevatedButton( + onPressed: () => Navigator.push(context, + MessageListPage.buildRoute(context: context, + narrow: ChannelNarrow(testStreamId!))), + child: const Text("#test here")), // scaffolding hack, see above + ], + ])))); + } +} diff --git a/lib/widgets/login.dart b/lib/widgets/login.dart index aceaf66b52..088650e4f2 100644 --- a/lib/widgets/login.dart +++ b/lib/widgets/login.dart @@ -14,8 +14,8 @@ import '../generated/l10n/zulip_localizations.dart'; import '../log.dart'; import '../model/binding.dart'; import '../model/store.dart'; -import 'app.dart'; import 'dialog.dart'; +import 'home.dart'; import 'input.dart'; import 'page.dart'; import 'store.dart'; diff --git a/test/notifications/display_test.dart b/test/notifications/display_test.dart index 23b6fd8357..202beba5e5 100644 --- a/test/notifications/display_test.dart +++ b/test/notifications/display_test.dart @@ -21,6 +21,7 @@ import 'package:zulip/notifications/display.dart'; import 'package:zulip/notifications/receive.dart'; import 'package:zulip/widgets/app.dart'; import 'package:zulip/widgets/color.dart'; +import 'package:zulip/widgets/home.dart'; import 'package:zulip/widgets/inbox.dart'; import 'package:zulip/widgets/message_list.dart'; import 'package:zulip/widgets/page.dart'; diff --git a/test/widgets/app_test.dart b/test/widgets/app_test.dart index c21ac1915e..b3bd1eebe9 100644 --- a/test/widgets/app_test.dart +++ b/test/widgets/app_test.dart @@ -4,6 +4,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:zulip/log.dart'; import 'package:zulip/model/database.dart'; import 'package:zulip/widgets/app.dart'; +import 'package:zulip/widgets/home.dart'; import 'package:zulip/widgets/inbox.dart'; import 'package:zulip/widgets/page.dart'; diff --git a/test/widgets/login_test.dart b/test/widgets/login_test.dart index 3c194a3136..252740325e 100644 --- a/test/widgets/login_test.dart +++ b/test/widgets/login_test.dart @@ -11,6 +11,7 @@ import 'package:zulip/api/route/realm.dart'; import 'package:zulip/model/binding.dart'; import 'package:zulip/model/localizations.dart'; import 'package:zulip/widgets/app.dart'; +import 'package:zulip/widgets/home.dart'; import 'package:zulip/widgets/login.dart'; import 'package:zulip/widgets/page.dart'; From 7881245dbb3b81fea6d8b753a5e1123bc4986603 Mon Sep 17 00:00:00 2001 From: Zixuan James Li Date: Fri, 22 Nov 2024 12:22:38 -0500 Subject: [PATCH 3/4] action_sheet [nfc]: Centralize .withValues variants of contextMenuCancelBg Also switched to .fromMap for better readability Signed-off-by: Zixuan James Li --- lib/widgets/action_sheet.dart | 7 ++++--- lib/widgets/theme.dart | 11 +++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/widgets/action_sheet.dart b/lib/widgets/action_sheet.dart index c1e2c0e1f2..f9242cf006 100644 --- a/lib/widgets/action_sheet.dart +++ b/lib/widgets/action_sheet.dart @@ -169,9 +169,10 @@ class MessageActionSheetCancelButton extends StatelessWidget { foregroundColor: designVariables.contextMenuCancelText, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(7)), splashFactory: NoSplash.splashFactory, - ).copyWith(backgroundColor: WidgetStateColor.resolveWith((states) => - designVariables.contextMenuCancelBg.withFadedAlpha( - states.contains(WidgetState.pressed) ? 0.20 : 0.15))), + ).copyWith(backgroundColor: WidgetStateColor.fromMap({ + WidgetState.pressed: designVariables.contextMenuCancelPressedBg, + ~WidgetState.pressed: designVariables.contextMenuCancelBg, + })), onPressed: () { Navigator.pop(context); }, diff --git a/lib/widgets/theme.dart b/lib/widgets/theme.dart index 3ba5221245..13bb12b391 100644 --- a/lib/widgets/theme.dart +++ b/lib/widgets/theme.dart @@ -136,7 +136,8 @@ class DesignVariables extends ThemeExtension { title: const Color(0xff1a1a1a), channelColorSwatches: ChannelColorSwatches.light, atMentionMarker: const HSLColor.fromAHSL(0.5, 0, 0, 0.2).toColor(), - contextMenuCancelBg: const Color(0xff797986), + contextMenuCancelBg: const Color(0xff797986).withValues(alpha: 0.15), + contextMenuCancelPressedBg: const Color(0xff797986).withValues(alpha: 0.20), dmHeaderBg: const HSLColor.fromAHSL(1, 46, 0.35, 0.93).toColor(), groupDmConversationIcon: Colors.black.withValues(alpha: 0.5), groupDmConversationIconBg: const Color(0x33808080), @@ -175,7 +176,8 @@ class DesignVariables extends ThemeExtension { textInput: const Color(0xffffffff).withValues(alpha: 0.9), title: const Color(0xffffffff), channelColorSwatches: ChannelColorSwatches.dark, - contextMenuCancelBg: const Color(0xff797986), // the same as the light mode in Figma + contextMenuCancelBg: const Color(0xff797986).withValues(alpha: 0.15), // the same as the light mode in Figma + contextMenuCancelPressedBg: const Color(0xff797986).withValues(alpha: 0.20), // the same as the light mode in Figma // TODO(design-dark) need proper dark-theme color (this is ad hoc) atMentionMarker: const HSLColor.fromAHSL(0.4, 0, 0, 1).toColor(), dmHeaderBg: const HSLColor.fromAHSL(1, 46, 0.15, 0.2).toColor(), @@ -224,6 +226,7 @@ class DesignVariables extends ThemeExtension { required this.channelColorSwatches, required this.atMentionMarker, required this.contextMenuCancelBg, + required this.contextMenuCancelPressedBg, required this.dmHeaderBg, required this.groupDmConversationIcon, required this.groupDmConversationIconBg, @@ -276,6 +279,7 @@ class DesignVariables extends ThemeExtension { // Not named variables in Figma; taken from older Figma drafts, or elsewhere. final Color atMentionMarker; final Color contextMenuCancelBg; // In Figma, but unnamed. + final Color contextMenuCancelPressedBg; // In Figma, but unnamed. final Color dmHeaderBg; final Color groupDmConversationIcon; final Color groupDmConversationIconBg; @@ -315,6 +319,7 @@ class DesignVariables extends ThemeExtension { ChannelColorSwatches? channelColorSwatches, Color? atMentionMarker, Color? contextMenuCancelBg, + Color? contextMenuCancelPressedBg, Color? dmHeaderBg, Color? groupDmConversationIcon, Color? groupDmConversationIconBg, @@ -353,6 +358,7 @@ class DesignVariables extends ThemeExtension { channelColorSwatches: channelColorSwatches ?? this.channelColorSwatches, atMentionMarker: atMentionMarker ?? this.atMentionMarker, contextMenuCancelBg: contextMenuCancelBg ?? this.contextMenuCancelBg, + contextMenuCancelPressedBg: contextMenuCancelPressedBg ?? this.contextMenuCancelPressedBg, dmHeaderBg: dmHeaderBg ?? this.dmHeaderBg, groupDmConversationIcon: groupDmConversationIcon ?? this.groupDmConversationIcon, groupDmConversationIconBg: groupDmConversationIconBg ?? this.groupDmConversationIconBg, @@ -398,6 +404,7 @@ class DesignVariables extends ThemeExtension { channelColorSwatches: ChannelColorSwatches.lerp(channelColorSwatches, other.channelColorSwatches, t), atMentionMarker: Color.lerp(atMentionMarker, other.atMentionMarker, t)!, contextMenuCancelBg: Color.lerp(contextMenuCancelBg, other.contextMenuCancelBg, t)!, + contextMenuCancelPressedBg: Color.lerp(contextMenuCancelPressedBg, other.contextMenuCancelPressedBg, t)!, dmHeaderBg: Color.lerp(dmHeaderBg, other.dmHeaderBg, t)!, groupDmConversationIcon: Color.lerp(groupDmConversationIcon, other.groupDmConversationIcon, t)!, groupDmConversationIconBg: Color.lerp(groupDmConversationIconBg, other.groupDmConversationIconBg, t)!, From c216d510a2cd460c8a87d4a7bcaf03e3a72ab24d Mon Sep 17 00:00:00 2001 From: Zixuan James Li Date: Fri, 15 Nov 2024 11:41:09 -0500 Subject: [PATCH 4/4] icons: Add icons for bottom nav bar The icons are taken from Figma: https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=544-22131&node-type=canvas&t=f4vYPq2CZUpFuf7V-0 Signed-off-by: Zixuan James Li --- assets/icons/ZulipIcons.ttf | Bin 9532 -> 10604 bytes assets/icons/contacts.svg | 5 ++++ assets/icons/hash_italic.svg | 3 ++ assets/icons/inbox.svg | 3 ++ assets/icons/menu.svg | 5 ++++ lib/widgets/icons.dart | 52 +++++++++++++++++++++-------------- 6 files changed, 48 insertions(+), 20 deletions(-) create mode 100644 assets/icons/contacts.svg create mode 100644 assets/icons/hash_italic.svg create mode 100644 assets/icons/inbox.svg create mode 100644 assets/icons/menu.svg diff --git a/assets/icons/ZulipIcons.ttf b/assets/icons/ZulipIcons.ttf index 00faedbb398e415d76ea4347c8cb88b724452501..4f54b9a7305fe9734217d2b3b37ed2f80b3d1bbc 100644 GIT binary patch delta 2598 zcmb7GOKcle6uoa|JmYVi_;1>}@yysxW5;$J{}Q+9M-&Q31*OmxQZZ?oq|L8QT2fSD z3xQNsv{Gdu3P=bf7RaT18iZIB1hlZ~q99Qr$f80lic|rSSg^ne=Zz;Vh!q~6ciz44 zzWeUI?`GmF58oN75hEf8)k&o6gZrM?wKKQtRU%d<5`KSlUnaNz#=hr?q#js3H9B3L zWA{IFn8-2*-G%X~lZS4fmPU!}b)x%jO^j6!p16JOGHh=F#R({^k_2CL1<)s^7Z&Sm zZ54Y@L%uyVJG!kJ?C%P|ffb4q)78Z}Ho`U{fDq*Fnd2t07hv=4`74@h$jAU$oILa5Yv=Fuv!OnuUpcC#W9|2aVMbc3X{u%GeWw zmmE1^lYvD)?74>sbs)?r{P7TbjBqpHKB~FNc6Z@!R0t{p7aAOLBN^M{sKNs@LQm2r zBjYHJT+f#08>jo=sTaOfNc<2D$A#f#UAF=A9Q52egcGtzBVMD8nDX+eSaxE0i1yIq zD3c2tvov62mVm>1CAT484V&9V=T(yNMBzLOt0-36+4oT%mmnEw6mcQ^s+^Eiqn7*{ zN>nRDk;Fz`rVvVPg@XhNT?X@VdkqKN6?b644RU+YD0zzfaF|5^Dbt!)B#S3bK3MY% z%M`$@eD@3=ZN^Cngkb1WXg&$sdE^W^^E#}zRtlw0qg?%lu_P24Ka1O0cSDy(8ha&c zK$CQGrF(4+x&u_DBP-09 zL&S$St{sNUe-}(j_!xvGIXr(6I^Kvl)pI zlN%pO)eUgd-xtrOxAbCrCYR6(B{h{S59ou%#!>k>Bw%a&Cd)Pc$Im~5zyT96aMeT< z{IrRFFkUDI`oITG#KB`Gvfx7|(%^9uTfh@0dcl)z)KGb%BPR0TDH9p+v1U_a$1<#vEffr09!N<{!7zoA08_vW4_!$#Ac+mu}&`A@;rHFNe zrB^l)ap3QhRw8ZS9%yBA8@Lf#>28C%8*?__LD45ZFTQITw$v@R`*8WjFOowAGAObg-}=-0JnZ>~@z|*x%+>eO>VTTTQ?4vO)f~a@1uP1aBY^ z>kE0pVUN#cv$=eH7V2YTYGrpt(=e!YsI+jnwWO9Jk&-HU+N?V5&epFO@D)55{#x#A zOP$Iyor=;Hmfq-CmTI_v66@k6+Dbc6-3W8DfzoiBdG(S^88EqnrP+3-DJHA&e6hkx zn#se=ThbKvz9%3An;~JN27ZNy{Fe74PM?~(f3I!7xA>m-qE~DR8$36D=n6<6l(7kz z(tnh#wPur+7QN?%nrLKkO|0YpM<4A(9#K_RM1H{$0T(uF9=_*p3=9u&q^LG9Tv`o)0#&*+qrf2vcoIyUf3;r9hc`U zUI%-@;kC3bJ-Ea*Z}YXKkh^uv9TMw_=<1I)`x=Eay3eO)!c4GPSuC1Zz38w_P1zj3 z*j+CB>KB+n{*D(2t*-=Tn3>E)F^PW_VZ2evn=kS}+oAdYDFjv7#~Zkqk84kaJtA>G zEn~blPjY2jG)DC2uTX)m@<9)yc(PHzOX5v(FV%wuCw}XEtR1$q5-YPoHpD88Zv7FP zb#!)Sp*p%S-#DU23yz8E{KSFDh3eGgsLhzqPma%6Ca0_8W0uL8r)L+fQ`MRA;}9kM UAUh&WkIfu!H1%^b<8LMU8;uB#>Hq)$ delta 1585 zcmb7EO>7%Q6n?Xr-S{u`hcrza|E~Ww&f19`m%pm2RaGqlDo`Rgpdzzz96Q90oy1A8 z1UM(e0fDLo2`-2eY9$T@Ul7UxB&eziRYl4HP(X0w5Dwfbgc`n?ObS=L`u5Gd`QA70 zy&cc)%+^yqVnpPpO&Xx7i?h$4JN@;U&xqJ#MB24yXD16U+?`z@8u%5gmn%)D{r*e0 zu&xgi@fT~2?!xP*&%a6J-61-@S+6?t8^3?^Ikm&?`r8x)+Hf?Q4%RQRAQBkGJi6>OtH-`56^LS&k<)>>!ukH!t6_{bV^o$D#H5D7q0H+*(o>7YP}K-Q50o;OgCtocIj_6$3A7Z*)F@MnZ(IQCOtvV zk%r>Kcp_lOU=x(26s5uN9D2_xVas z$H^T4Y=GmG{sblQ+lE(+Y*?T4bGnAP_~;s#Wm=>w)crHKw+C;T99qJR$r&8-$G{Fy z5V;mHk0HM-vNEBWk>@r+QzwyP4h4zRFpcyrlv>26iBiEejK`6K;U3YQF$8DFO*@lJ zA!s5OkL0JrR=PMTc;6eZxl!HU3y4j%P4UK0Y*|)_!8!^d*g1tPY(-%lc3#1RttyDd z;7*aqz}6I!uyutL>|#&6fCR9lfV+Yk3KOtRg&gd%LL9cG5P=nUi$ES&Q4p8XszMlc zO~HcgDCE-^tSgA>HxwpeuP8)ey9$N=Tb>yv(j)u_gY*G2SeyN#Y1*82SNn^<&F|=@ zF=A{O-wi%F_>t$lr|r4tea5@vEBW5=-S%7lum11T-}HUU`_~Wu$ouC;-qrdao@lUs zcdQh^J@{q%6*p%+eU?{R?QUS9wc2z#<(2hTr>fT)t+}eFw%S^6m*<<_y0ca + + + + diff --git a/assets/icons/hash_italic.svg b/assets/icons/hash_italic.svg new file mode 100644 index 0000000000..cc094b5231 --- /dev/null +++ b/assets/icons/hash_italic.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/inbox.svg b/assets/icons/inbox.svg new file mode 100644 index 0000000000..5287079c64 --- /dev/null +++ b/assets/icons/inbox.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/menu.svg b/assets/icons/menu.svg new file mode 100644 index 0000000000..7f7226d3a3 --- /dev/null +++ b/assets/icons/menu.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/lib/widgets/icons.dart b/lib/widgets/icons.dart index ad18d73777..ea83562085 100644 --- a/lib/widgets/icons.dart +++ b/lib/widgets/icons.dart @@ -48,65 +48,77 @@ abstract final class ZulipIcons { /// The Zulip custom icon "clock". static const IconData clock = IconData(0xf108, fontFamily: "Zulip Icons"); + /// The Zulip custom icon "contacts". + static const IconData contacts = IconData(0xf109, fontFamily: "Zulip Icons"); + /// The Zulip custom icon "copy". - static const IconData copy = IconData(0xf109, fontFamily: "Zulip Icons"); + static const IconData copy = IconData(0xf10a, fontFamily: "Zulip Icons"); /// The Zulip custom icon "format_quote". - static const IconData format_quote = IconData(0xf10a, fontFamily: "Zulip Icons"); + static const IconData format_quote = IconData(0xf10b, fontFamily: "Zulip Icons"); /// The Zulip custom icon "globe". - static const IconData globe = IconData(0xf10b, fontFamily: "Zulip Icons"); + static const IconData globe = IconData(0xf10c, fontFamily: "Zulip Icons"); /// The Zulip custom icon "group_dm". - static const IconData group_dm = IconData(0xf10c, fontFamily: "Zulip Icons"); + static const IconData group_dm = IconData(0xf10d, fontFamily: "Zulip Icons"); + + /// The Zulip custom icon "hash_italic". + static const IconData hash_italic = IconData(0xf10e, fontFamily: "Zulip Icons"); /// The Zulip custom icon "hash_sign". - static const IconData hash_sign = IconData(0xf10d, fontFamily: "Zulip Icons"); + static const IconData hash_sign = IconData(0xf10f, fontFamily: "Zulip Icons"); /// The Zulip custom icon "image". - static const IconData image = IconData(0xf10e, fontFamily: "Zulip Icons"); + static const IconData image = IconData(0xf110, fontFamily: "Zulip Icons"); + + /// The Zulip custom icon "inbox". + static const IconData inbox = IconData(0xf111, fontFamily: "Zulip Icons"); /// The Zulip custom icon "language". - static const IconData language = IconData(0xf10f, fontFamily: "Zulip Icons"); + static const IconData language = IconData(0xf112, fontFamily: "Zulip Icons"); /// The Zulip custom icon "lock". - static const IconData lock = IconData(0xf110, fontFamily: "Zulip Icons"); + static const IconData lock = IconData(0xf113, fontFamily: "Zulip Icons"); + + /// The Zulip custom icon "menu". + static const IconData menu = IconData(0xf114, fontFamily: "Zulip Icons"); /// The Zulip custom icon "message_feed". - static const IconData message_feed = IconData(0xf111, fontFamily: "Zulip Icons"); + static const IconData message_feed = IconData(0xf115, fontFamily: "Zulip Icons"); /// The Zulip custom icon "mute". - static const IconData mute = IconData(0xf112, fontFamily: "Zulip Icons"); + static const IconData mute = IconData(0xf116, fontFamily: "Zulip Icons"); /// The Zulip custom icon "read_receipts". - static const IconData read_receipts = IconData(0xf113, fontFamily: "Zulip Icons"); + static const IconData read_receipts = IconData(0xf117, fontFamily: "Zulip Icons"); /// The Zulip custom icon "send". - static const IconData send = IconData(0xf114, fontFamily: "Zulip Icons"); + static const IconData send = IconData(0xf118, fontFamily: "Zulip Icons"); /// The Zulip custom icon "share". - static const IconData share = IconData(0xf115, fontFamily: "Zulip Icons"); + static const IconData share = IconData(0xf119, fontFamily: "Zulip Icons"); /// The Zulip custom icon "share_ios". - static const IconData share_ios = IconData(0xf116, fontFamily: "Zulip Icons"); + static const IconData share_ios = IconData(0xf11a, fontFamily: "Zulip Icons"); /// The Zulip custom icon "smile". - static const IconData smile = IconData(0xf117, fontFamily: "Zulip Icons"); + static const IconData smile = IconData(0xf11b, fontFamily: "Zulip Icons"); /// The Zulip custom icon "star". - static const IconData star = IconData(0xf118, fontFamily: "Zulip Icons"); + static const IconData star = IconData(0xf11c, fontFamily: "Zulip Icons"); /// The Zulip custom icon "star_filled". - static const IconData star_filled = IconData(0xf119, fontFamily: "Zulip Icons"); + static const IconData star_filled = IconData(0xf11d, fontFamily: "Zulip Icons"); /// The Zulip custom icon "topic". - static const IconData topic = IconData(0xf11a, fontFamily: "Zulip Icons"); + static const IconData topic = IconData(0xf11e, fontFamily: "Zulip Icons"); /// The Zulip custom icon "unmute". - static const IconData unmute = IconData(0xf11b, fontFamily: "Zulip Icons"); + static const IconData unmute = IconData(0xf11f, fontFamily: "Zulip Icons"); /// The Zulip custom icon "user". - static const IconData user = IconData(0xf11c, fontFamily: "Zulip Icons"); + static const IconData user = IconData(0xf120, fontFamily: "Zulip Icons"); // END GENERATED ICON DATA }