diff --git a/assets/icons/ZulipIcons.ttf b/assets/icons/ZulipIcons.ttf
index 00faedbb39..4f54b9a730 100644
Binary files a/assets/icons/ZulipIcons.ttf and b/assets/icons/ZulipIcons.ttf differ
diff --git a/assets/icons/contacts.svg b/assets/icons/contacts.svg
new file mode 100644
index 0000000000..26f209508a
--- /dev/null
+++ b/assets/icons/contacts.svg
@@ -0,0 +1,5 @@
+
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/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/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/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
}
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/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/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())),
+ ]));
}
}
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)!,
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';