Skip to content

Commit

Permalink
channel_list: Setup "All Channels" page
Browse files Browse the repository at this point in the history
Fixes parts of zulip#188
  • Loading branch information
Khader-1 committed Aug 6, 2024
1 parent 99865f4 commit ea790b7
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 2 deletions.
11 changes: 11 additions & 0 deletions assets/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,13 @@
"num": {"type": "int", "example": "4"}
}
},
"browseMoreNChannels": "Browse {num, plural, =0{all channels} =1{1 more channel} other{{num} more channels}}",
"@browseMoreNChannels": {
"description": "Label showing the number of other channels that user can subscribe to",
"placeholders": {
"num": {"type": "int", "example": "4"}
}
},
"errorInvalidResponse": "The server sent an invalid response",
"@errorInvalidResponse": {
"description": "Error message when an API call returned an invalid response."
Expand Down Expand Up @@ -480,6 +487,10 @@
"@mentionsPageTitle": {
"description": "Title for the page of @-mentions."
},
"channelListPageTitle": "All Channels",
"@channelListPageTitle": {
"description": "Title for the page of all channels."
},
"notifGroupDmConversationLabel": "{senderFullName} to you and {numOthers, plural, =1{1 other} other{{numOthers} others}}",
"@notifGroupDmConversationLabel": {
"description": "Label for a group DM conversation notification.",
Expand Down
74 changes: 74 additions & 0 deletions lib/widgets/channel_list.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/zulip_localizations.dart';

import '../api/model/model.dart';
import '../model/narrow.dart';
import 'icons.dart';
import 'message_list.dart';
import 'page.dart';
import 'store.dart';

class ChannelListPage extends StatelessWidget {
const ChannelListPage({super.key});

static Route<void> buildRoute({int? accountId, BuildContext? context}) {
return MaterialAccountWidgetRoute(accountId: accountId, context: context,
page: const ChannelListPage());
}

@override
Widget build(BuildContext context) {
final store = PerAccountStoreWidget.of(context);
final zulipLocalizations = ZulipLocalizations.of(context);
final streams = store.streams.values.toList();
return Scaffold(
appBar: AppBar(title: Text(zulipLocalizations.channelListPageTitle)),
body: SafeArea(
child: ListView.builder(
itemCount: streams.length,
itemBuilder: (context, index) => ChannelItem(stream: streams[index]))));
}
}

@visibleForTesting
class ChannelItem extends StatelessWidget {
const ChannelItem({super.key, required this.stream});

final ZulipStream stream;

@override
Widget build(BuildContext context) {
return Material(
color: Colors.white,
child: InkWell(
onTap: () => Navigator.push(context, MessageListPage.buildRoute(context: context,
narrow: ChannelNarrow(stream.streamId))),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
child: Row(children: [
Icon(size: 16, iconDataForStream(stream)),
const SizedBox(width: 8),
Expanded(child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(stream.name,
style: const TextStyle(
fontSize: 18,
height: (20 / 18),
// TODO(#95) need dark-theme color
color: Color(0xFF262626)),
maxLines: 1,
overflow: TextOverflow.ellipsis),
if (stream.description.isNotEmpty) Text(
stream.description,
style: const TextStyle(
fontSize: 12,
// TODO(#95) need dark-theme color
color: Color(0xCC262626)),
maxLines: 1,
overflow: TextOverflow.ellipsis),
])),
]))));
}
}
39 changes: 38 additions & 1 deletion lib/widgets/subscription_list.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/zulip_localizations.dart';

import '../api/model/model.dart';
import '../model/narrow.dart';
Expand All @@ -7,6 +8,7 @@ import 'icons.dart';
import 'message_list.dart';
import 'page.dart';
import 'store.dart';
import 'channel_list.dart';
import 'text.dart';
import 'theme.dart';
import 'unread_count_badge.dart';
Expand Down Expand Up @@ -106,7 +108,7 @@ class _SubscriptionListPageState extends State<SubscriptionListPage> with PerAcc
_SubscriptionList(unreadsModel: unreadsModel, subscriptions: unpinned),
],

// TODO(#188): add button leading to "All Streams" page with ability to subscribe
if (store.streams.isNotEmpty) const _ChannelListLinkItem(),

// This ensures last item in scrollable can settle in an unobstructed area.
const SliverSafeArea(sliver: SliverToBoxAdapter(child: SizedBox.shrink())),
Expand Down Expand Up @@ -195,6 +197,41 @@ class _SubscriptionList extends StatelessWidget {
}
}

class _ChannelListLinkItem extends StatelessWidget {
const _ChannelListLinkItem();

@override
Widget build(BuildContext context) {
final store = PerAccountStoreWidget.of(context);
final notShownStreams = store.streams.length - store.subscriptions.length;
final zulipLocalizations = ZulipLocalizations.of(context);

return SliverToBoxAdapter(
child: Material(
// TODO(#95) need dark-theme color
color: Colors.white,
child: InkWell(
onTap: () => Navigator.push(context,
ChannelListPage.buildRoute(context: context)),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
style: const TextStyle(
fontSize: 18,
height: (20 / 18),
// TODO(#95) need dark-theme color
color: Color(0xFF262626),
).merge(weightVariableTextStyle(context, wght: 600)),
zulipLocalizations.browseMoreNChannels(notShownStreams)),
const Icon(Icons.arrow_forward_ios, size: 18),
])))));
}
}

@visibleForTesting
class SubscriptionItem extends StatelessWidget {
const SubscriptionItem({
Expand Down
42 changes: 42 additions & 0 deletions test/widgets/channel_list_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import 'package:checks/checks.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:zulip/api/model/model.dart';
import 'package:zulip/widgets/channel_list.dart';

import '../model/binding.dart';
import '../example_data.dart' as eg;
import 'test_app.dart';

void main() {
TestZulipBinding.ensureInitialized();

Future<void> setupChannelListPage(WidgetTester tester, {
required List<ZulipStream> streams, required List<Subscription> subscriptions}) async {
addTearDown(testBinding.reset);
final initialSnapshot = eg.initialSnapshot(
subscriptions: subscriptions,
streams: streams.toList(),
);
await testBinding.globalStore.add(eg.selfAccount, initialSnapshot);

await tester.pumpWidget(TestZulipApp(accountId: eg.selfAccount.id, child: const ChannelListPage()));

// global store, per-account store
await tester.pumpAndSettle();
}

int getItemCount() {
return find.byType(ChannelItem).evaluate().length;
}

testWidgets('smoke', (tester) async {
await setupChannelListPage(tester, streams: [], subscriptions: []);
check(getItemCount()).equals(0);
});

testWidgets('basic list', (tester) async {
final streams = List.generate(3, (index) => eg.stream());
await setupChannelListPage(tester, streams: streams, subscriptions: []);
check(getItemCount()).equals(3);
});
}
21 changes: 20 additions & 1 deletion test/widgets/subscription_list_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:zulip/api/model/initial_snapshot.dart';
import 'package:zulip/api/model/model.dart';
import 'package:zulip/model/localizations.dart';
import 'package:zulip/widgets/icons.dart';
import 'package:zulip/widgets/channel_colors.dart';
import 'package:zulip/widgets/subscription_list.dart';
Expand All @@ -20,11 +21,12 @@ void main() {
required List<Subscription> subscriptions,
List<UserTopicItem> userTopics = const [],
UnreadMessagesSnapshot? unreadMsgs,
List<ZulipStream>? streams,
}) async {
addTearDown(testBinding.reset);
final initialSnapshot = eg.initialSnapshot(
subscriptions: subscriptions,
streams: subscriptions,
streams: streams ?? subscriptions,
userTopics: userTopics,
unreadMsgs: unreadMsgs,
);
Expand Down Expand Up @@ -56,6 +58,23 @@ void main() {
check(isUnpinnedHeaderInTree()).isFalse();
});

testWidgets('link to other channels is shown', (tester) async {
final zulipLocalizations = GlobalLocalizations.zulipLocalizations;
final streams = List.generate(5, (index) => eg.stream());
await setupStreamListPage(tester,
streams: streams,
subscriptions: [eg.subscription(streams[1])]);

check(find.text(zulipLocalizations.browseMoreNChannels(4)).evaluate()).isNotEmpty();
});

testWidgets('link to other channels is not shown if server has no visible channels', (tester) async {
final zulipLocalizations = GlobalLocalizations.zulipLocalizations;
await setupStreamListPage(tester, streams: [], subscriptions: []);

check(find.text(zulipLocalizations.browseMoreNChannels(0)).evaluate()).isEmpty();
});

testWidgets('basic subscriptions', (tester) async {
await setupStreamListPage(tester, subscriptions: [
eg.subscription(eg.stream(streamId: 1), pinToTop: true),
Expand Down

0 comments on commit ea790b7

Please sign in to comment.