Skip to content

Commit

Permalink
inbox: Fix collapsing a section from its sticky header
Browse files Browse the repository at this point in the history
Before this fix, when scrolled down through the inbox page, collapsing a
section (either the all-DMs section or a stream section) through its
sticky header would work but cause an abnormal behavior, pushing the
current section header and some of the following sections off the screen
by the amount of space the current section occupied.

After this fix, collapsing a section through a sticky header would work
as expected, without pushing the header and the following sections off
the screen.

Fixes: #391
  • Loading branch information
sm-sayedi committed Mar 22, 2024
1 parent 9044a9a commit ca7d5bc
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 3 deletions.
27 changes: 24 additions & 3 deletions lib/widgets/inbox.dart
Original file line number Diff line number Diff line change
Expand Up @@ -209,11 +209,19 @@ abstract class _HeaderItem extends StatelessWidget {
final int count;
final bool hasMention;

/// Context to access the [_StreamSection] or [_AllDmsSection].
///
/// Used to ensure the [_StreamSection] or [_AllDmsSection] that encloses the
/// current [_HeaderItem] is visible after being collapsed through this
/// [_HeaderItem].
final BuildContext sectionContext;

const _HeaderItem({
required this.collapsed,
required this.pageState,
required this.count,
required this.hasMention,
required this.sectionContext,
});

String get title;
Expand All @@ -223,7 +231,14 @@ abstract class _HeaderItem extends StatelessWidget {
Color get uncollapsedBackgroundColor;
Color? get unreadCountBadgeBackgroundColor;

void Function() get onCollapseButtonTap;
Future<void> Function() get onCollapseButtonTap => () async {
if (!collapsed) {
await Scrollable.ensureVisible(
sectionContext,
alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtStart,
);
}
};
void Function() get onRowTap;

@override
Expand Down Expand Up @@ -271,6 +286,7 @@ class _AllDmsHeaderItem extends _HeaderItem {
required super.pageState,
required super.count,
required super.hasMention,
required super.sectionContext,
});

@override get title => 'Direct messages'; // TODO(i18n)
Expand All @@ -280,7 +296,8 @@ class _AllDmsHeaderItem extends _HeaderItem {
@override get uncollapsedBackgroundColor => const Color(0xFFF3F0E7);
@override get unreadCountBadgeBackgroundColor => null;

@override get onCollapseButtonTap => () {
@override get onCollapseButtonTap => () async {
await super.onCollapseButtonTap();
pageState.allDmsCollapsed = !collapsed;
};
@override get onRowTap => onCollapseButtonTap; // TODO open all-DMs narrow?
Expand All @@ -304,6 +321,7 @@ class _AllDmsSection extends StatelessWidget {
hasMention: data.hasMention,
collapsed: collapsed,
pageState: pageState,
sectionContext: context,
);
return StickyHeaderItem(
header: header,
Expand Down Expand Up @@ -386,6 +404,7 @@ class _StreamHeaderItem extends _HeaderItem {
required super.pageState,
required super.count,
required super.hasMention,
required super.sectionContext,
});

@override get title => subscription.name;
Expand All @@ -397,7 +416,8 @@ class _StreamHeaderItem extends _HeaderItem {
@override get unreadCountBadgeBackgroundColor =>
subscription.colorSwatch().unreadCountBadgeBackground;

@override get onCollapseButtonTap => () {
@override get onCollapseButtonTap => () async {
await super.onCollapseButtonTap();
if (collapsed) {
pageState.uncollapseStream(subscription.streamId);
} else {
Expand Down Expand Up @@ -427,6 +447,7 @@ class _StreamSection extends StatelessWidget {
hasMention: data.hasMention,
collapsed: collapsed,
pageState: pageState,
sectionContext: context,
);
return StickyHeaderItem(
header: header,
Expand Down
55 changes: 55 additions & 0 deletions test/widgets/inbox_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ void main() {
await tester.pumpAndSettle();
}

List<StreamMessage> generateStreamMessages(ZulipStream stream, int count) {
return List.generate(count, (index) => eg.streamMessage(
stream: stream, topic: '${stream.name} topic $index', flags: []));
}

/// Set up an inbox view with lots of interesting content.
Future<void> setupVarious(WidgetTester tester) async {
final stream1 = eg.stream(streamId: 1, name: 'stream 1');
Expand All @@ -64,7 +69,9 @@ void main() {
users: [eg.selfUser, eg.otherUser, eg.thirdUser],
unreadMessages: [
eg.streamMessage(stream: stream1, topic: 'specific topic', flags: []),
...generateStreamMessages(stream1, 10),
eg.streamMessage(stream: stream2, flags: []),
...generateStreamMessages(stream2, 40),
eg.dmMessage(from: eg.otherUser, to: [eg.selfUser], flags: []),
eg.dmMessage(from: eg.otherUser, to: [eg.selfUser, eg.thirdUser], flags: []),
]);
Expand Down Expand Up @@ -310,6 +317,30 @@ void main() {
checkAppearsUncollapsed(tester, findSectionContent);
});

testWidgets('collapse all-DMs section after scroll', (tester) async {
await setupVarious(tester);

final listFinder = find.byType(Scrollable);
// Scroll the [StickyHeaderListView] enough so that
// the [_AllDmsSection] shows a sticky header
await tester.drag(listFinder, const Offset(0, -50));

final headerRow = findAllDmsHeaderRow(tester);
// Check that the sticky header is present
check(headerRow).isNotNull();

final findSectionContent = find.text(eg.otherUser.fullName);

checkAppearsUncollapsed(tester, findSectionContent);
await tapCollapseIcon(tester);
// Check that the header is still visible even after
// collapsing the section
check(headerRow).isNotNull();
checkAppearsCollapsed(tester, findSectionContent);
await tapCollapseIcon(tester);
checkAppearsUncollapsed(tester, findSectionContent);
});

// TODO check it remains collapsed even if you scroll far away and back

// TODO check that it's always uncollapsed when it appears after being
Expand Down Expand Up @@ -385,6 +416,30 @@ void main() {
checkAppearsUncollapsed(tester, 1, findSectionContent);
});

testWidgets('collapse stream section after scroll', (tester) async {
await setupVarious(tester);

final listFinder = find.byType(Scrollable);
// Scroll the [StickyHeaderListView] enough so that
// the [_StreamSection] shows a sticky header
await tester.drag(listFinder, const Offset(0, -200));

final headerRow = findStreamHeaderRow(tester, 1);
// Check that the sticky header is present
check(headerRow).isNotNull();

final findSectionContent = find.text('specific topic');

checkAppearsUncollapsed(tester, 1, findSectionContent);
await tapCollapseIcon(tester, 1);
// Check that the header is still visible even after
// collapsing the section
check(headerRow).isNotNull();
checkAppearsCollapsed(tester, 1, findSectionContent);
await tapCollapseIcon(tester, 1);
checkAppearsUncollapsed(tester, 1, findSectionContent);
});

// TODO check it remains collapsed even if you scroll far away and back

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

0 comments on commit ca7d5bc

Please sign in to comment.