Skip to content

Commit c317fbe

Browse files
committed
topicList: Add topic list page for each channel
fixes: zulip#1158
1 parent e524e6b commit c317fbe

16 files changed

+491
-42
lines changed

assets/icons/ZulipIcons.ttf

140 Bytes
Binary file not shown.

assets/icons/chevron_down.svg

+3
Loading

assets/l10n/app_en.arb

+12
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@
108108
"@actionSheetOptionUnresolveTopic": {
109109
"description": "Label for the 'Mark as unresolved' button on the topic action sheet."
110110
},
111+
"actionSheetOptionTopicList": "Topic list",
112+
"@actionSheetOptionTopicList": {
113+
"description": "Label for a button in the channel action sheet that opens the list of topics in the channel"
114+
},
111115
"errorResolveTopicFailedTitle": "Failed to mark topic as resolved",
112116
"@errorResolveTopicFailedTitle": {
113117
"description": "Error title when marking a topic as resolved failed."
@@ -855,6 +859,14 @@
855859
"@emojiPickerSearchEmoji": {
856860
"description": "Hint text for the emoji picker search text field."
857861
},
862+
"errorFetchingTopics": "Error fetching topics",
863+
"@errorFetchingTopics": {
864+
"description": "Error title when fetching the topics failed."
865+
},
866+
"noTopicsInChannel": "No topics in the channel",
867+
"@noTopicsInChannel": {
868+
"description": "Text to show when a channel has no topics."
869+
},
858870
"noEarlierMessages": "No earlier messages",
859871
"@noEarlierMessages": {
860872
"description": "Text to show at the start of a message list if there are no earlier messages."

lib/generated/l10n/zulip_localizations.dart

+18
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,12 @@ abstract class ZulipLocalizations {
267267
/// **'Mark as unresolved'**
268268
String get actionSheetOptionUnresolveTopic;
269269

270+
/// Label for a button in the channel action sheet that opens the list of topics in the channel
271+
///
272+
/// In en, this message translates to:
273+
/// **'Topic list'**
274+
String get actionSheetOptionTopicList;
275+
270276
/// Error title when marking a topic as resolved failed.
271277
///
272278
/// In en, this message translates to:
@@ -1251,6 +1257,18 @@ abstract class ZulipLocalizations {
12511257
/// **'Search emoji'**
12521258
String get emojiPickerSearchEmoji;
12531259

1260+
/// Error title when fetching the topics failed.
1261+
///
1262+
/// In en, this message translates to:
1263+
/// **'Error fetching topics'**
1264+
String get errorFetchingTopics;
1265+
1266+
/// Text to show when a channel has no topics.
1267+
///
1268+
/// In en, this message translates to:
1269+
/// **'No topics in the channel'**
1270+
String get noTopicsInChannel;
1271+
12541272
/// Text to show at the start of a message list if there are no earlier messages.
12551273
///
12561274
/// In en, this message translates to:

lib/generated/l10n/zulip_localizations_ar.dart

+9
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ class ZulipLocalizationsAr extends ZulipLocalizations {
9191
@override
9292
String get actionSheetOptionUnresolveTopic => 'Mark as unresolved';
9393

94+
@override
95+
String get actionSheetOptionTopicList => 'Topic list';
96+
9497
@override
9598
String get errorResolveTopicFailedTitle => 'Failed to mark topic as resolved';
9699

@@ -667,6 +670,12 @@ class ZulipLocalizationsAr extends ZulipLocalizations {
667670
@override
668671
String get emojiPickerSearchEmoji => 'Search emoji';
669672

673+
@override
674+
String get errorFetchingTopics => 'Error fetching topics';
675+
676+
@override
677+
String get noTopicsInChannel => 'No topics in the channel';
678+
670679
@override
671680
String get noEarlierMessages => 'No earlier messages';
672681

lib/generated/l10n/zulip_localizations_en.dart

+9
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ class ZulipLocalizationsEn extends ZulipLocalizations {
9191
@override
9292
String get actionSheetOptionUnresolveTopic => 'Mark as unresolved';
9393

94+
@override
95+
String get actionSheetOptionTopicList => 'Topic list';
96+
9497
@override
9598
String get errorResolveTopicFailedTitle => 'Failed to mark topic as resolved';
9699

@@ -667,6 +670,12 @@ class ZulipLocalizationsEn extends ZulipLocalizations {
667670
@override
668671
String get emojiPickerSearchEmoji => 'Search emoji';
669672

673+
@override
674+
String get errorFetchingTopics => 'Error fetching topics';
675+
676+
@override
677+
String get noTopicsInChannel => 'No topics in the channel';
678+
670679
@override
671680
String get noEarlierMessages => 'No earlier messages';
672681

lib/generated/l10n/zulip_localizations_ja.dart

+9
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ class ZulipLocalizationsJa extends ZulipLocalizations {
9191
@override
9292
String get actionSheetOptionUnresolveTopic => 'Mark as unresolved';
9393

94+
@override
95+
String get actionSheetOptionTopicList => 'Topic list';
96+
9497
@override
9598
String get errorResolveTopicFailedTitle => 'Failed to mark topic as resolved';
9699

@@ -667,6 +670,12 @@ class ZulipLocalizationsJa extends ZulipLocalizations {
667670
@override
668671
String get emojiPickerSearchEmoji => 'Search emoji';
669672

673+
@override
674+
String get errorFetchingTopics => 'Error fetching topics';
675+
676+
@override
677+
String get noTopicsInChannel => 'No topics in the channel';
678+
670679
@override
671680
String get noEarlierMessages => 'No earlier messages';
672681

lib/generated/l10n/zulip_localizations_nb.dart

+9
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ class ZulipLocalizationsNb extends ZulipLocalizations {
9191
@override
9292
String get actionSheetOptionUnresolveTopic => 'Mark as unresolved';
9393

94+
@override
95+
String get actionSheetOptionTopicList => 'Topic list';
96+
9497
@override
9598
String get errorResolveTopicFailedTitle => 'Failed to mark topic as resolved';
9699

@@ -667,6 +670,12 @@ class ZulipLocalizationsNb extends ZulipLocalizations {
667670
@override
668671
String get emojiPickerSearchEmoji => 'Search emoji';
669672

673+
@override
674+
String get errorFetchingTopics => 'Error fetching topics';
675+
676+
@override
677+
String get noTopicsInChannel => 'No topics in the channel';
678+
670679
@override
671680
String get noEarlierMessages => 'No earlier messages';
672681

lib/generated/l10n/zulip_localizations_pl.dart

+9
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ class ZulipLocalizationsPl extends ZulipLocalizations {
9191
@override
9292
String get actionSheetOptionUnresolveTopic => 'Oznacz brak rozwiązania';
9393

94+
@override
95+
String get actionSheetOptionTopicList => 'Topic list';
96+
9497
@override
9598
String get errorResolveTopicFailedTitle => 'Nie udało się oznaczyć jako rozwiązany';
9699

@@ -667,6 +670,12 @@ class ZulipLocalizationsPl extends ZulipLocalizations {
667670
@override
668671
String get emojiPickerSearchEmoji => 'Szukaj emoji';
669672

673+
@override
674+
String get errorFetchingTopics => 'Error fetching topics';
675+
676+
@override
677+
String get noTopicsInChannel => 'No topics in the channel';
678+
670679
@override
671680
String get noEarlierMessages => 'Brak historii';
672681

lib/generated/l10n/zulip_localizations_ru.dart

+9
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ class ZulipLocalizationsRu extends ZulipLocalizations {
9191
@override
9292
String get actionSheetOptionUnresolveTopic => 'Mark as unresolved';
9393

94+
@override
95+
String get actionSheetOptionTopicList => 'Topic list';
96+
9497
@override
9598
String get errorResolveTopicFailedTitle => 'Failed to mark topic as resolved';
9699

@@ -667,6 +670,12 @@ class ZulipLocalizationsRu extends ZulipLocalizations {
667670
@override
668671
String get emojiPickerSearchEmoji => 'Поиск эмодзи';
669672

673+
@override
674+
String get errorFetchingTopics => 'Error fetching topics';
675+
676+
@override
677+
String get noTopicsInChannel => 'No topics in the channel';
678+
670679
@override
671680
String get noEarlierMessages => 'No earlier messages';
672681

lib/generated/l10n/zulip_localizations_sk.dart

+9
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ class ZulipLocalizationsSk extends ZulipLocalizations {
9191
@override
9292
String get actionSheetOptionUnresolveTopic => 'Mark as unresolved';
9393

94+
@override
95+
String get actionSheetOptionTopicList => 'Topic list';
96+
9497
@override
9598
String get errorResolveTopicFailedTitle => 'Failed to mark topic as resolved';
9699

@@ -667,6 +670,12 @@ class ZulipLocalizationsSk extends ZulipLocalizations {
667670
@override
668671
String get emojiPickerSearchEmoji => 'Hľadať emotikon';
669672

673+
@override
674+
String get errorFetchingTopics => 'Error fetching topics';
675+
676+
@override
677+
String get noTopicsInChannel => 'No topics in the channel';
678+
670679
@override
671680
String get noEarlierMessages => 'No earlier messages';
672681

lib/model/topic_list.dart

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import 'package:flutter/foundation.dart';
2+
import '../api/route/channels.dart';
3+
import '../model/store.dart';
4+
5+
class TopicListView extends ChangeNotifier {
6+
TopicListView({required this.store, required this.streamId});
7+
8+
final PerAccountStore store;
9+
final int streamId;
10+
11+
bool _isLoading = true;
12+
bool _hasError = false;
13+
String _errorMessage = '';
14+
List<GetStreamTopicsEntry>? _topics;
15+
16+
bool get isLoading => _isLoading;
17+
bool get hasError => _hasError;
18+
String get errorMessage => _errorMessage;
19+
List<GetStreamTopicsEntry>? get topics => _topics;
20+
21+
Future<void> fetchTopics() async {
22+
_isLoading = true;
23+
_hasError = false;
24+
_errorMessage = '';
25+
notifyListeners();
26+
27+
try {
28+
final response = await getStreamTopics(store.connection, streamId: streamId);
29+
_topics = response.topics;
30+
_isLoading = false;
31+
} catch (e) {
32+
_isLoading = false;
33+
_hasError = true;
34+
_errorMessage = e.toString();
35+
}
36+
notifyListeners();
37+
}
38+
}

lib/widgets/action_sheet.dart

+33-9
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import 'page.dart';
2828
import 'store.dart';
2929
import 'text.dart';
3030
import 'theme.dart';
31+
import 'topic_list.dart';
3132

3233
void _showActionSheet(
3334
BuildContext context, {
@@ -173,23 +174,46 @@ void showChannelActionSheet(BuildContext context, {
173174
final store = PerAccountStoreWidget.of(pageContext);
174175

175176
final optionButtons = <ActionSheetMenuItemButton>[];
177+
178+
optionButtons.add(
179+
TopicListButton(pageContext: pageContext, channelId: channelId));
180+
176181
final unreadCount = store.unreads.countInChannelNarrow(channelId);
177182
if (unreadCount > 0) {
178183
optionButtons.add(
179184
MarkChannelAsReadButton(pageContext: pageContext, channelId: channelId));
180185
}
181-
if (optionButtons.isEmpty) {
182-
// TODO(a11y): This case makes a no-op gesture handler; as a consequence,
183-
// we're presenting some UI (to people who use screen-reader software) as
184-
// though it offers a gesture interaction that it doesn't meaningfully
185-
// offer, which is confusing. The solution here is probably to remove this
186-
// is-empty case by having at least one button that's always present,
187-
// such as "copy link to channel".
188-
return;
189-
}
186+
190187
_showActionSheet(pageContext, optionButtons: optionButtons);
191188
}
192189

190+
class TopicListButton extends ActionSheetMenuItemButton {
191+
const TopicListButton({
192+
super.key,
193+
required this.channelId,
194+
required super.pageContext,
195+
});
196+
197+
final int channelId;
198+
199+
@override
200+
IconData get icon => ZulipIcons.message_feed;
201+
202+
@override
203+
String label(ZulipLocalizations zulipLocalizations) {
204+
return zulipLocalizations.actionSheetOptionTopicList;
205+
}
206+
207+
@override
208+
void onPressed() {
209+
Navigator.push(pageContext,
210+
TopicListPage.buildRoute(
211+
context: pageContext,
212+
streamId: channelId,
213+
));
214+
}
215+
}
216+
193217
class MarkChannelAsReadButton extends ActionSheetMenuItemButton {
194218
const MarkChannelAsReadButton({
195219
super.key,

0 commit comments

Comments
 (0)