Skip to content

Commit 24ed3b6

Browse files
author
chimnayajith
committed
inbox: Add label for archived channels in headers
Fixes #800
1 parent 7fe4ce8 commit 24ed3b6

13 files changed

+160
-22
lines changed

Diff for: assets/l10n/app_en.arb

+4
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,10 @@
364364
"@unknownChannelName": {
365365
"description": "Replacement name for channel when it cannot be found in the store."
366366
},
367+
"channelArchivedLabel": "(archived)",
368+
"@channelArchivedLabel": {
369+
"description": "Label shown next to an archived channel's name in headers."
370+
},
367371
"composeBoxTopicHintText": "Topic",
368372
"@composeBoxTopicHintText": {
369373
"description": "Hint text for topic input widget in compose box."

Diff for: lib/generated/l10n/zulip_localizations.dart

+6
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,12 @@ abstract class ZulipLocalizations {
585585
/// **'(unknown channel)'**
586586
String get unknownChannelName;
587587

588+
/// Label shown next to an archived channel's name in headers.
589+
///
590+
/// In en, this message translates to:
591+
/// **'(archived)'**
592+
String get channelArchivedLabel;
593+
588594
/// Hint text for topic input widget in compose box.
589595
///
590596
/// In en, this message translates to:

Diff for: lib/generated/l10n/zulip_localizations_ar.dart

+3
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,9 @@ class ZulipLocalizationsAr extends ZulipLocalizations {
286286
@override
287287
String get unknownChannelName => '(unknown channel)';
288288

289+
@override
290+
String get channelArchivedLabel => '(archived)';
291+
289292
@override
290293
String get composeBoxTopicHintText => 'Topic';
291294

Diff for: lib/generated/l10n/zulip_localizations_en.dart

+3
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,9 @@ class ZulipLocalizationsEn extends ZulipLocalizations {
286286
@override
287287
String get unknownChannelName => '(unknown channel)';
288288

289+
@override
290+
String get channelArchivedLabel => '(archived)';
291+
289292
@override
290293
String get composeBoxTopicHintText => 'Topic';
291294

Diff for: lib/generated/l10n/zulip_localizations_ja.dart

+3
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,9 @@ class ZulipLocalizationsJa extends ZulipLocalizations {
286286
@override
287287
String get unknownChannelName => '(unknown channel)';
288288

289+
@override
290+
String get channelArchivedLabel => '(archived)';
291+
289292
@override
290293
String get composeBoxTopicHintText => 'Topic';
291294

Diff for: lib/generated/l10n/zulip_localizations_nb.dart

+3
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,9 @@ class ZulipLocalizationsNb extends ZulipLocalizations {
286286
@override
287287
String get unknownChannelName => '(unknown channel)';
288288

289+
@override
290+
String get channelArchivedLabel => '(archived)';
291+
289292
@override
290293
String get composeBoxTopicHintText => 'Topic';
291294

Diff for: lib/generated/l10n/zulip_localizations_pl.dart

+3
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,9 @@ class ZulipLocalizationsPl extends ZulipLocalizations {
286286
@override
287287
String get unknownChannelName => '(nieznany kanał)';
288288

289+
@override
290+
String get channelArchivedLabel => '(archived)';
291+
289292
@override
290293
String get composeBoxTopicHintText => 'Wątek';
291294

Diff for: lib/generated/l10n/zulip_localizations_ru.dart

+3
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,9 @@ class ZulipLocalizationsRu extends ZulipLocalizations {
286286
@override
287287
String get unknownChannelName => '(unknown channel)';
288288

289+
@override
290+
String get channelArchivedLabel => '(archived)';
291+
289292
@override
290293
String get composeBoxTopicHintText => 'Тема';
291294

Diff for: lib/generated/l10n/zulip_localizations_sk.dart

+3
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,9 @@ class ZulipLocalizationsSk extends ZulipLocalizations {
286286
@override
287287
String get unknownChannelName => '(unknown channel)';
288288

289+
@override
290+
String get channelArchivedLabel => '(archived)';
291+
289292
@override
290293
String get composeBoxTopicHintText => 'Topic';
291294

Diff for: lib/widgets/inbox.dart

+29-9
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ abstract class _HeaderItem extends StatelessWidget {
222222
final _InboxPageState pageState;
223223
final int count;
224224
final bool hasMention;
225-
225+
final bool isArchived;
226226
/// A build context within the [_StreamSection] or [_AllDmsSection].
227227
///
228228
/// Used to ensure the [_StreamSection] or [_AllDmsSection] that encloses the
@@ -236,6 +236,7 @@ abstract class _HeaderItem extends StatelessWidget {
236236
required this.count,
237237
required this.hasMention,
238238
required this.sectionContext,
239+
this.isArchived = false,
239240
});
240241

241242
String title(ZulipLocalizations zulipLocalizations);
@@ -284,16 +285,33 @@ abstract class _HeaderItem extends StatelessWidget {
284285
const SizedBox(width: 5),
285286
Expanded(child: Padding(
286287
padding: const EdgeInsets.symmetric(vertical: 4),
287-
child: Text(
288-
style: TextStyle(
289-
fontSize: 17,
290-
height: (20 / 17),
291-
// TODO(design) check if this is the right variable
292-
color: designVariables.labelMenuButton,
293-
).merge(weightVariableTextStyle(context, wght: 600)),
288+
child: RichText(
294289
maxLines: 1,
295290
overflow: TextOverflow.ellipsis,
296-
title(zulipLocalizations)))),
291+
text: TextSpan(
292+
children: [
293+
TextSpan(
294+
text: title(zulipLocalizations),
295+
style: TextStyle(
296+
fontSize: 17,
297+
height: (20 / 17),
298+
color: designVariables.labelMenuButton,
299+
).merge(weightVariableTextStyle(context, wght: 600)),
300+
),
301+
if (isArchived)
302+
TextSpan(
303+
text: ' ${zulipLocalizations.channelArchivedLabel}',
304+
style: TextStyle(
305+
fontSize: 17,
306+
height: (20 / 17),
307+
color: MessageListTheme.of(context).streamRecipientHeaderChevronRight,
308+
fontStyle: FontStyle.italic,
309+
),
310+
),
311+
],
312+
),
313+
),
314+
)),
297315
const SizedBox(width: 12),
298316
if (hasMention) const _IconMarker(icon: ZulipIcons.at_sign),
299317
Padding(padding: const EdgeInsetsDirectional.only(end: 16),
@@ -441,6 +459,7 @@ class _StreamHeaderItem extends _HeaderItem {
441459
required super.count,
442460
required super.hasMention,
443461
required super.sectionContext,
462+
required super.isArchived,
444463
});
445464

446465
@override String title(ZulipLocalizations zulipLocalizations) =>
@@ -487,6 +506,7 @@ class _StreamSection extends StatelessWidget {
487506
collapsed: collapsed,
488507
pageState: pageState,
489508
sectionContext: context,
509+
isArchived: subscription.isArchived,
490510
);
491511
return StickyHeaderItem(
492512
header: header,

Diff for: lib/widgets/message_list.dart

+13
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,19 @@ class StreamMessageRecipientHeader extends StatelessWidget {
11011101
style: recipientHeaderTextStyle(context),
11021102
overflow: TextOverflow.ellipsis),
11031103
),
1104+
if (stream?.isArchived ?? false)
1105+
Padding(
1106+
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 4),
1107+
child: Text(
1108+
zulipLocalizations.channelArchivedLabel,
1109+
style: recipientHeaderTextStyle(context).copyWith(
1110+
color: messageListTheme.streamRecipientHeaderChevronRight,
1111+
fontStyle: FontStyle.italic,
1112+
),
1113+
overflow: TextOverflow.ellipsis,
1114+
maxLines: 1,
1115+
),
1116+
),
11041117
Padding(
11051118
// Figma has 5px horizontal padding around an 8px wide icon.
11061119
// Icon is 16px wide here so horizontal padding is 1px.

Diff for: test/widgets/inbox_test.dart

+63-12
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:flutter_checks/flutter_checks.dart';
44
import 'package:flutter_test/flutter_test.dart';
55
import 'package:zulip/api/model/events.dart';
66
import 'package:zulip/api/model/model.dart';
7+
import 'package:zulip/model/localizations.dart';
78
import 'package:zulip/model/store.dart';
89
import 'package:zulip/widgets/color.dart';
910
import 'package:zulip/widgets/home.dart';
@@ -133,7 +134,14 @@ void main() {
133134

134135
/// Find the all-DMs header element.
135136
Widget? findAllDmsHeaderRow(WidgetTester tester) {
136-
return findRowByLabel(tester, 'Direct messages');
137+
final finder = find.ancestor(
138+
of: find.byWidgetPredicate((widget) =>
139+
widget is RichText &&
140+
widget.text.toPlainText().contains('Direct messages')
141+
),
142+
matching: find.byType(Row),
143+
);
144+
return finder.evaluate().isNotEmpty ? tester.widget(finder.first) : null;
137145
}
138146

139147
Color? allDmsHeaderBackgroundColor(WidgetTester tester) {
@@ -149,7 +157,15 @@ void main() {
149157
/// For the given stream ID, find the stream header element.
150158
Widget? findStreamHeaderRow(WidgetTester tester, int streamId) {
151159
final stream = store.streams[streamId]!;
152-
return findRowByLabel(tester, stream.name);
160+
return tester.widget<Row>(
161+
find.ancestor(
162+
of: find.byWidgetPredicate((widget) =>
163+
widget is RichText &&
164+
widget.text.toPlainText().contains(stream.name)
165+
),
166+
matching: find.byType(Row),
167+
).first
168+
);
153169
}
154170

155171
Color? streamHeaderBackgroundColor(WidgetTester tester, int streamId) {
@@ -259,8 +275,13 @@ void main() {
259275
final subscription = eg.subscription(stream);
260276
const topic = 'lunch';
261277

262-
bool hasAtSign(WidgetTester tester, Widget? parent) =>
263-
hasIcon(tester, parent: parent, icon: ZulipIcons.at_sign);
278+
bool hasAtSign(WidgetTester tester, {required Widget? parent}) {
279+
if (parent == null) return false;
280+
return tester.widgetList(find.descendant(
281+
of: find.byWidget(parent),
282+
matching: find.byIcon(ZulipIcons.at_sign),
283+
)).isNotEmpty;
284+
}
264285

265286
testWidgets('topic with a mention', (tester) async {
266287
await setupPage(tester,
@@ -269,9 +290,9 @@ void main() {
269290
unreadMessages: [eg.streamMessage(stream: stream, topic: topic,
270291
flags: [MessageFlag.mentioned])]);
271292

272-
check(hasAtSign(tester, findStreamHeaderRow(tester, stream.streamId)))
293+
check(hasAtSign(tester, parent: findStreamHeaderRow(tester, stream.streamId)))
273294
.isTrue();
274-
check(hasAtSign(tester, findRowByLabel(tester, topic))).isTrue();
295+
check(hasAtSign(tester, parent: findRowByLabel(tester, topic))).isTrue();
275296
});
276297

277298
testWidgets('topic without a mention', (tester) async {
@@ -281,9 +302,9 @@ void main() {
281302
unreadMessages: [eg.streamMessage(stream: stream, topic: topic,
282303
flags: [])]);
283304

284-
check(hasAtSign(tester, findStreamHeaderRow(tester, stream.streamId)))
305+
check(hasAtSign(tester, parent: findStreamHeaderRow(tester, stream.streamId)))
285306
.isFalse();
286-
check(hasAtSign(tester, findRowByLabel(tester, topic))).isFalse();
307+
check(hasAtSign(tester, parent: findRowByLabel(tester, topic))).isFalse();
287308
});
288309

289310
testWidgets('dm with a mention', (tester) async {
@@ -292,8 +313,8 @@ void main() {
292313
unreadMessages: [eg.dmMessage(from: eg.otherUser, to: [eg.selfUser],
293314
flags: [MessageFlag.mentioned])]);
294315

295-
check(hasAtSign(tester, findAllDmsHeaderRow(tester))).isTrue();
296-
check(hasAtSign(tester, findRowByLabel(tester, eg.otherUser.fullName))).isTrue();
316+
check(hasAtSign(tester, parent: findAllDmsHeaderRow(tester))).isTrue();
317+
check(hasAtSign(tester, parent: findRowByLabel(tester, eg.otherUser.fullName))).isTrue();
297318
});
298319

299320
testWidgets('dm without mention', (tester) async {
@@ -302,8 +323,8 @@ void main() {
302323
unreadMessages: [eg.dmMessage(from: eg.otherUser, to: [eg.selfUser],
303324
flags: [])]);
304325

305-
check(hasAtSign(tester, findAllDmsHeaderRow(tester))).isFalse();
306-
check(hasAtSign(tester, findRowByLabel(tester, eg.otherUser.fullName))).isFalse();
326+
check(hasAtSign(tester, parent: findAllDmsHeaderRow(tester))).isFalse();
327+
check(hasAtSign(tester, parent: findRowByLabel(tester, eg.otherUser.fullName))).isFalse();
307328
});
308329
});
309330

@@ -595,6 +616,36 @@ void main() {
595616
check(rectAfterTap).equals(rectBeforeTap);
596617
});
597618

619+
testWidgets('shows archived label for archived streams', (tester) async {
620+
final zulipLocalizations = GlobalLocalizations.zulipLocalizations;
621+
final stream = eg.stream(streamId: 1, isArchived: true);
622+
final subscription = eg.subscription(stream);
623+
624+
await setupPage(tester,
625+
streams: [stream],
626+
subscriptions: [subscription],
627+
unreadMessages: [eg.streamMessage(stream: stream)]);
628+
await tester.pumpAndSettle();
629+
630+
final headerRowFinder = findStreamHeaderRow(tester, stream.streamId);
631+
check(headerRowFinder).isNotNull();
632+
633+
final richTextFinder = find.descendant(
634+
of: find.byWidget(headerRowFinder!),
635+
matching: find.byWidgetPredicate((widget) =>
636+
widget is RichText &&
637+
widget.text.toPlainText().contains(stream.name) &&
638+
widget.text.toPlainText().contains(zulipLocalizations.channelArchivedLabel)
639+
),
640+
);
641+
expect(richTextFinder, findsOneWidget);
642+
643+
final richText = tester.widget<RichText>(richTextFinder);
644+
final textSpan = richText.text as TextSpan;
645+
final archivedSpan = textSpan.children![1] as TextSpan;
646+
expect(archivedSpan.style?.fontStyle, FontStyle.italic);
647+
});
648+
598649
// TODO check it remains collapsed even if you scroll far away and back
599650

600651
// TODO check that it's always uncollapsed when it appears after being

Diff for: test/widgets/message_list_test.dart

+24-1
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,29 @@ void main() {
975975
await tester.pump();
976976
check(pushedRoutes).isEmpty();
977977
});
978+
979+
testWidgets('shows archived label for archived streams', (tester) async {
980+
final zulipLocalizations = GlobalLocalizations.zulipLocalizations;
981+
final stream = eg.stream(streamId: 1, name: 'stream name', isArchived: true);
982+
final message = eg.streamMessage(stream: stream, topic: 'topic');
983+
984+
await setupMessageListPage(tester,
985+
narrow: const CombinedFeedNarrow(),
986+
messages: [message],
987+
streams: [stream],
988+
subscriptions: [eg.subscription(stream)]);
989+
await tester.pump();
990+
991+
check(findInMessageList('stream name')).length.equals(1);
992+
check(findInMessageList(zulipLocalizations.channelArchivedLabel)).length.equals(1);
993+
994+
final archivedLabelFinder = find.descendant(
995+
of: find.byType(MessageList),
996+
matching: find.text(zulipLocalizations.channelArchivedLabel),
997+
);
998+
final textWidget = tester.widget<Text>(archivedLabelFinder);
999+
expect(textWidget.style?.fontStyle, FontStyle.italic);
1000+
});
9781001
});
9791002

9801003
group('DmRecipientHeader', () {
@@ -1062,7 +1085,7 @@ void main() {
10621085
.initNarrow.equals(DmNarrow.withUser(eg.otherUser.userId, selfUserId: eg.selfUser.userId));
10631086
await tester.pumpAndSettle();
10641087
});
1065-
1088+
10661089
testWidgets('does not navigate on tapping recipient header in DmNarrow', (tester) async {
10671090
final pushedRoutes = <Route<void>>[];
10681091
final navObserver = TestNavigatorObserver()

0 commit comments

Comments
 (0)